forked from bartvdbraak/blender
netrender tab to spaces
This commit is contained in:
parent
42154ad226
commit
58b1591ceb
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -22,129 +22,129 @@ from netrender.utils import *
|
||||
import netrender.model
|
||||
|
||||
class RatingRule:
|
||||
def rate(self, job):
|
||||
return 0
|
||||
def rate(self, job):
|
||||
return 0
|
||||
|
||||
class ExclusionRule:
|
||||
def test(self, job):
|
||||
return False
|
||||
def test(self, job):
|
||||
return False
|
||||
|
||||
class PriorityRule:
|
||||
def test(self, job):
|
||||
return False
|
||||
def test(self, job):
|
||||
return False
|
||||
|
||||
class Balancer:
|
||||
def __init__(self):
|
||||
self.rules = []
|
||||
self.priorities = []
|
||||
self.exceptions = []
|
||||
|
||||
def addRule(self, rule):
|
||||
self.rules.append(rule)
|
||||
|
||||
def addPriority(self, priority):
|
||||
self.priorities.append(priority)
|
||||
|
||||
def addException(self, exception):
|
||||
self.exceptions.append(exception)
|
||||
|
||||
def applyRules(self, job):
|
||||
return sum((rule.rate(job) for rule in self.rules))
|
||||
|
||||
def applyPriorities(self, job):
|
||||
for priority in self.priorities:
|
||||
if priority.test(job):
|
||||
return True # priorities are first
|
||||
|
||||
return False
|
||||
|
||||
def applyExceptions(self, job):
|
||||
for exception in self.exceptions:
|
||||
if exception.test(job):
|
||||
return True # exceptions are last
|
||||
|
||||
return False
|
||||
|
||||
def sortKey(self, job):
|
||||
return (1 if self.applyExceptions(job) else 0, # exceptions after
|
||||
0 if self.applyPriorities(job) else 1, # priorities first
|
||||
self.applyRules(job))
|
||||
|
||||
def balance(self, jobs):
|
||||
if jobs:
|
||||
# use inline copy to make sure the list is still accessible while sorting
|
||||
jobs[:] = sorted(jobs, key=self.sortKey)
|
||||
return jobs[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def __init__(self):
|
||||
self.rules = []
|
||||
self.priorities = []
|
||||
self.exceptions = []
|
||||
|
||||
def addRule(self, rule):
|
||||
self.rules.append(rule)
|
||||
|
||||
def addPriority(self, priority):
|
||||
self.priorities.append(priority)
|
||||
|
||||
def addException(self, exception):
|
||||
self.exceptions.append(exception)
|
||||
|
||||
def applyRules(self, job):
|
||||
return sum((rule.rate(job) for rule in self.rules))
|
||||
|
||||
def applyPriorities(self, job):
|
||||
for priority in self.priorities:
|
||||
if priority.test(job):
|
||||
return True # priorities are first
|
||||
|
||||
return False
|
||||
|
||||
def applyExceptions(self, job):
|
||||
for exception in self.exceptions:
|
||||
if exception.test(job):
|
||||
return True # exceptions are last
|
||||
|
||||
return False
|
||||
|
||||
def sortKey(self, job):
|
||||
return (1 if self.applyExceptions(job) else 0, # exceptions after
|
||||
0 if self.applyPriorities(job) else 1, # priorities first
|
||||
self.applyRules(job))
|
||||
|
||||
def balance(self, jobs):
|
||||
if jobs:
|
||||
# use inline copy to make sure the list is still accessible while sorting
|
||||
jobs[:] = sorted(jobs, key=self.sortKey)
|
||||
return jobs[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
# ==========================
|
||||
|
||||
class RatingUsage(RatingRule):
|
||||
def __str__(self):
|
||||
return "Usage rating"
|
||||
|
||||
def rate(self, job):
|
||||
# less usage is better
|
||||
return job.usage / job.priority
|
||||
def __str__(self):
|
||||
return "Usage rating"
|
||||
|
||||
def rate(self, job):
|
||||
# less usage is better
|
||||
return job.usage / job.priority
|
||||
|
||||
class RatingUsageByCategory(RatingRule):
|
||||
def __str__(self):
|
||||
return "Usage per category rating"
|
||||
|
||||
def __init__(self, get_jobs):
|
||||
self.getJobs = get_jobs
|
||||
def rate(self, job):
|
||||
total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
|
||||
maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
|
||||
|
||||
# less usage is better
|
||||
return total_category_usage / maximum_priority
|
||||
|
||||
class NewJobPriority(PriorityRule):
|
||||
def str_limit(self):
|
||||
return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
|
||||
def __str__(self):
|
||||
return "Usage per category rating"
|
||||
|
||||
def __str__(self):
|
||||
return "Priority to new jobs"
|
||||
|
||||
def __init__(self, limit = 1):
|
||||
self.limit = limit
|
||||
|
||||
def test(self, job):
|
||||
return job.countFrames(status = DONE) < self.limit
|
||||
def __init__(self, get_jobs):
|
||||
self.getJobs = get_jobs
|
||||
def rate(self, job):
|
||||
total_category_usage = sum([j.usage for j in self.getJobs() if j.category == job.category])
|
||||
maximum_priority = max([j.priority for j in self.getJobs() if j.category == job.category])
|
||||
|
||||
# less usage is better
|
||||
return total_category_usage / maximum_priority
|
||||
|
||||
class NewJobPriority(PriorityRule):
|
||||
def str_limit(self):
|
||||
return "less than %i frame%s done" % (self.limit, "s" if self.limit > 1 else "")
|
||||
|
||||
def __str__(self):
|
||||
return "Priority to new jobs"
|
||||
|
||||
def __init__(self, limit = 1):
|
||||
self.limit = limit
|
||||
|
||||
def test(self, job):
|
||||
return job.countFrames(status = DONE) < self.limit
|
||||
|
||||
class MinimumTimeBetweenDispatchPriority(PriorityRule):
|
||||
def str_limit(self):
|
||||
return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
|
||||
def str_limit(self):
|
||||
return "more than %i minute%s since last" % (self.limit, "s" if self.limit > 1 else "")
|
||||
|
||||
def __str__(self):
|
||||
return "Priority to jobs that haven't been dispatched recently"
|
||||
|
||||
def __init__(self, limit = 10):
|
||||
self.limit = limit
|
||||
|
||||
def test(self, job):
|
||||
return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
|
||||
def __str__(self):
|
||||
return "Priority to jobs that haven't been dispatched recently"
|
||||
|
||||
def __init__(self, limit = 10):
|
||||
self.limit = limit
|
||||
|
||||
def test(self, job):
|
||||
return job.countFrames(status = DISPATCHED) == 0 and (time.time() - job.last_dispatched) / 60 > self.limit
|
||||
|
||||
class ExcludeQueuedEmptyJob(ExclusionRule):
|
||||
def __str__(self):
|
||||
return "Exclude queued and empty jobs"
|
||||
|
||||
def test(self, job):
|
||||
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
|
||||
|
||||
class ExcludeSlavesLimit(ExclusionRule):
|
||||
def str_limit(self):
|
||||
return "more than %.0f%% of all slaves" % (self.limit * 100)
|
||||
def __str__(self):
|
||||
return "Exclude queued and empty jobs"
|
||||
|
||||
def __str__(self):
|
||||
return "Exclude jobs that would use too many slaves"
|
||||
|
||||
def __init__(self, count_jobs, count_slaves, limit = 0.75):
|
||||
self.count_jobs = count_jobs
|
||||
self.count_slaves = count_slaves
|
||||
self.limit = limit
|
||||
|
||||
def test(self, job):
|
||||
return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
|
||||
def test(self, job):
|
||||
return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0
|
||||
|
||||
class ExcludeSlavesLimit(ExclusionRule):
|
||||
def str_limit(self):
|
||||
return "more than %.0f%% of all slaves" % (self.limit * 100)
|
||||
|
||||
def __str__(self):
|
||||
return "Exclude jobs that would use too many slaves"
|
||||
|
||||
def __init__(self, count_jobs, count_slaves, limit = 0.75):
|
||||
self.count_jobs = count_jobs
|
||||
self.count_slaves = count_slaves
|
||||
self.limit = limit
|
||||
|
||||
def test(self, job):
|
||||
return not ( self.count_jobs() == 1 or self.count_slaves() <= 1 or float(job.countSlaves() + 1) / self.count_slaves() <= self.limit )
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -28,253 +28,253 @@ import netrender.master as master
|
||||
from netrender.utils import *
|
||||
|
||||
def addFluidFiles(job, path):
|
||||
if os.path.exists(path):
|
||||
pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
|
||||
if os.path.exists(path):
|
||||
pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz")
|
||||
|
||||
for fluid_file in sorted(os.listdir(path)):
|
||||
match = pattern.match(fluid_file)
|
||||
|
||||
if match:
|
||||
# fluid frames starts at 0, which explains the +1
|
||||
# This is stupid
|
||||
current_frame = int(match.groups()[1]) + 1
|
||||
job.addFile(path + fluid_file, current_frame, current_frame)
|
||||
for fluid_file in sorted(os.listdir(path)):
|
||||
match = pattern.match(fluid_file)
|
||||
|
||||
if match:
|
||||
# fluid frames starts at 0, which explains the +1
|
||||
# This is stupid
|
||||
current_frame = int(match.groups()[1]) + 1
|
||||
job.addFile(path + fluid_file, current_frame, current_frame)
|
||||
|
||||
def addPointCache(job, ob, point_cache, default_path):
|
||||
if not point_cache.disk_cache:
|
||||
return
|
||||
|
||||
|
||||
name = point_cache.name
|
||||
if name == "":
|
||||
name = "".join(["%02X" % ord(c) for c in ob.name])
|
||||
|
||||
cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
|
||||
|
||||
index = "%02i" % point_cache.index
|
||||
|
||||
if os.path.exists(cache_path):
|
||||
pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
|
||||
|
||||
cache_files = []
|
||||
if not point_cache.disk_cache:
|
||||
return
|
||||
|
||||
|
||||
name = point_cache.name
|
||||
if name == "":
|
||||
name = "".join(["%02X" % ord(c) for c in ob.name])
|
||||
|
||||
cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path
|
||||
|
||||
index = "%02i" % point_cache.index
|
||||
|
||||
if os.path.exists(cache_path):
|
||||
pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys")
|
||||
|
||||
cache_files = []
|
||||
|
||||
for cache_file in sorted(os.listdir(cache_path)):
|
||||
match = pattern.match(cache_file)
|
||||
|
||||
if match:
|
||||
cache_frame = int(match.groups()[0])
|
||||
cache_files.append((cache_frame, cache_file))
|
||||
|
||||
cache_files.sort()
|
||||
|
||||
if len(cache_files) == 1:
|
||||
cache_frame, cache_file = cache_files[0]
|
||||
job.addFile(cache_path + cache_file, cache_frame, cache_frame)
|
||||
else:
|
||||
for i in range(len(cache_files)):
|
||||
current_item = cache_files[i]
|
||||
next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
|
||||
previous_item = cache_files[i - 1] if i > 0 else None
|
||||
|
||||
current_frame, current_file = current_item
|
||||
|
||||
if not next_item and not previous_item:
|
||||
job.addFile(cache_path + current_file, current_frame, current_frame)
|
||||
elif next_item and not previous_item:
|
||||
next_frame = next_item[0]
|
||||
job.addFile(cache_path + current_file, current_frame, next_frame - 1)
|
||||
elif not next_item and previous_item:
|
||||
previous_frame = previous_item[0]
|
||||
job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
|
||||
else:
|
||||
next_frame = next_item[0]
|
||||
previous_frame = previous_item[0]
|
||||
job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
|
||||
|
||||
for cache_file in sorted(os.listdir(cache_path)):
|
||||
match = pattern.match(cache_file)
|
||||
|
||||
if match:
|
||||
cache_frame = int(match.groups()[0])
|
||||
cache_files.append((cache_frame, cache_file))
|
||||
|
||||
cache_files.sort()
|
||||
|
||||
if len(cache_files) == 1:
|
||||
cache_frame, cache_file = cache_files[0]
|
||||
job.addFile(cache_path + cache_file, cache_frame, cache_frame)
|
||||
else:
|
||||
for i in range(len(cache_files)):
|
||||
current_item = cache_files[i]
|
||||
next_item = cache_files[i+1] if i + 1 < len(cache_files) else None
|
||||
previous_item = cache_files[i - 1] if i > 0 else None
|
||||
|
||||
current_frame, current_file = current_item
|
||||
|
||||
if not next_item and not previous_item:
|
||||
job.addFile(cache_path + current_file, current_frame, current_frame)
|
||||
elif next_item and not previous_item:
|
||||
next_frame = next_item[0]
|
||||
job.addFile(cache_path + current_file, current_frame, next_frame - 1)
|
||||
elif not next_item and previous_item:
|
||||
previous_frame = previous_item[0]
|
||||
job.addFile(cache_path + current_file, previous_frame + 1, current_frame)
|
||||
else:
|
||||
next_frame = next_item[0]
|
||||
previous_frame = previous_item[0]
|
||||
job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1)
|
||||
|
||||
def clientSendJob(conn, scene, anim = False):
|
||||
netsettings = scene.network_render
|
||||
job = netrender.model.RenderJob()
|
||||
|
||||
if anim:
|
||||
for f in range(scene.start_frame, scene.end_frame + 1):
|
||||
job.addFrame(f)
|
||||
else:
|
||||
job.addFrame(scene.current_frame)
|
||||
|
||||
filename = bpy.data.filename
|
||||
job.addFile(filename)
|
||||
|
||||
job_name = netsettings.job_name
|
||||
path, name = os.path.split(filename)
|
||||
if job_name == "[default]":
|
||||
job_name = name
|
||||
|
||||
###########################
|
||||
# LIBRARIES
|
||||
###########################
|
||||
for lib in bpy.data.libraries:
|
||||
job.addFile(bpy.utils.expandpath(lib.filename))
|
||||
|
||||
###########################
|
||||
# IMAGES
|
||||
###########################
|
||||
for image in bpy.data.images:
|
||||
if image.source == "FILE" and not image.packed_file:
|
||||
job.addFile(bpy.utils.expandpath(image.filename))
|
||||
|
||||
###########################
|
||||
# FLUID + POINT CACHE
|
||||
###########################
|
||||
root, ext = os.path.splitext(name)
|
||||
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
||||
netsettings = scene.network_render
|
||||
job = netrender.model.RenderJob()
|
||||
|
||||
for object in bpy.data.objects:
|
||||
for modifier in object.modifiers:
|
||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||
addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
|
||||
elif modifier.type == "CLOTH":
|
||||
addPointCache(job, object, modifier.point_cache, default_path)
|
||||
elif modifier.type == "SOFT_BODY":
|
||||
addPointCache(job, object, modifier.point_cache, default_path)
|
||||
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
||||
addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path)
|
||||
if modifier.domain_settings.highres:
|
||||
addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
|
||||
if anim:
|
||||
for f in range(scene.start_frame, scene.end_frame + 1):
|
||||
job.addFrame(f)
|
||||
else:
|
||||
job.addFrame(scene.current_frame)
|
||||
|
||||
# particles modifier are stupid and don't contain data
|
||||
# we have to go through the object property
|
||||
for psys in object.particle_systems:
|
||||
addPointCache(job, object, psys.point_cache, default_path)
|
||||
|
||||
#print(job.files)
|
||||
|
||||
job.name = job_name
|
||||
job.category = netsettings.job_category
|
||||
|
||||
for slave in netrender.blacklist:
|
||||
job.blacklist.append(slave.id)
|
||||
|
||||
job.chunks = netsettings.chunks
|
||||
job.priority = netsettings.priority
|
||||
|
||||
# try to send path first
|
||||
conn.request("POST", "/job", repr(job.serialize()))
|
||||
response = conn.getresponse()
|
||||
|
||||
job_id = response.getheader("job-id")
|
||||
|
||||
# if not ACCEPTED (but not processed), send files
|
||||
if response.status == http.client.ACCEPTED:
|
||||
for rfile in job.files:
|
||||
f = open(rfile.filepath, "rb")
|
||||
conn.request("PUT", fileURL(job_id, rfile.index), f)
|
||||
f.close()
|
||||
response = conn.getresponse()
|
||||
|
||||
# server will reply with ACCEPTED until all files are found
|
||||
|
||||
return job_id
|
||||
filename = bpy.data.filename
|
||||
job.addFile(filename)
|
||||
|
||||
job_name = netsettings.job_name
|
||||
path, name = os.path.split(filename)
|
||||
if job_name == "[default]":
|
||||
job_name = name
|
||||
|
||||
###########################
|
||||
# LIBRARIES
|
||||
###########################
|
||||
for lib in bpy.data.libraries:
|
||||
job.addFile(bpy.utils.expandpath(lib.filename))
|
||||
|
||||
###########################
|
||||
# IMAGES
|
||||
###########################
|
||||
for image in bpy.data.images:
|
||||
if image.source == "FILE" and not image.packed_file:
|
||||
job.addFile(bpy.utils.expandpath(image.filename))
|
||||
|
||||
###########################
|
||||
# FLUID + POINT CACHE
|
||||
###########################
|
||||
root, ext = os.path.splitext(name)
|
||||
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
||||
|
||||
for object in bpy.data.objects:
|
||||
for modifier in object.modifiers:
|
||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||
addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path))
|
||||
elif modifier.type == "CLOTH":
|
||||
addPointCache(job, object, modifier.point_cache, default_path)
|
||||
elif modifier.type == "SOFT_BODY":
|
||||
addPointCache(job, object, modifier.point_cache, default_path)
|
||||
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
||||
addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path)
|
||||
if modifier.domain_settings.highres:
|
||||
addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path)
|
||||
|
||||
# particles modifier are stupid and don't contain data
|
||||
# we have to go through the object property
|
||||
for psys in object.particle_systems:
|
||||
addPointCache(job, object, psys.point_cache, default_path)
|
||||
|
||||
#print(job.files)
|
||||
|
||||
job.name = job_name
|
||||
job.category = netsettings.job_category
|
||||
|
||||
for slave in netrender.blacklist:
|
||||
job.blacklist.append(slave.id)
|
||||
|
||||
job.chunks = netsettings.chunks
|
||||
job.priority = netsettings.priority
|
||||
|
||||
# try to send path first
|
||||
conn.request("POST", "/job", repr(job.serialize()))
|
||||
response = conn.getresponse()
|
||||
|
||||
job_id = response.getheader("job-id")
|
||||
|
||||
# if not ACCEPTED (but not processed), send files
|
||||
if response.status == http.client.ACCEPTED:
|
||||
for rfile in job.files:
|
||||
f = open(rfile.filepath, "rb")
|
||||
conn.request("PUT", fileURL(job_id, rfile.index), f)
|
||||
f.close()
|
||||
response = conn.getresponse()
|
||||
|
||||
# server will reply with ACCEPTED until all files are found
|
||||
|
||||
return job_id
|
||||
|
||||
def requestResult(conn, job_id, frame):
|
||||
conn.request("GET", renderURL(job_id, frame))
|
||||
conn.request("GET", renderURL(job_id, frame))
|
||||
|
||||
@rnaType
|
||||
class NetworkRenderEngine(bpy.types.RenderEngine):
|
||||
bl_idname = 'NET_RENDER'
|
||||
bl_label = "Network Render"
|
||||
def render(self, scene):
|
||||
if scene.network_render.mode == "RENDER_CLIENT":
|
||||
self.render_client(scene)
|
||||
elif scene.network_render.mode == "RENDER_SLAVE":
|
||||
self.render_slave(scene)
|
||||
elif scene.network_render.mode == "RENDER_MASTER":
|
||||
self.render_master(scene)
|
||||
else:
|
||||
print("UNKNOWN OPERATION MODE")
|
||||
|
||||
def render_master(self, scene):
|
||||
netsettings = scene.network_render
|
||||
|
||||
address = "" if netsettings.server_address == "[default]" else netsettings.server_address
|
||||
|
||||
master.runMaster((address, netsettings.server_port), netsettings.server_broadcast, netsettings.path, self.update_stats, self.test_break)
|
||||
bl_idname = 'NET_RENDER'
|
||||
bl_label = "Network Render"
|
||||
def render(self, scene):
|
||||
if scene.network_render.mode == "RENDER_CLIENT":
|
||||
self.render_client(scene)
|
||||
elif scene.network_render.mode == "RENDER_SLAVE":
|
||||
self.render_slave(scene)
|
||||
elif scene.network_render.mode == "RENDER_MASTER":
|
||||
self.render_master(scene)
|
||||
else:
|
||||
print("UNKNOWN OPERATION MODE")
|
||||
|
||||
def render_master(self, scene):
|
||||
netsettings = scene.network_render
|
||||
|
||||
address = "" if netsettings.server_address == "[default]" else netsettings.server_address
|
||||
|
||||
master.runMaster((address, netsettings.server_port), netsettings.server_broadcast, netsettings.path, self.update_stats, self.test_break)
|
||||
|
||||
|
||||
def render_slave(self, scene):
|
||||
slave.render_slave(self, scene.network_render)
|
||||
|
||||
def render_client(self, scene):
|
||||
netsettings = scene.network_render
|
||||
self.update_stats("", "Network render client initiation")
|
||||
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
||||
|
||||
if conn:
|
||||
# Sending file
|
||||
|
||||
self.update_stats("", "Network render exporting")
|
||||
|
||||
new_job = False
|
||||
|
||||
job_id = netsettings.job_id
|
||||
|
||||
# reading back result
|
||||
|
||||
self.update_stats("", "Network render waiting for results")
|
||||
|
||||
requestResult(conn, job_id, scene.current_frame)
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status == http.client.NO_CONTENT:
|
||||
new_job = True
|
||||
netsettings.job_id = clientSendJob(conn, scene)
|
||||
job_id = netsettings.job_id
|
||||
|
||||
requestResult(conn, job_id, scene.current_frame)
|
||||
response = conn.getresponse()
|
||||
|
||||
while response.status == http.client.ACCEPTED and not self.test_break():
|
||||
time.sleep(1)
|
||||
requestResult(conn, job_id, scene.current_frame)
|
||||
response = conn.getresponse()
|
||||
|
||||
# cancel new jobs (animate on network) on break
|
||||
if self.test_break() and new_job:
|
||||
conn.request("POST", cancelURL(job_id))
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
netsettings.job_id = 0
|
||||
|
||||
if response.status != http.client.OK:
|
||||
conn.close()
|
||||
return
|
||||
|
||||
r = scene.render_data
|
||||
x= int(r.resolution_x*r.resolution_percentage*0.01)
|
||||
y= int(r.resolution_y*r.resolution_percentage*0.01)
|
||||
|
||||
f = open(netsettings.path + "output.exr", "wb")
|
||||
buf = response.read(1024)
|
||||
|
||||
while buf:
|
||||
f.write(buf)
|
||||
buf = response.read(1024)
|
||||
|
||||
f.close()
|
||||
|
||||
result = self.begin_result(0, 0, x, y)
|
||||
result.load_from_file(netsettings.path + "output.exr", 0, 0)
|
||||
self.end_result(result)
|
||||
|
||||
conn.close()
|
||||
def render_slave(self, scene):
|
||||
slave.render_slave(self, scene.network_render)
|
||||
|
||||
def render_client(self, scene):
|
||||
netsettings = scene.network_render
|
||||
self.update_stats("", "Network render client initiation")
|
||||
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
||||
|
||||
if conn:
|
||||
# Sending file
|
||||
|
||||
self.update_stats("", "Network render exporting")
|
||||
|
||||
new_job = False
|
||||
|
||||
job_id = netsettings.job_id
|
||||
|
||||
# reading back result
|
||||
|
||||
self.update_stats("", "Network render waiting for results")
|
||||
|
||||
requestResult(conn, job_id, scene.current_frame)
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status == http.client.NO_CONTENT:
|
||||
new_job = True
|
||||
netsettings.job_id = clientSendJob(conn, scene)
|
||||
job_id = netsettings.job_id
|
||||
|
||||
requestResult(conn, job_id, scene.current_frame)
|
||||
response = conn.getresponse()
|
||||
|
||||
while response.status == http.client.ACCEPTED and not self.test_break():
|
||||
time.sleep(1)
|
||||
requestResult(conn, job_id, scene.current_frame)
|
||||
response = conn.getresponse()
|
||||
|
||||
# cancel new jobs (animate on network) on break
|
||||
if self.test_break() and new_job:
|
||||
conn.request("POST", cancelURL(job_id))
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
netsettings.job_id = 0
|
||||
|
||||
if response.status != http.client.OK:
|
||||
conn.close()
|
||||
return
|
||||
|
||||
r = scene.render_data
|
||||
x= int(r.resolution_x*r.resolution_percentage*0.01)
|
||||
y= int(r.resolution_y*r.resolution_percentage*0.01)
|
||||
|
||||
f = open(netsettings.path + "output.exr", "wb")
|
||||
buf = response.read(1024)
|
||||
|
||||
while buf:
|
||||
f.write(buf)
|
||||
buf = response.read(1024)
|
||||
|
||||
f.close()
|
||||
|
||||
result = self.begin_result(0, 0, x, y)
|
||||
result.load_from_file(netsettings.path + "output.exr", 0, 0)
|
||||
self.end_result(result)
|
||||
|
||||
conn.close()
|
||||
|
||||
def compatible(module):
|
||||
module = __import__(module)
|
||||
for subclass in module.__dict__.values():
|
||||
try: subclass.COMPAT_ENGINES.add('NET_RENDER')
|
||||
except: pass
|
||||
del module
|
||||
module = __import__(module)
|
||||
for subclass in module.__dict__.values():
|
||||
try: subclass.COMPAT_ENGINES.add('NET_RENDER')
|
||||
except: pass
|
||||
del module
|
||||
|
||||
compatible("properties_render")
|
||||
compatible("properties_world")
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -24,226 +24,226 @@ from netrender.utils import *
|
||||
src_folder = os.path.split(__file__)[0]
|
||||
|
||||
def get(handler):
|
||||
def output(text):
|
||||
handler.wfile.write(bytes(text, encoding='utf8'))
|
||||
|
||||
def head(title):
|
||||
output("<html><head>")
|
||||
output("<script src='/html/netrender.js' type='text/javascript'></script>")
|
||||
def output(text):
|
||||
handler.wfile.write(bytes(text, encoding='utf8'))
|
||||
|
||||
def head(title):
|
||||
output("<html><head>")
|
||||
output("<script src='/html/netrender.js' type='text/javascript'></script>")
|
||||
# output("<script src='/html/json2.js' type='text/javascript'></script>")
|
||||
output("<title>")
|
||||
output(title)
|
||||
output("</title></head><body>")
|
||||
output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
|
||||
output("<title>")
|
||||
output(title)
|
||||
output("</title></head><body>")
|
||||
output("<link rel='stylesheet' href='/html/netrender.css' type='text/css'>")
|
||||
|
||||
|
||||
def link(text, url):
|
||||
return "<a href='%s'>%s</a>" % (url, text)
|
||||
|
||||
def startTable(border=1, class_style = None, caption = None):
|
||||
output("<table border='%i'" % border)
|
||||
|
||||
if class_style:
|
||||
output(" class='%s'" % class_style)
|
||||
|
||||
output(">")
|
||||
|
||||
if caption:
|
||||
output("<caption>%s</caption>" % caption)
|
||||
|
||||
def headerTable(*headers):
|
||||
output("<thead><tr>")
|
||||
|
||||
for c in headers:
|
||||
output("<td>" + c + "</td>")
|
||||
|
||||
output("</tr></thead>")
|
||||
|
||||
def rowTable(*data, id = None, class_style = None, extra = None):
|
||||
output("<tr")
|
||||
|
||||
if id:
|
||||
output(" id='%s'" % id)
|
||||
|
||||
if class_style:
|
||||
output(" class='%s'" % class_style)
|
||||
|
||||
if extra:
|
||||
output(" %s" % extra)
|
||||
def link(text, url):
|
||||
return "<a href='%s'>%s</a>" % (url, text)
|
||||
|
||||
output(">")
|
||||
|
||||
for c in data:
|
||||
output("<td>" + str(c) + "</td>")
|
||||
|
||||
output("</tr>")
|
||||
|
||||
def endTable():
|
||||
output("</table>")
|
||||
|
||||
if handler.path == "/html/netrender.js":
|
||||
f = open(os.path.join(src_folder, "netrender.js"), 'rb')
|
||||
|
||||
handler.send_head(content = "text/javascript")
|
||||
shutil.copyfileobj(f, handler.wfile)
|
||||
|
||||
f.close()
|
||||
elif handler.path == "/html/netrender.css":
|
||||
f = open(os.path.join(src_folder, "netrender.css"), 'rb')
|
||||
|
||||
handler.send_head(content = "text/css")
|
||||
shutil.copyfileobj(f, handler.wfile)
|
||||
|
||||
f.close()
|
||||
elif handler.path == "/html" or handler.path == "/":
|
||||
handler.send_head(content = "text/html")
|
||||
head("NetRender")
|
||||
|
||||
output("<h2>Master</h2>")
|
||||
|
||||
output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
|
||||
def startTable(border=1, class_style = None, caption = None):
|
||||
output("<table border='%i'" % border)
|
||||
|
||||
startTable(caption = "Rules", class_style = "rules")
|
||||
if class_style:
|
||||
output(" class='%s'" % class_style)
|
||||
|
||||
headerTable("type", "description", "limit")
|
||||
output(">")
|
||||
|
||||
for rule in handler.server.balancer.rules:
|
||||
rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||
if caption:
|
||||
output("<caption>%s</caption>" % caption)
|
||||
|
||||
for rule in handler.server.balancer.priorities:
|
||||
rowTable("priority", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||
|
||||
for rule in handler.server.balancer.exceptions:
|
||||
rowTable("exception", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||
def headerTable(*headers):
|
||||
output("<thead><tr>")
|
||||
|
||||
endTable()
|
||||
for c in headers:
|
||||
output("<td>" + c + "</td>")
|
||||
|
||||
output("<h2>Slaves</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable("name", "address", "last seen", "stats", "job")
|
||||
|
||||
for slave in handler.server.slaves:
|
||||
rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
|
||||
|
||||
endTable()
|
||||
|
||||
output("<h2>Jobs</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable(
|
||||
" ",
|
||||
"id",
|
||||
"name",
|
||||
"category",
|
||||
"chunks",
|
||||
"priority",
|
||||
"usage",
|
||||
"wait",
|
||||
"status",
|
||||
"length",
|
||||
"done",
|
||||
"dispatched",
|
||||
"error",
|
||||
"first",
|
||||
"exception"
|
||||
)
|
||||
output("</tr></thead>")
|
||||
|
||||
handler.server.balance()
|
||||
|
||||
for job in handler.server.jobs:
|
||||
results = job.framesStatus()
|
||||
rowTable(
|
||||
"""<button title="cancel job" onclick="request('/cancel_%s', null);">X</button>""" % job.id +
|
||||
"""<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
|
||||
job.id,
|
||||
link(job.name, "/html/job" + job.id),
|
||||
job.category if job.category else "<i>None</i>",
|
||||
str(job.chunks) +
|
||||
"""<button title="increase priority" onclick="request('/edit_%s', "{'chunks': %i}");">+</button>""" % (job.id, job.chunks + 1) +
|
||||
"""<button title="decrease priority" onclick="request('/edit_%s', "{'chunks': %i}");" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
|
||||
str(job.priority) +
|
||||
"""<button title="increase chunks size" onclick="request('/edit_%s', "{'priority': %i}");">+</button>""" % (job.id, job.priority + 1) +
|
||||
"""<button title="decrease chunks size" onclick="request('/edit_%s', "{'priority': %i}");" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
|
||||
"%0.1f%%" % (job.usage * 100),
|
||||
"%is" % int(time.time() - job.last_dispatched),
|
||||
job.statusText(),
|
||||
len(job),
|
||||
results[DONE],
|
||||
results[DISPATCHED],
|
||||
str(results[ERROR]) +
|
||||
"""<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
|
||||
handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job)
|
||||
)
|
||||
|
||||
endTable()
|
||||
|
||||
output("</body></html>")
|
||||
|
||||
elif handler.path.startswith("/html/job"):
|
||||
handler.send_head(content = "text/html")
|
||||
job_id = handler.path[9:]
|
||||
|
||||
head("NetRender")
|
||||
|
||||
job = handler.server.getJobID(job_id)
|
||||
|
||||
if job:
|
||||
output("<h2>Files</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable("path")
|
||||
|
||||
tot_cache = 0
|
||||
tot_fluid = 0
|
||||
|
||||
for file in job.files:
|
||||
if file.filepath.endswith(".bphys"):
|
||||
tot_cache += 1
|
||||
elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
||||
tot_fluid += 1
|
||||
else:
|
||||
rowTable(file.filepath)
|
||||
def rowTable(*data, id = None, class_style = None, extra = None):
|
||||
output("<tr")
|
||||
|
||||
if tot_cache > 0:
|
||||
rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(".cache", "none", "table-row")'")
|
||||
for file in job.files:
|
||||
if file.filepath.endswith(".bphys"):
|
||||
rowTable(os.path.split(file.filepath)[1], class_style = "cache")
|
||||
|
||||
if tot_fluid > 0:
|
||||
rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(".fluid", "none", "table-row")'")
|
||||
for file in job.files:
|
||||
if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
||||
rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
|
||||
if id:
|
||||
output(" id='%s'" % id)
|
||||
|
||||
endTable()
|
||||
|
||||
output("<h2>Blacklist</h2>")
|
||||
|
||||
if job.blacklist:
|
||||
startTable()
|
||||
headerTable("name", "address")
|
||||
|
||||
for slave_id in job.blacklist:
|
||||
slave = handler.server.slaves_map[slave_id]
|
||||
rowTable(slave.name, slave.address[0])
|
||||
|
||||
endTable()
|
||||
else:
|
||||
output("<i>Empty</i>")
|
||||
if class_style:
|
||||
output(" class='%s'" % class_style)
|
||||
|
||||
output("<h2>Frames</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable("no", "status", "render time", "slave", "log", "result")
|
||||
|
||||
for frame in job.frames:
|
||||
rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else " ")
|
||||
|
||||
endTable()
|
||||
else:
|
||||
output("no such job")
|
||||
|
||||
output("</body></html>")
|
||||
if extra:
|
||||
output(" %s" % extra)
|
||||
|
||||
output(">")
|
||||
|
||||
for c in data:
|
||||
output("<td>" + str(c) + "</td>")
|
||||
|
||||
output("</tr>")
|
||||
|
||||
def endTable():
|
||||
output("</table>")
|
||||
|
||||
if handler.path == "/html/netrender.js":
|
||||
f = open(os.path.join(src_folder, "netrender.js"), 'rb')
|
||||
|
||||
handler.send_head(content = "text/javascript")
|
||||
shutil.copyfileobj(f, handler.wfile)
|
||||
|
||||
f.close()
|
||||
elif handler.path == "/html/netrender.css":
|
||||
f = open(os.path.join(src_folder, "netrender.css"), 'rb')
|
||||
|
||||
handler.send_head(content = "text/css")
|
||||
shutil.copyfileobj(f, handler.wfile)
|
||||
|
||||
f.close()
|
||||
elif handler.path == "/html" or handler.path == "/":
|
||||
handler.send_head(content = "text/html")
|
||||
head("NetRender")
|
||||
|
||||
output("<h2>Master</h2>")
|
||||
|
||||
output("""<button title="remove all jobs" onclick="request('/clear', null);">CLEAR JOB LIST</button>""")
|
||||
|
||||
startTable(caption = "Rules", class_style = "rules")
|
||||
|
||||
headerTable("type", "description", "limit")
|
||||
|
||||
for rule in handler.server.balancer.rules:
|
||||
rowTable("rating", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||
|
||||
for rule in handler.server.balancer.priorities:
|
||||
rowTable("priority", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||
|
||||
for rule in handler.server.balancer.exceptions:
|
||||
rowTable("exception", rule, rule.str_limit() if hasattr(rule, "limit") else " ")
|
||||
|
||||
endTable()
|
||||
|
||||
output("<h2>Slaves</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable("name", "address", "last seen", "stats", "job")
|
||||
|
||||
for slave in handler.server.slaves:
|
||||
rowTable(slave.name, slave.address[0], time.ctime(slave.last_seen), slave.stats, link(slave.job.name, "/html/job" + slave.job.id) if slave.job else "None")
|
||||
|
||||
endTable()
|
||||
|
||||
output("<h2>Jobs</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable(
|
||||
" ",
|
||||
"id",
|
||||
"name",
|
||||
"category",
|
||||
"chunks",
|
||||
"priority",
|
||||
"usage",
|
||||
"wait",
|
||||
"status",
|
||||
"length",
|
||||
"done",
|
||||
"dispatched",
|
||||
"error",
|
||||
"first",
|
||||
"exception"
|
||||
)
|
||||
|
||||
handler.server.balance()
|
||||
|
||||
for job in handler.server.jobs:
|
||||
results = job.framesStatus()
|
||||
rowTable(
|
||||
"""<button title="cancel job" onclick="request('/cancel_%s', null);">X</button>""" % job.id +
|
||||
"""<button title="reset all frames" onclick="request('/resetall_%s_0', null);">R</button>""" % job.id,
|
||||
job.id,
|
||||
link(job.name, "/html/job" + job.id),
|
||||
job.category if job.category else "<i>None</i>",
|
||||
str(job.chunks) +
|
||||
"""<button title="increase priority" onclick="request('/edit_%s', "{'chunks': %i}");">+</button>""" % (job.id, job.chunks + 1) +
|
||||
"""<button title="decrease priority" onclick="request('/edit_%s', "{'chunks': %i}");" %s>-</button>""" % (job.id, job.chunks - 1, "disabled=True" if job.chunks == 1 else ""),
|
||||
str(job.priority) +
|
||||
"""<button title="increase chunks size" onclick="request('/edit_%s', "{'priority': %i}");">+</button>""" % (job.id, job.priority + 1) +
|
||||
"""<button title="decrease chunks size" onclick="request('/edit_%s', "{'priority': %i}");" %s>-</button>""" % (job.id, job.priority - 1, "disabled=True" if job.priority == 1 else ""),
|
||||
"%0.1f%%" % (job.usage * 100),
|
||||
"%is" % int(time.time() - job.last_dispatched),
|
||||
job.statusText(),
|
||||
len(job),
|
||||
results[DONE],
|
||||
results[DISPATCHED],
|
||||
str(results[ERROR]) +
|
||||
"""<button title="reset error frames" onclick="request('/reset_%s_0', null);" %s>R</button>""" % (job.id, "disabled=True" if not results[ERROR] else ""),
|
||||
handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job)
|
||||
)
|
||||
|
||||
endTable()
|
||||
|
||||
output("</body></html>")
|
||||
|
||||
elif handler.path.startswith("/html/job"):
|
||||
handler.send_head(content = "text/html")
|
||||
job_id = handler.path[9:]
|
||||
|
||||
head("NetRender")
|
||||
|
||||
job = handler.server.getJobID(job_id)
|
||||
|
||||
if job:
|
||||
output("<h2>Files</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable("path")
|
||||
|
||||
tot_cache = 0
|
||||
tot_fluid = 0
|
||||
|
||||
for file in job.files:
|
||||
if file.filepath.endswith(".bphys"):
|
||||
tot_cache += 1
|
||||
elif file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
||||
tot_fluid += 1
|
||||
else:
|
||||
rowTable(file.filepath)
|
||||
|
||||
if tot_cache > 0:
|
||||
rowTable("%i physic cache files" % tot_cache, class_style = "toggle", extra = "onclick='toggleDisplay(".cache", "none", "table-row")'")
|
||||
for file in job.files:
|
||||
if file.filepath.endswith(".bphys"):
|
||||
rowTable(os.path.split(file.filepath)[1], class_style = "cache")
|
||||
|
||||
if tot_fluid > 0:
|
||||
rowTable("%i fluid bake files" % tot_fluid, class_style = "toggle", extra = "onclick='toggleDisplay(".fluid", "none", "table-row")'")
|
||||
for file in job.files:
|
||||
if file.filepath.endswith(".bobj.gz") or file.filepath.endswith(".bvel.gz"):
|
||||
rowTable(os.path.split(file.filepath)[1], class_style = "fluid")
|
||||
|
||||
endTable()
|
||||
|
||||
output("<h2>Blacklist</h2>")
|
||||
|
||||
if job.blacklist:
|
||||
startTable()
|
||||
headerTable("name", "address")
|
||||
|
||||
for slave_id in job.blacklist:
|
||||
slave = handler.server.slaves_map[slave_id]
|
||||
rowTable(slave.name, slave.address[0])
|
||||
|
||||
endTable()
|
||||
else:
|
||||
output("<i>Empty</i>")
|
||||
|
||||
output("<h2>Frames</h2>")
|
||||
|
||||
startTable()
|
||||
headerTable("no", "status", "render time", "slave", "log", "result")
|
||||
|
||||
for frame in job.frames:
|
||||
rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else " ")
|
||||
|
||||
endTable()
|
||||
else:
|
||||
output("no such job")
|
||||
|
||||
output("</body></html>")
|
||||
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -23,256 +23,256 @@ import subprocess, shutil, time, hashlib
|
||||
from netrender.utils import *
|
||||
|
||||
class LogFile:
|
||||
def __init__(self, job_id = 0, slave_id = 0, frames = []):
|
||||
self.job_id = job_id
|
||||
self.slave_id = slave_id
|
||||
self.frames = frames
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"job_id": self.job_id,
|
||||
"slave_id": self.slave_id,
|
||||
"frames": self.frames
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
logfile = LogFile()
|
||||
logfile.job_id = data["job_id"]
|
||||
logfile.slave_id = data["slave_id"]
|
||||
logfile.frames = data["frames"]
|
||||
|
||||
return logfile
|
||||
def __init__(self, job_id = 0, slave_id = 0, frames = []):
|
||||
self.job_id = job_id
|
||||
self.slave_id = slave_id
|
||||
self.frames = frames
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"job_id": self.job_id,
|
||||
"slave_id": self.slave_id,
|
||||
"frames": self.frames
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
logfile = LogFile()
|
||||
logfile.job_id = data["job_id"]
|
||||
logfile.slave_id = data["slave_id"]
|
||||
logfile.frames = data["frames"]
|
||||
|
||||
return logfile
|
||||
|
||||
class RenderSlave:
|
||||
_slave_map = {}
|
||||
|
||||
def __init__(self):
|
||||
self.id = ""
|
||||
self.name = ""
|
||||
self.address = ("",0)
|
||||
self.stats = ""
|
||||
self.total_done = 0
|
||||
self.total_error = 0
|
||||
self.last_seen = 0.0
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"address": self.address,
|
||||
"stats": self.stats,
|
||||
"total_done": self.total_done,
|
||||
"total_error": self.total_error,
|
||||
"last_seen": self.last_seen
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def materialize(data, cache = True):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
slave_id = data["id"]
|
||||
_slave_map = {}
|
||||
|
||||
if cache and slave_id in RenderSlave._slave_map:
|
||||
return RenderSlave._slave_map[slave_id]
|
||||
def __init__(self):
|
||||
self.id = ""
|
||||
self.name = ""
|
||||
self.address = ("",0)
|
||||
self.stats = ""
|
||||
self.total_done = 0
|
||||
self.total_error = 0
|
||||
self.last_seen = 0.0
|
||||
|
||||
slave = RenderSlave()
|
||||
slave.id = slave_id
|
||||
slave.name = data["name"]
|
||||
slave.address = data["address"]
|
||||
slave.stats = data["stats"]
|
||||
slave.total_done = data["total_done"]
|
||||
slave.total_error = data["total_error"]
|
||||
slave.last_seen = data["last_seen"]
|
||||
def serialize(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"address": self.address,
|
||||
"stats": self.stats,
|
||||
"total_done": self.total_done,
|
||||
"total_error": self.total_error,
|
||||
"last_seen": self.last_seen
|
||||
}
|
||||
|
||||
if cache:
|
||||
RenderSlave._slave_map[slave_id] = slave
|
||||
|
||||
return slave
|
||||
@staticmethod
|
||||
def materialize(data, cache = True):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
slave_id = data["id"]
|
||||
|
||||
if cache and slave_id in RenderSlave._slave_map:
|
||||
return RenderSlave._slave_map[slave_id]
|
||||
|
||||
slave = RenderSlave()
|
||||
slave.id = slave_id
|
||||
slave.name = data["name"]
|
||||
slave.address = data["address"]
|
||||
slave.stats = data["stats"]
|
||||
slave.total_done = data["total_done"]
|
||||
slave.total_error = data["total_error"]
|
||||
slave.last_seen = data["last_seen"]
|
||||
|
||||
if cache:
|
||||
RenderSlave._slave_map[slave_id] = slave
|
||||
|
||||
return slave
|
||||
|
||||
JOB_BLENDER = 1
|
||||
JOB_PROCESS = 2
|
||||
|
||||
JOB_TYPES = {
|
||||
JOB_BLENDER: "Blender",
|
||||
JOB_PROCESS: "Process"
|
||||
}
|
||||
JOB_BLENDER: "Blender",
|
||||
JOB_PROCESS: "Process"
|
||||
}
|
||||
|
||||
class RenderFile:
|
||||
def __init__(self, filepath = "", index = 0, start = -1, end = -1):
|
||||
self.filepath = filepath
|
||||
self.index = index
|
||||
self.start = start
|
||||
self.end = end
|
||||
def __init__(self, filepath = "", index = 0, start = -1, end = -1):
|
||||
self.filepath = filepath
|
||||
self.index = index
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"filepath": self.filepath,
|
||||
"index": self.index,
|
||||
"start": self.start,
|
||||
"end": self.end
|
||||
}
|
||||
def serialize(self):
|
||||
return {
|
||||
"filepath": self.filepath,
|
||||
"index": self.index,
|
||||
"start": self.start,
|
||||
"end": self.end
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"])
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
return rfile
|
||||
rfile = RenderFile(data["filepath"], data["index"], data["start"], data["end"])
|
||||
|
||||
return rfile
|
||||
|
||||
class RenderJob:
|
||||
def __init__(self, job_info = None):
|
||||
self.id = ""
|
||||
self.type = JOB_BLENDER
|
||||
self.name = ""
|
||||
self.category = "None"
|
||||
self.status = JOB_WAITING
|
||||
self.files = []
|
||||
self.chunks = 0
|
||||
self.priority = 0
|
||||
self.blacklist = []
|
||||
def __init__(self, job_info = None):
|
||||
self.id = ""
|
||||
self.type = JOB_BLENDER
|
||||
self.name = ""
|
||||
self.category = "None"
|
||||
self.status = JOB_WAITING
|
||||
self.files = []
|
||||
self.chunks = 0
|
||||
self.priority = 0
|
||||
self.blacklist = []
|
||||
|
||||
self.usage = 0.0
|
||||
self.last_dispatched = 0.0
|
||||
self.frames = []
|
||||
|
||||
if job_info:
|
||||
self.type = job_info.type
|
||||
self.name = job_info.name
|
||||
self.category = job_info.category
|
||||
self.status = job_info.status
|
||||
self.files = job_info.files
|
||||
self.chunks = job_info.chunks
|
||||
self.priority = job_info.priority
|
||||
self.blacklist = job_info.blacklist
|
||||
|
||||
def addFile(self, file_path, start=-1, end=-1):
|
||||
self.files.append(RenderFile(file_path, len(self.files), start, end))
|
||||
|
||||
def addFrame(self, frame_number, command = ""):
|
||||
frame = RenderFrame(frame_number, command)
|
||||
self.frames.append(frame)
|
||||
return frame
|
||||
|
||||
def __len__(self):
|
||||
return len(self.frames)
|
||||
|
||||
def countFrames(self, status=QUEUED):
|
||||
total = 0
|
||||
for f in self.frames:
|
||||
if f.status == status:
|
||||
total += 1
|
||||
|
||||
return total
|
||||
|
||||
def countSlaves(self):
|
||||
return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
|
||||
|
||||
def statusText(self):
|
||||
return JOB_STATUS_TEXT[self.status]
|
||||
|
||||
def framesStatus(self):
|
||||
results = {
|
||||
QUEUED: 0,
|
||||
DISPATCHED: 0,
|
||||
DONE: 0,
|
||||
ERROR: 0
|
||||
}
|
||||
|
||||
for frame in self.frames:
|
||||
results[frame.status] += 1
|
||||
|
||||
return results
|
||||
|
||||
def __contains__(self, frame_number):
|
||||
for f in self.frames:
|
||||
if f.number == frame_number:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __getitem__(self, frame_number):
|
||||
for f in self.frames:
|
||||
if f.number == frame_number:
|
||||
return f
|
||||
else:
|
||||
return None
|
||||
|
||||
def serialize(self, frames = None):
|
||||
min_frame = min((f.number for f in frames)) if frames else -1
|
||||
max_frame = max((f.number for f in frames)) if frames else -1
|
||||
return {
|
||||
"id": self.id,
|
||||
"type": self.type,
|
||||
"name": self.name,
|
||||
"category": self.category,
|
||||
"status": self.status,
|
||||
"files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
|
||||
"frames": [f.serialize() for f in self.frames if not frames or f in frames],
|
||||
"chunks": self.chunks,
|
||||
"priority": self.priority,
|
||||
"usage": self.usage,
|
||||
"blacklist": self.blacklist,
|
||||
"last_dispatched": self.last_dispatched
|
||||
}
|
||||
self.usage = 0.0
|
||||
self.last_dispatched = 0.0
|
||||
self.frames = []
|
||||
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
job = RenderJob()
|
||||
job.id = data["id"]
|
||||
job.type = data["type"]
|
||||
job.name = data["name"]
|
||||
job.category = data["category"]
|
||||
job.status = data["status"]
|
||||
job.files = [RenderFile.materialize(f) for f in data["files"]]
|
||||
job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
|
||||
job.chunks = data["chunks"]
|
||||
job.priority = data["priority"]
|
||||
job.usage = data["usage"]
|
||||
job.blacklist = data["blacklist"]
|
||||
job.last_dispatched = data["last_dispatched"]
|
||||
if job_info:
|
||||
self.type = job_info.type
|
||||
self.name = job_info.name
|
||||
self.category = job_info.category
|
||||
self.status = job_info.status
|
||||
self.files = job_info.files
|
||||
self.chunks = job_info.chunks
|
||||
self.priority = job_info.priority
|
||||
self.blacklist = job_info.blacklist
|
||||
|
||||
return job
|
||||
def addFile(self, file_path, start=-1, end=-1):
|
||||
self.files.append(RenderFile(file_path, len(self.files), start, end))
|
||||
|
||||
def addFrame(self, frame_number, command = ""):
|
||||
frame = RenderFrame(frame_number, command)
|
||||
self.frames.append(frame)
|
||||
return frame
|
||||
|
||||
def __len__(self):
|
||||
return len(self.frames)
|
||||
|
||||
def countFrames(self, status=QUEUED):
|
||||
total = 0
|
||||
for f in self.frames:
|
||||
if f.status == status:
|
||||
total += 1
|
||||
|
||||
return total
|
||||
|
||||
def countSlaves(self):
|
||||
return len(set((frame.slave for frame in self.frames if frame.status == DISPATCHED)))
|
||||
|
||||
def statusText(self):
|
||||
return JOB_STATUS_TEXT[self.status]
|
||||
|
||||
def framesStatus(self):
|
||||
results = {
|
||||
QUEUED: 0,
|
||||
DISPATCHED: 0,
|
||||
DONE: 0,
|
||||
ERROR: 0
|
||||
}
|
||||
|
||||
for frame in self.frames:
|
||||
results[frame.status] += 1
|
||||
|
||||
return results
|
||||
|
||||
def __contains__(self, frame_number):
|
||||
for f in self.frames:
|
||||
if f.number == frame_number:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __getitem__(self, frame_number):
|
||||
for f in self.frames:
|
||||
if f.number == frame_number:
|
||||
return f
|
||||
else:
|
||||
return None
|
||||
|
||||
def serialize(self, frames = None):
|
||||
min_frame = min((f.number for f in frames)) if frames else -1
|
||||
max_frame = max((f.number for f in frames)) if frames else -1
|
||||
return {
|
||||
"id": self.id,
|
||||
"type": self.type,
|
||||
"name": self.name,
|
||||
"category": self.category,
|
||||
"status": self.status,
|
||||
"files": [f.serialize() for f in self.files if f.start == -1 or not frames or (f.start <= max_frame and f.end >= min_frame)],
|
||||
"frames": [f.serialize() for f in self.frames if not frames or f in frames],
|
||||
"chunks": self.chunks,
|
||||
"priority": self.priority,
|
||||
"usage": self.usage,
|
||||
"blacklist": self.blacklist,
|
||||
"last_dispatched": self.last_dispatched
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
job = RenderJob()
|
||||
job.id = data["id"]
|
||||
job.type = data["type"]
|
||||
job.name = data["name"]
|
||||
job.category = data["category"]
|
||||
job.status = data["status"]
|
||||
job.files = [RenderFile.materialize(f) for f in data["files"]]
|
||||
job.frames = [RenderFrame.materialize(f) for f in data["frames"]]
|
||||
job.chunks = data["chunks"]
|
||||
job.priority = data["priority"]
|
||||
job.usage = data["usage"]
|
||||
job.blacklist = data["blacklist"]
|
||||
job.last_dispatched = data["last_dispatched"]
|
||||
|
||||
return job
|
||||
|
||||
class RenderFrame:
|
||||
def __init__(self, number = 0, command = ""):
|
||||
self.number = number
|
||||
self.time = 0
|
||||
self.status = QUEUED
|
||||
self.slave = None
|
||||
self.command = command
|
||||
def __init__(self, number = 0, command = ""):
|
||||
self.number = number
|
||||
self.time = 0
|
||||
self.status = QUEUED
|
||||
self.slave = None
|
||||
self.command = command
|
||||
|
||||
def statusText(self):
|
||||
return FRAME_STATUS_TEXT[self.status]
|
||||
def statusText(self):
|
||||
return FRAME_STATUS_TEXT[self.status]
|
||||
|
||||
def serialize(self):
|
||||
return {
|
||||
"number": self.number,
|
||||
"time": self.time,
|
||||
"status": self.status,
|
||||
"slave": None if not self.slave else self.slave.serialize(),
|
||||
"command": self.command
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
frame = RenderFrame()
|
||||
frame.number = data["number"]
|
||||
frame.time = data["time"]
|
||||
frame.status = data["status"]
|
||||
frame.slave = RenderSlave.materialize(data["slave"])
|
||||
frame.command = data["command"]
|
||||
def serialize(self):
|
||||
return {
|
||||
"number": self.number,
|
||||
"time": self.time,
|
||||
"status": self.status,
|
||||
"slave": None if not self.slave else self.slave.serialize(),
|
||||
"command": self.command
|
||||
}
|
||||
|
||||
return frame
|
||||
@staticmethod
|
||||
def materialize(data):
|
||||
if not data:
|
||||
return None
|
||||
|
||||
frame = RenderFrame()
|
||||
frame.number = data["number"]
|
||||
frame.time = data["time"]
|
||||
frame.status = data["status"]
|
||||
frame.slave = RenderSlave.materialize(data["slave"])
|
||||
frame.command = data["command"]
|
||||
|
||||
return frame
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -28,414 +28,414 @@ import netrender.model
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netslave_bake(bpy.types.Operator):
|
||||
'''NEED DESCRIPTION'''
|
||||
bl_idname = "render.netslavebake"
|
||||
bl_label = "Bake all in file"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
filename = bpy.data.filename
|
||||
path, name = os.path.split(filename)
|
||||
root, ext = os.path.splitext(name)
|
||||
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
||||
relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
|
||||
|
||||
# Force all point cache next to the blend file
|
||||
for object in bpy.data.objects:
|
||||
for modifier in object.modifiers:
|
||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||
modifier.settings.path = relative_path
|
||||
bpy.ops.fluid.bake({"active_object": object, "scene": scene})
|
||||
elif modifier.type == "CLOTH":
|
||||
modifier.point_cache.step = 1
|
||||
modifier.point_cache.disk_cache = True
|
||||
modifier.point_cache.external = False
|
||||
elif modifier.type == "SOFT_BODY":
|
||||
modifier.point_cache.step = 1
|
||||
modifier.point_cache.disk_cache = True
|
||||
modifier.point_cache.external = False
|
||||
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
||||
modifier.domain_settings.point_cache_low.step = 1
|
||||
modifier.domain_settings.point_cache_low.disk_cache = True
|
||||
modifier.domain_settings.point_cache_low.external = False
|
||||
modifier.domain_settings.point_cache_high.step = 1
|
||||
modifier.domain_settings.point_cache_high.disk_cache = True
|
||||
modifier.domain_settings.point_cache_high.external = False
|
||||
|
||||
# particles modifier are stupid and don't contain data
|
||||
# we have to go through the object property
|
||||
for psys in object.particle_systems:
|
||||
psys.point_cache.step = 1
|
||||
psys.point_cache.disk_cache = True
|
||||
psys.point_cache.external = False
|
||||
psys.point_cache.filepath = relative_path
|
||||
|
||||
bpy.ops.ptcache.bake_all()
|
||||
|
||||
#bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
'''NEED DESCRIPTION'''
|
||||
bl_idname = "render.netslavebake"
|
||||
bl_label = "Bake all in file"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
filename = bpy.data.filename
|
||||
path, name = os.path.split(filename)
|
||||
root, ext = os.path.splitext(name)
|
||||
default_path = path + os.sep + "blendcache_" + root + os.sep # need an API call for that
|
||||
relative_path = os.sep + os.sep + "blendcache_" + root + os.sep
|
||||
|
||||
# Force all point cache next to the blend file
|
||||
for object in bpy.data.objects:
|
||||
for modifier in object.modifiers:
|
||||
if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN":
|
||||
modifier.settings.path = relative_path
|
||||
bpy.ops.fluid.bake({"active_object": object, "scene": scene})
|
||||
elif modifier.type == "CLOTH":
|
||||
modifier.point_cache.step = 1
|
||||
modifier.point_cache.disk_cache = True
|
||||
modifier.point_cache.external = False
|
||||
elif modifier.type == "SOFT_BODY":
|
||||
modifier.point_cache.step = 1
|
||||
modifier.point_cache.disk_cache = True
|
||||
modifier.point_cache.external = False
|
||||
elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN":
|
||||
modifier.domain_settings.point_cache_low.step = 1
|
||||
modifier.domain_settings.point_cache_low.disk_cache = True
|
||||
modifier.domain_settings.point_cache_low.external = False
|
||||
modifier.domain_settings.point_cache_high.step = 1
|
||||
modifier.domain_settings.point_cache_high.disk_cache = True
|
||||
modifier.domain_settings.point_cache_high.external = False
|
||||
|
||||
# particles modifier are stupid and don't contain data
|
||||
# we have to go through the object property
|
||||
for psys in object.particle_systems:
|
||||
psys.point_cache.step = 1
|
||||
psys.point_cache.disk_cache = True
|
||||
psys.point_cache.external = False
|
||||
psys.point_cache.filepath = relative_path
|
||||
|
||||
bpy.ops.ptcache.bake_all()
|
||||
|
||||
#bpy.ops.wm.save_mainfile(path = path + os.sep + root + "_baked.blend")
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientanim(bpy.types.Operator):
|
||||
'''Start rendering an animation on network'''
|
||||
bl_idname = "render.netclientanim"
|
||||
bl_label = "Animation on network"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
'''Start rendering an animation on network'''
|
||||
bl_idname = "render.netclientanim"
|
||||
bl_label = "Animation on network"
|
||||
|
||||
if conn:
|
||||
# Sending file
|
||||
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
||||
conn.close()
|
||||
|
||||
bpy.ops.screen.render('INVOKE_AREA', animation=True)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
# Sending file
|
||||
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
||||
conn.close()
|
||||
|
||||
bpy.ops.screen.render('INVOKE_AREA', animation=True)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientsend(bpy.types.Operator):
|
||||
'''Send Render Job to the Network'''
|
||||
bl_idname = "render.netclientsend"
|
||||
bl_label = "Send job"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
try:
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
'''Send Render Job to the Network'''
|
||||
bl_idname = "render.netclientsend"
|
||||
bl_label = "Send job"
|
||||
|
||||
if conn:
|
||||
# Sending file
|
||||
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
||||
conn.close()
|
||||
self.report('INFO', "Job sent to master")
|
||||
except Exception as err:
|
||||
self.report('ERROR', str(err))
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
try:
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
# Sending file
|
||||
scene.network_render.job_id = client.clientSendJob(conn, scene, True)
|
||||
conn.close()
|
||||
self.report('INFO', "Job sent to master")
|
||||
except Exception as err:
|
||||
self.report('ERROR', str(err))
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientstatus(bpy.types.Operator):
|
||||
'''Refresh the status of the current jobs'''
|
||||
bl_idname = "render.netclientstatus"
|
||||
bl_label = "Client Status"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
'''Refresh the status of the current jobs'''
|
||||
bl_idname = "render.netclientstatus"
|
||||
bl_label = "Client Status"
|
||||
|
||||
if conn:
|
||||
conn.request("GET", "/status")
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
|
||||
|
||||
while(len(netsettings.jobs) > 0):
|
||||
netsettings.jobs.remove(0)
|
||||
|
||||
netrender.jobs = []
|
||||
|
||||
for j in jobs:
|
||||
netrender.jobs.append(j)
|
||||
netsettings.jobs.add()
|
||||
job = netsettings.jobs[-1]
|
||||
|
||||
j.results = j.framesStatus() # cache frame status
|
||||
|
||||
job.name = j.name
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.request("GET", "/status")
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
jobs = (netrender.model.RenderJob.materialize(j) for j in eval(str(response.read(), encoding='utf8')))
|
||||
|
||||
while(len(netsettings.jobs) > 0):
|
||||
netsettings.jobs.remove(0)
|
||||
|
||||
netrender.jobs = []
|
||||
|
||||
for j in jobs:
|
||||
netrender.jobs.append(j)
|
||||
netsettings.jobs.add()
|
||||
job = netsettings.jobs[-1]
|
||||
|
||||
j.results = j.framesStatus() # cache frame status
|
||||
|
||||
job.name = j.name
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientblacklistslave(bpy.types.Operator):
|
||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||
bl_idname = "render.netclientblacklistslave"
|
||||
bl_label = "Client Blacklist Slave"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
|
||||
if netsettings.active_slave_index >= 0:
|
||||
|
||||
# deal with data
|
||||
slave = netrender.slaves.pop(netsettings.active_slave_index)
|
||||
netrender.blacklist.append(slave)
|
||||
|
||||
# deal with rna
|
||||
netsettings.slaves_blacklist.add()
|
||||
netsettings.slaves_blacklist[-1].name = slave.name
|
||||
|
||||
netsettings.slaves.remove(netsettings.active_slave_index)
|
||||
netsettings.active_slave_index = -1
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||
bl_idname = "render.netclientblacklistslave"
|
||||
bl_label = "Client Blacklist Slave"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
|
||||
if netsettings.active_slave_index >= 0:
|
||||
|
||||
# deal with data
|
||||
slave = netrender.slaves.pop(netsettings.active_slave_index)
|
||||
netrender.blacklist.append(slave)
|
||||
|
||||
# deal with rna
|
||||
netsettings.slaves_blacklist.add()
|
||||
netsettings.slaves_blacklist[-1].name = slave.name
|
||||
|
||||
netsettings.slaves.remove(netsettings.active_slave_index)
|
||||
netsettings.active_slave_index = -1
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientwhitelistslave(bpy.types.Operator):
|
||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||
bl_idname = "render.netclientwhitelistslave"
|
||||
bl_label = "Client Whitelist Slave"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
|
||||
if netsettings.active_blacklisted_slave_index >= 0:
|
||||
|
||||
# deal with data
|
||||
slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
|
||||
netrender.slaves.append(slave)
|
||||
|
||||
# deal with rna
|
||||
netsettings.slaves.add()
|
||||
netsettings.slaves[-1].name = slave.name
|
||||
|
||||
netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
|
||||
netsettings.active_blacklisted_slave_index = -1
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||
bl_idname = "render.netclientwhitelistslave"
|
||||
bl_label = "Client Whitelist Slave"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
|
||||
if netsettings.active_blacklisted_slave_index >= 0:
|
||||
|
||||
# deal with data
|
||||
slave = netrender.blacklist.pop(netsettings.active_blacklisted_slave_index)
|
||||
netrender.slaves.append(slave)
|
||||
|
||||
# deal with rna
|
||||
netsettings.slaves.add()
|
||||
netsettings.slaves[-1].name = slave.name
|
||||
|
||||
netsettings.slaves_blacklist.remove(netsettings.active_blacklisted_slave_index)
|
||||
netsettings.active_blacklisted_slave_index = -1
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientslaves(bpy.types.Operator):
|
||||
'''Refresh status about available Render slaves'''
|
||||
bl_idname = "render.netclientslaves"
|
||||
bl_label = "Client Slaves"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.request("GET", "/slaves")
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
|
||||
|
||||
while(len(netsettings.slaves) > 0):
|
||||
netsettings.slaves.remove(0)
|
||||
|
||||
netrender.slaves = []
|
||||
|
||||
for s in slaves:
|
||||
for i in range(len(netrender.blacklist)):
|
||||
slave = netrender.blacklist[i]
|
||||
if slave.id == s.id:
|
||||
netrender.blacklist[i] = s
|
||||
netsettings.slaves_blacklist[i].name = s.name
|
||||
break
|
||||
else:
|
||||
netrender.slaves.append(s)
|
||||
|
||||
netsettings.slaves.add()
|
||||
slave = netsettings.slaves[-1]
|
||||
slave.name = s.name
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
'''Refresh status about available Render slaves'''
|
||||
bl_idname = "render.netclientslaves"
|
||||
bl_label = "Client Slaves"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.request("GET", "/slaves")
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
slaves = (netrender.model.RenderSlave.materialize(s) for s in eval(str(response.read(), encoding='utf8')))
|
||||
|
||||
while(len(netsettings.slaves) > 0):
|
||||
netsettings.slaves.remove(0)
|
||||
|
||||
netrender.slaves = []
|
||||
|
||||
for s in slaves:
|
||||
for i in range(len(netrender.blacklist)):
|
||||
slave = netrender.blacklist[i]
|
||||
if slave.id == s.id:
|
||||
netrender.blacklist[i] = s
|
||||
netsettings.slaves_blacklist[i].name = s.name
|
||||
break
|
||||
else:
|
||||
netrender.slaves.append(s)
|
||||
|
||||
netsettings.slaves.add()
|
||||
slave = netsettings.slaves[-1]
|
||||
slave.name = s.name
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientcancel(bpy.types.Operator):
|
||||
'''Cancel the selected network rendering job.'''
|
||||
bl_idname = "render.netclientcancel"
|
||||
bl_label = "Client Cancel"
|
||||
|
||||
def poll(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
job = netrender.jobs[netsettings.active_job_index]
|
||||
|
||||
conn.request("POST", cancelURL(job.id))
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
'''Cancel the selected network rendering job.'''
|
||||
bl_idname = "render.netclientcancel"
|
||||
bl_label = "Client Cancel"
|
||||
|
||||
def poll(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
job = netrender.jobs[netsettings.active_job_index]
|
||||
|
||||
conn.request("POST", cancelURL(job.id))
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
netsettings.jobs.remove(netsettings.active_job_index)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
netsettings.jobs.remove(netsettings.active_job_index)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class RENDER_OT_netclientcancelall(bpy.types.Operator):
|
||||
'''Cancel all running network rendering jobs.'''
|
||||
bl_idname = "render.netclientcancelall"
|
||||
bl_label = "Client Cancel All"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.request("POST", "/clear")
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
while(len(netsettings.jobs) > 0):
|
||||
netsettings.jobs.remove(0)
|
||||
'''Cancel all running network rendering jobs.'''
|
||||
bl_idname = "render.netclientcancelall"
|
||||
bl_label = "Client Cancel All"
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.request("POST", "/clear")
|
||||
|
||||
response = conn.getresponse()
|
||||
print( response.status, response.reason )
|
||||
|
||||
while(len(netsettings.jobs) > 0):
|
||||
netsettings.jobs.remove(0)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class netclientdownload(bpy.types.Operator):
|
||||
'''Download render results from the network'''
|
||||
bl_idname = "render.netclientdownload"
|
||||
bl_label = "Client Download"
|
||||
|
||||
def poll(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
rd = context.scene.render_data
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
job = netrender.jobs[netsettings.active_job_index]
|
||||
|
||||
for frame in job.frames:
|
||||
client.requestResult(conn, job.id, frame.number)
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status != http.client.OK:
|
||||
print("missing", frame.number)
|
||||
continue
|
||||
|
||||
print("got back", frame.number)
|
||||
|
||||
f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
|
||||
buf = response.read(1024)
|
||||
|
||||
while buf:
|
||||
f.write(buf)
|
||||
buf = response.read(1024)
|
||||
|
||||
f.close()
|
||||
|
||||
conn.close()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
'''Download render results from the network'''
|
||||
bl_idname = "render.netclientdownload"
|
||||
bl_label = "Client Download"
|
||||
|
||||
def poll(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
return netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
rd = context.scene.render_data
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
job = netrender.jobs[netsettings.active_job_index]
|
||||
|
||||
for frame in job.frames:
|
||||
client.requestResult(conn, job.id, frame.number)
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status != http.client.OK:
|
||||
print("missing", frame.number)
|
||||
continue
|
||||
|
||||
print("got back", frame.number)
|
||||
|
||||
f = open(netsettings.path + "%06d" % frame.number + ".exr", "wb")
|
||||
buf = response.read(1024)
|
||||
|
||||
while buf:
|
||||
f.write(buf)
|
||||
buf = response.read(1024)
|
||||
|
||||
f.close()
|
||||
|
||||
conn.close()
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class netclientscan(bpy.types.Operator):
|
||||
__slots__ = []
|
||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||
bl_idname = "render.netclientscan"
|
||||
bl_label = "Client Scan"
|
||||
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
address, port = clientScan(self.report)
|
||||
__slots__ = []
|
||||
'''Operator documentation text, will be used for the operator tooltip and python docs.'''
|
||||
bl_idname = "render.netclientscan"
|
||||
bl_label = "Client Scan"
|
||||
|
||||
if address:
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
netsettings.server_address = address
|
||||
netsettings.server_port = port
|
||||
|
||||
return {'FINISHED'}
|
||||
def poll(self, context):
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
print(dir(self))
|
||||
return self.execute(context)
|
||||
def execute(self, context):
|
||||
address, port = clientScan(self.report)
|
||||
|
||||
if address:
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
netsettings.server_address = address
|
||||
netsettings.server_port = port
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
print(dir(self))
|
||||
return self.execute(context)
|
||||
|
||||
@rnaType
|
||||
class netclientweb(bpy.types.Operator):
|
||||
'''Open new window with information about running rendering jobs'''
|
||||
bl_idname = "render.netclientweb"
|
||||
bl_label = "Open Master Monitor"
|
||||
|
||||
def poll(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
return netsettings.server_address != "[default]"
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
|
||||
|
||||
# open connection to make sure server exists
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
'''Open new window with information about running rendering jobs'''
|
||||
bl_idname = "render.netclientweb"
|
||||
bl_label = "Open Master Monitor"
|
||||
|
||||
def poll(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
return netsettings.server_address != "[default]"
|
||||
|
||||
def execute(self, context):
|
||||
netsettings = context.scene.network_render
|
||||
|
||||
|
||||
# open connection to make sure server exists
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port, self.report)
|
||||
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
webbrowser.open("http://%s:%i" % (netsettings.server_address, netsettings.server_port))
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
return self.execute(context)
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -30,218 +30,218 @@ MAX_TIMEOUT = 10
|
||||
INCREMENT_TIMEOUT = 1
|
||||
|
||||
if platform.system() == 'Windows' and platform.version() >= '5': # Error mode is only available on Win2k or higher, that's version 5
|
||||
import ctypes
|
||||
def SetErrorMode():
|
||||
val = ctypes.windll.kernel32.SetErrorMode(0x0002)
|
||||
ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
|
||||
return val
|
||||
|
||||
def RestoreErrorMode(val):
|
||||
ctypes.windll.kernel32.SetErrorMode(val)
|
||||
import ctypes
|
||||
def SetErrorMode():
|
||||
val = ctypes.windll.kernel32.SetErrorMode(0x0002)
|
||||
ctypes.windll.kernel32.SetErrorMode(val | 0x0002)
|
||||
return val
|
||||
|
||||
def RestoreErrorMode(val):
|
||||
ctypes.windll.kernel32.SetErrorMode(val)
|
||||
else:
|
||||
def SetErrorMode():
|
||||
return 0
|
||||
|
||||
def RestoreErrorMode(val):
|
||||
pass
|
||||
def SetErrorMode():
|
||||
return 0
|
||||
|
||||
def RestoreErrorMode(val):
|
||||
pass
|
||||
|
||||
def slave_Info():
|
||||
sysname, nodename, release, version, machine, processor = platform.uname()
|
||||
slave = netrender.model.RenderSlave()
|
||||
slave.name = nodename
|
||||
slave.stats = sysname + " " + release + " " + machine + " " + processor
|
||||
return slave
|
||||
sysname, nodename, release, version, machine, processor = platform.uname()
|
||||
slave = netrender.model.RenderSlave()
|
||||
slave.name = nodename
|
||||
slave.stats = sysname + " " + release + " " + machine + " " + processor
|
||||
return slave
|
||||
|
||||
def testCancel(conn, job_id, frame_number):
|
||||
conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
|
||||
conn.request("HEAD", "/status", headers={"job-id":job_id, "job-frame": str(frame_number)})
|
||||
|
||||
# cancelled if job isn't found anymore
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# cancelled if job isn't found anymore
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def testFile(conn, job_id, slave_id, file_index, JOB_PREFIX, file_path, main_path = None):
|
||||
job_full_path = prefixPath(JOB_PREFIX, file_path, main_path)
|
||||
|
||||
if not os.path.exists(job_full_path):
|
||||
temp_path = JOB_PREFIX + "slave.temp.blend"
|
||||
conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id})
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status != http.client.OK:
|
||||
return None # file for job not returned by server, need to return an error code to server
|
||||
|
||||
f = open(temp_path, "wb")
|
||||
buf = response.read(1024)
|
||||
|
||||
while buf:
|
||||
f.write(buf)
|
||||
buf = response.read(1024)
|
||||
|
||||
f.close()
|
||||
|
||||
os.renames(temp_path, job_full_path)
|
||||
|
||||
return job_full_path
|
||||
job_full_path = prefixPath(JOB_PREFIX, file_path, main_path)
|
||||
|
||||
if not os.path.exists(job_full_path):
|
||||
temp_path = JOB_PREFIX + "slave.temp.blend"
|
||||
conn.request("GET", fileURL(job_id, file_index), headers={"slave-id":slave_id})
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status != http.client.OK:
|
||||
return None # file for job not returned by server, need to return an error code to server
|
||||
|
||||
f = open(temp_path, "wb")
|
||||
buf = response.read(1024)
|
||||
|
||||
while buf:
|
||||
f.write(buf)
|
||||
buf = response.read(1024)
|
||||
|
||||
f.close()
|
||||
|
||||
os.renames(temp_path, job_full_path)
|
||||
|
||||
return job_full_path
|
||||
|
||||
def render_slave(engine, netsettings):
|
||||
timeout = 1
|
||||
|
||||
engine.update_stats("", "Network render node initiation")
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
||||
|
||||
if conn:
|
||||
conn.request("POST", "/slave", repr(slave_Info().serialize()))
|
||||
response = conn.getresponse()
|
||||
|
||||
slave_id = response.getheader("slave-id")
|
||||
|
||||
NODE_PREFIX = netsettings.path + "slave_" + slave_id + os.sep
|
||||
if not os.path.exists(NODE_PREFIX):
|
||||
os.mkdir(NODE_PREFIX)
|
||||
|
||||
while not engine.test_break():
|
||||
|
||||
conn.request("GET", "/job", headers={"slave-id":slave_id})
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status == http.client.OK:
|
||||
timeout = 1 # reset timeout on new job
|
||||
|
||||
job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8')))
|
||||
|
||||
JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep
|
||||
if not os.path.exists(JOB_PREFIX):
|
||||
os.mkdir(JOB_PREFIX)
|
||||
|
||||
|
||||
if job.type == netrender.model.JOB_BLENDER:
|
||||
job_path = job.files[0].filepath # path of main file
|
||||
main_path, main_file = os.path.split(job_path)
|
||||
|
||||
job_full_path = testFile(conn, job.id, slave_id, 0, JOB_PREFIX, job_path)
|
||||
print("Fullpath", job_full_path)
|
||||
print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
|
||||
engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
|
||||
|
||||
for rfile in job.files[1:]:
|
||||
print("\t", rfile.filepath)
|
||||
testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path)
|
||||
timeout = 1
|
||||
|
||||
# announce log to master
|
||||
logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
|
||||
conn.request("POST", "/log", bytes(repr(logfile.serialize()), encoding='utf8'))
|
||||
response = conn.getresponse()
|
||||
|
||||
|
||||
first_frame = job.frames[0].number
|
||||
|
||||
# start render
|
||||
start_t = time.time()
|
||||
|
||||
if job.type == netrender.model.JOB_BLENDER:
|
||||
frame_args = []
|
||||
|
||||
for frame in job.frames:
|
||||
print("frame", frame.number)
|
||||
frame_args += ["-f", str(frame.number)]
|
||||
|
||||
val = SetErrorMode()
|
||||
process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
RestoreErrorMode(val)
|
||||
elif job.type == netrender.model.JOB_PROCESS:
|
||||
command = job.frames[0].command
|
||||
val = SetErrorMode()
|
||||
process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
RestoreErrorMode(val)
|
||||
|
||||
headers = {"slave-id":slave_id}
|
||||
|
||||
cancelled = False
|
||||
stdout = bytes()
|
||||
run_t = time.time()
|
||||
while process.poll() == None and not cancelled:
|
||||
stdout += process.stdout.read(32)
|
||||
current_t = time.time()
|
||||
cancelled = engine.test_break()
|
||||
if current_t - run_t > CANCEL_POLL_SPEED:
|
||||
|
||||
# update logs if needed
|
||||
if stdout:
|
||||
# (only need to update on one frame, they are linked
|
||||
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
||||
response = conn.getresponse()
|
||||
|
||||
stdout = bytes()
|
||||
|
||||
run_t = current_t
|
||||
if testCancel(conn, job.id, first_frame):
|
||||
cancelled = True
|
||||
|
||||
# read leftovers if needed
|
||||
stdout += process.stdout.read()
|
||||
|
||||
if cancelled:
|
||||
# kill process if needed
|
||||
if process.poll() == None:
|
||||
process.terminate()
|
||||
continue # to next frame
|
||||
|
||||
total_t = time.time() - start_t
|
||||
|
||||
avg_t = total_t / len(job.frames)
|
||||
|
||||
status = process.returncode
|
||||
|
||||
print("status", status)
|
||||
|
||||
# flush the rest of the logs
|
||||
if stdout:
|
||||
# (only need to update on one frame, they are linked
|
||||
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
|
||||
headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
|
||||
|
||||
|
||||
if status == 0: # non zero status is error
|
||||
headers["job-result"] = str(DONE)
|
||||
for frame in job.frames:
|
||||
headers["job-frame"] = str(frame.number)
|
||||
|
||||
if job.type == netrender.model.JOB_BLENDER:
|
||||
# send image back to server
|
||||
f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
|
||||
conn.request("PUT", "/render", f, headers=headers)
|
||||
f.close()
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
elif job.type == netrender.model.JOB_PROCESS:
|
||||
conn.request("PUT", "/render", headers=headers)
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
else:
|
||||
headers["job-result"] = str(ERROR)
|
||||
for frame in job.frames:
|
||||
headers["job-frame"] = str(frame.number)
|
||||
# send error result back to server
|
||||
conn.request("PUT", "/render", headers=headers)
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
else:
|
||||
if timeout < MAX_TIMEOUT:
|
||||
timeout += INCREMENT_TIMEOUT
|
||||
|
||||
for i in range(timeout):
|
||||
time.sleep(1)
|
||||
if engine.test_break():
|
||||
conn.close()
|
||||
return
|
||||
|
||||
conn.close()
|
||||
engine.update_stats("", "Network render node initiation")
|
||||
|
||||
conn = clientConnection(netsettings.server_address, netsettings.server_port)
|
||||
|
||||
if conn:
|
||||
conn.request("POST", "/slave", repr(slave_Info().serialize()))
|
||||
response = conn.getresponse()
|
||||
|
||||
slave_id = response.getheader("slave-id")
|
||||
|
||||
NODE_PREFIX = netsettings.path + "slave_" + slave_id + os.sep
|
||||
if not os.path.exists(NODE_PREFIX):
|
||||
os.mkdir(NODE_PREFIX)
|
||||
|
||||
while not engine.test_break():
|
||||
|
||||
conn.request("GET", "/job", headers={"slave-id":slave_id})
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status == http.client.OK:
|
||||
timeout = 1 # reset timeout on new job
|
||||
|
||||
job = netrender.model.RenderJob.materialize(eval(str(response.read(), encoding='utf8')))
|
||||
|
||||
JOB_PREFIX = NODE_PREFIX + "job_" + job.id + os.sep
|
||||
if not os.path.exists(JOB_PREFIX):
|
||||
os.mkdir(JOB_PREFIX)
|
||||
|
||||
|
||||
if job.type == netrender.model.JOB_BLENDER:
|
||||
job_path = job.files[0].filepath # path of main file
|
||||
main_path, main_file = os.path.split(job_path)
|
||||
|
||||
job_full_path = testFile(conn, job.id, slave_id, 0, JOB_PREFIX, job_path)
|
||||
print("Fullpath", job_full_path)
|
||||
print("File:", main_file, "and %i other files" % (len(job.files) - 1,))
|
||||
engine.update_stats("", "Render File "+ main_file+ " for job "+ job.id)
|
||||
|
||||
for rfile in job.files[1:]:
|
||||
print("\t", rfile.filepath)
|
||||
testFile(conn, job.id, slave_id, rfile.index, JOB_PREFIX, rfile.filepath, main_path)
|
||||
|
||||
# announce log to master
|
||||
logfile = netrender.model.LogFile(job.id, slave_id, [frame.number for frame in job.frames])
|
||||
conn.request("POST", "/log", bytes(repr(logfile.serialize()), encoding='utf8'))
|
||||
response = conn.getresponse()
|
||||
|
||||
|
||||
first_frame = job.frames[0].number
|
||||
|
||||
# start render
|
||||
start_t = time.time()
|
||||
|
||||
if job.type == netrender.model.JOB_BLENDER:
|
||||
frame_args = []
|
||||
|
||||
for frame in job.frames:
|
||||
print("frame", frame.number)
|
||||
frame_args += ["-f", str(frame.number)]
|
||||
|
||||
val = SetErrorMode()
|
||||
process = subprocess.Popen([BLENDER_PATH, "-b", "-noaudio", job_full_path, "-o", JOB_PREFIX + "######", "-E", "BLENDER_RENDER", "-F", "MULTILAYER"] + frame_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
RestoreErrorMode(val)
|
||||
elif job.type == netrender.model.JOB_PROCESS:
|
||||
command = job.frames[0].command
|
||||
val = SetErrorMode()
|
||||
process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
RestoreErrorMode(val)
|
||||
|
||||
headers = {"slave-id":slave_id}
|
||||
|
||||
cancelled = False
|
||||
stdout = bytes()
|
||||
run_t = time.time()
|
||||
while process.poll() == None and not cancelled:
|
||||
stdout += process.stdout.read(32)
|
||||
current_t = time.time()
|
||||
cancelled = engine.test_break()
|
||||
if current_t - run_t > CANCEL_POLL_SPEED:
|
||||
|
||||
# update logs if needed
|
||||
if stdout:
|
||||
# (only need to update on one frame, they are linked
|
||||
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
||||
response = conn.getresponse()
|
||||
|
||||
stdout = bytes()
|
||||
|
||||
run_t = current_t
|
||||
if testCancel(conn, job.id, first_frame):
|
||||
cancelled = True
|
||||
|
||||
# read leftovers if needed
|
||||
stdout += process.stdout.read()
|
||||
|
||||
if cancelled:
|
||||
# kill process if needed
|
||||
if process.poll() == None:
|
||||
process.terminate()
|
||||
continue # to next frame
|
||||
|
||||
total_t = time.time() - start_t
|
||||
|
||||
avg_t = total_t / len(job.frames)
|
||||
|
||||
status = process.returncode
|
||||
|
||||
print("status", status)
|
||||
|
||||
# flush the rest of the logs
|
||||
if stdout:
|
||||
# (only need to update on one frame, they are linked
|
||||
conn.request("PUT", logURL(job.id, first_frame), stdout, headers=headers)
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
|
||||
headers = {"job-id":job.id, "slave-id":slave_id, "job-time":str(avg_t)}
|
||||
|
||||
|
||||
if status == 0: # non zero status is error
|
||||
headers["job-result"] = str(DONE)
|
||||
for frame in job.frames:
|
||||
headers["job-frame"] = str(frame.number)
|
||||
|
||||
if job.type == netrender.model.JOB_BLENDER:
|
||||
# send image back to server
|
||||
f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
|
||||
conn.request("PUT", "/render", f, headers=headers)
|
||||
f.close()
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
elif job.type == netrender.model.JOB_PROCESS:
|
||||
conn.request("PUT", "/render", headers=headers)
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
else:
|
||||
headers["job-result"] = str(ERROR)
|
||||
for frame in job.frames:
|
||||
headers["job-frame"] = str(frame.number)
|
||||
# send error result back to server
|
||||
conn.request("PUT", "/render", headers=headers)
|
||||
if conn.getresponse().status == http.client.NO_CONTENT:
|
||||
continue
|
||||
else:
|
||||
if timeout < MAX_TIMEOUT:
|
||||
timeout += INCREMENT_TIMEOUT
|
||||
|
||||
for i in range(timeout):
|
||||
time.sleep(1)
|
||||
if engine.test_break():
|
||||
conn.close()
|
||||
return
|
||||
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
pass
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -37,319 +37,319 @@ DONE = 2
|
||||
ERROR = 3
|
||||
|
||||
class RenderButtonsPanel(bpy.types.Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
|
||||
|
||||
def poll(self, context):
|
||||
rd = context.scene.render_data
|
||||
return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "render"
|
||||
# COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
|
||||
|
||||
def poll(self, context):
|
||||
rd = context.scene.render_data
|
||||
return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
|
||||
|
||||
# Setting panel, use in the scene for now.
|
||||
@rnaType
|
||||
class RENDER_PT_network_settings(RenderButtonsPanel):
|
||||
bl_label = "Network Settings"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
bl_label = "Network Settings"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
rd = scene.render_data
|
||||
|
||||
layout.active = True
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
scene = context.scene
|
||||
rd = scene.render_data
|
||||
|
||||
|
||||
if scene.network_render.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
|
||||
col.operator("screen.render", text="Start", icon='PLAY').animation = True
|
||||
layout.active = True
|
||||
|
||||
col.prop(scene.network_render, "mode")
|
||||
col.prop(scene.network_render, "path")
|
||||
col.prop(scene.network_render, "server_address")
|
||||
col.prop(scene.network_render, "server_port")
|
||||
|
||||
if scene.network_render.mode == "RENDER_MASTER":
|
||||
col.prop(scene.network_render, "server_broadcast")
|
||||
else:
|
||||
col.operator("render.netclientscan", icon='FILE_REFRESH', text="")
|
||||
split = layout.split()
|
||||
|
||||
col.operator("render.netclientweb", icon='QUESTION')
|
||||
col = split.column()
|
||||
|
||||
|
||||
if scene.network_render.mode in ("RENDER_MASTER", "RENDER_SLAVE"):
|
||||
col.operator("screen.render", text="Start", icon='PLAY').animation = True
|
||||
|
||||
col.prop(scene.network_render, "mode")
|
||||
col.prop(scene.network_render, "path")
|
||||
col.prop(scene.network_render, "server_address")
|
||||
col.prop(scene.network_render, "server_port")
|
||||
|
||||
if scene.network_render.mode == "RENDER_MASTER":
|
||||
col.prop(scene.network_render, "server_broadcast")
|
||||
else:
|
||||
col.operator("render.netclientscan", icon='FILE_REFRESH', text="")
|
||||
|
||||
col.operator("render.netclientweb", icon='QUESTION')
|
||||
|
||||
@rnaType
|
||||
class RENDER_PT_network_job(RenderButtonsPanel):
|
||||
bl_label = "Job Settings"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT")
|
||||
bl_label = "Job Settings"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT")
|
||||
|
||||
scene = context.scene
|
||||
rd = scene.render_data
|
||||
|
||||
layout.active = True
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
if scene.network_render.server_address != "[default]":
|
||||
col.operator("render.netclientanim", icon='RENDER_ANIMATION')
|
||||
col.operator("render.netclientsend", icon='FILE_BLEND')
|
||||
if scene.network_render.job_id:
|
||||
col.operator("screen.render", text="Get Results", icon='RENDER_ANIMATION').animation = True
|
||||
col.prop(scene.network_render, "job_name")
|
||||
col.prop(scene.network_render, "job_category")
|
||||
row = col.row()
|
||||
row.prop(scene.network_render, "priority")
|
||||
row.prop(scene.network_render, "chunks")
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
rd = scene.render_data
|
||||
|
||||
layout.active = True
|
||||
|
||||
split = layout.split()
|
||||
|
||||
col = split.column()
|
||||
if scene.network_render.server_address != "[default]":
|
||||
col.operator("render.netclientanim", icon='RENDER_ANIMATION')
|
||||
col.operator("render.netclientsend", icon='FILE_BLEND')
|
||||
if scene.network_render.job_id:
|
||||
col.operator("screen.render", text="Get Results", icon='RENDER_ANIMATION').animation = True
|
||||
col.prop(scene.network_render, "job_name")
|
||||
col.prop(scene.network_render, "job_category")
|
||||
row = col.row()
|
||||
row.prop(scene.network_render, "priority")
|
||||
row.prop(scene.network_render, "chunks")
|
||||
|
||||
@rnaType
|
||||
class RENDER_PT_network_slaves(RenderButtonsPanel):
|
||||
bl_label = "Slaves Status"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT"
|
||||
and scene.network_render.server_address != "[default]")
|
||||
bl_label = "Slaves Status"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT"
|
||||
and scene.network_render.server_address != "[default]")
|
||||
|
||||
row = layout.row()
|
||||
row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
sub = row.column(align=True)
|
||||
sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
|
||||
sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
|
||||
|
||||
if len(netrender.slaves) == 0 and len(netsettings.slaves) > 0:
|
||||
while(len(netsettings.slaves) > 0):
|
||||
netsettings.slaves.remove(0)
|
||||
|
||||
if netsettings.active_slave_index >= 0 and len(netsettings.slaves) > 0:
|
||||
layout.separator()
|
||||
|
||||
slave = netrender.slaves[netsettings.active_slave_index]
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
layout.label(text="Name: " + slave.name)
|
||||
layout.label(text="Address: " + slave.address[0])
|
||||
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
||||
layout.label(text="Stats: " + slave.stats)
|
||||
row = layout.row()
|
||||
row.template_list(netsettings, "slaves", netsettings, "active_slave_index", rows=2)
|
||||
|
||||
sub = row.column(align=True)
|
||||
sub.operator("render.netclientslaves", icon='FILE_REFRESH', text="")
|
||||
sub.operator("render.netclientblacklistslave", icon='ZOOMOUT', text="")
|
||||
|
||||
if len(netrender.slaves) == 0 and len(netsettings.slaves) > 0:
|
||||
while(len(netsettings.slaves) > 0):
|
||||
netsettings.slaves.remove(0)
|
||||
|
||||
if netsettings.active_slave_index >= 0 and len(netsettings.slaves) > 0:
|
||||
layout.separator()
|
||||
|
||||
slave = netrender.slaves[netsettings.active_slave_index]
|
||||
|
||||
layout.label(text="Name: " + slave.name)
|
||||
layout.label(text="Address: " + slave.address[0])
|
||||
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
||||
layout.label(text="Stats: " + slave.stats)
|
||||
|
||||
@rnaType
|
||||
class RENDER_PT_network_slaves_blacklist(RenderButtonsPanel):
|
||||
bl_label = "Slaves Blacklist"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT"
|
||||
and scene.network_render.server_address != "[default]")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
bl_label = "Slaves Blacklist"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
row = layout.row()
|
||||
row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT"
|
||||
and scene.network_render.server_address != "[default]")
|
||||
|
||||
sub = row.column(align=True)
|
||||
sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
if len(netrender.blacklist) == 0 and len(netsettings.slaves_blacklist) > 0:
|
||||
while(len(netsettings.slaves_blacklist) > 0):
|
||||
netsettings.slaves_blacklist.remove(0)
|
||||
|
||||
if netsettings.active_blacklisted_slave_index >= 0 and len(netsettings.slaves_blacklist) > 0:
|
||||
layout.separator()
|
||||
|
||||
slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
layout.label(text="Name: " + slave.name)
|
||||
layout.label(text="Address: " + slave.address[0])
|
||||
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
||||
layout.label(text="Stats: " + slave.stats)
|
||||
row = layout.row()
|
||||
row.template_list(netsettings, "slaves_blacklist", netsettings, "active_blacklisted_slave_index", rows=2)
|
||||
|
||||
sub = row.column(align=True)
|
||||
sub.operator("render.netclientwhitelistslave", icon='ZOOMOUT', text="")
|
||||
|
||||
if len(netrender.blacklist) == 0 and len(netsettings.slaves_blacklist) > 0:
|
||||
while(len(netsettings.slaves_blacklist) > 0):
|
||||
netsettings.slaves_blacklist.remove(0)
|
||||
|
||||
if netsettings.active_blacklisted_slave_index >= 0 and len(netsettings.slaves_blacklist) > 0:
|
||||
layout.separator()
|
||||
|
||||
slave = netrender.blacklist[netsettings.active_blacklisted_slave_index]
|
||||
|
||||
layout.label(text="Name: " + slave.name)
|
||||
layout.label(text="Address: " + slave.address[0])
|
||||
layout.label(text="Seen: " + time.ctime(slave.last_seen))
|
||||
layout.label(text="Stats: " + slave.stats)
|
||||
|
||||
@rnaType
|
||||
class RENDER_PT_network_jobs(RenderButtonsPanel):
|
||||
bl_label = "Jobs"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT"
|
||||
and scene.network_render.server_address != "[default]")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
bl_label = "Jobs"
|
||||
COMPAT_ENGINES = {'NET_RENDER'}
|
||||
|
||||
row = layout.row()
|
||||
row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
|
||||
def poll(self, context):
|
||||
scene = context.scene
|
||||
return (super().poll(context)
|
||||
and scene.network_render.mode == "RENDER_CLIENT"
|
||||
and scene.network_render.server_address != "[default]")
|
||||
|
||||
sub = row.column(align=True)
|
||||
sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
|
||||
sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
|
||||
sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
|
||||
sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
if len(netrender.jobs) == 0 and len(netsettings.jobs) > 0:
|
||||
while(len(netsettings.jobs) > 0):
|
||||
netsettings.jobs.remove(0)
|
||||
|
||||
if netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0:
|
||||
layout.separator()
|
||||
|
||||
job = netrender.jobs[netsettings.active_job_index]
|
||||
scene = context.scene
|
||||
netsettings = scene.network_render
|
||||
|
||||
layout.label(text="Name: %s" % job.name)
|
||||
layout.label(text="Length: %04i" % len(job))
|
||||
layout.label(text="Done: %04i" % job.results[DONE])
|
||||
layout.label(text="Error: %04i" % job.results[ERROR])
|
||||
row = layout.row()
|
||||
row.template_list(netsettings, "jobs", netsettings, "active_job_index", rows=2)
|
||||
|
||||
sub = row.column(align=True)
|
||||
sub.operator("render.netclientstatus", icon='FILE_REFRESH', text="")
|
||||
sub.operator("render.netclientcancel", icon='ZOOMOUT', text="")
|
||||
sub.operator("render.netclientcancelall", icon='PANEL_CLOSE', text="")
|
||||
sub.operator("render.netclientdownload", icon='RENDER_ANIMATION', text="")
|
||||
|
||||
if len(netrender.jobs) == 0 and len(netsettings.jobs) > 0:
|
||||
while(len(netsettings.jobs) > 0):
|
||||
netsettings.jobs.remove(0)
|
||||
|
||||
if netsettings.active_job_index >= 0 and len(netsettings.jobs) > 0:
|
||||
layout.separator()
|
||||
|
||||
job = netrender.jobs[netsettings.active_job_index]
|
||||
|
||||
layout.label(text="Name: %s" % job.name)
|
||||
layout.label(text="Length: %04i" % len(job))
|
||||
layout.label(text="Done: %04i" % job.results[DONE])
|
||||
layout.label(text="Error: %04i" % job.results[ERROR])
|
||||
|
||||
@rnaType
|
||||
class NetRenderSettings(bpy.types.IDPropertyGroup):
|
||||
pass
|
||||
pass
|
||||
|
||||
@rnaType
|
||||
class NetRenderSlave(bpy.types.IDPropertyGroup):
|
||||
pass
|
||||
pass
|
||||
|
||||
@rnaType
|
||||
class NetRenderJob(bpy.types.IDPropertyGroup):
|
||||
pass
|
||||
pass
|
||||
|
||||
bpy.types.Scene.PointerProperty(attr="network_render", type=NetRenderSettings, name="Network Render", description="Network Render Settings")
|
||||
|
||||
NetRenderSettings.StringProperty( attr="server_address",
|
||||
name="Server address",
|
||||
description="IP or name of the master render server",
|
||||
maxlen = 128,
|
||||
default = "[default]")
|
||||
name="Server address",
|
||||
description="IP or name of the master render server",
|
||||
maxlen = 128,
|
||||
default = "[default]")
|
||||
|
||||
NetRenderSettings.IntProperty( attr="server_port",
|
||||
name="Server port",
|
||||
description="port of the master render server",
|
||||
default = 8000,
|
||||
min=1,
|
||||
max=65535)
|
||||
name="Server port",
|
||||
description="port of the master render server",
|
||||
default = 8000,
|
||||
min=1,
|
||||
max=65535)
|
||||
|
||||
NetRenderSettings.BoolProperty( attr="server_broadcast",
|
||||
name="Broadcast server address",
|
||||
description="broadcast server address on local network",
|
||||
default = True)
|
||||
name="Broadcast server address",
|
||||
description="broadcast server address on local network",
|
||||
default = True)
|
||||
|
||||
default_path = os.environ.get("TEMP", None)
|
||||
|
||||
if not default_path:
|
||||
if os.name == 'nt':
|
||||
default_path = "c:/tmp/"
|
||||
else:
|
||||
default_path = "/tmp/"
|
||||
if os.name == 'nt':
|
||||
default_path = "c:/tmp/"
|
||||
else:
|
||||
default_path = "/tmp/"
|
||||
elif not default_path.endswith(os.sep):
|
||||
default_path += os.sep
|
||||
default_path += os.sep
|
||||
|
||||
NetRenderSettings.StringProperty( attr="path",
|
||||
name="Path",
|
||||
description="Path for temporary files",
|
||||
maxlen = 128,
|
||||
default = default_path)
|
||||
name="Path",
|
||||
description="Path for temporary files",
|
||||
maxlen = 128,
|
||||
default = default_path)
|
||||
|
||||
NetRenderSettings.StringProperty( attr="job_name",
|
||||
name="Job name",
|
||||
description="Name of the job",
|
||||
maxlen = 128,
|
||||
default = "[default]")
|
||||
name="Job name",
|
||||
description="Name of the job",
|
||||
maxlen = 128,
|
||||
default = "[default]")
|
||||
|
||||
NetRenderSettings.StringProperty( attr="job_category",
|
||||
name="Job category",
|
||||
description="Category of the job",
|
||||
maxlen = 128,
|
||||
default = "")
|
||||
name="Job category",
|
||||
description="Category of the job",
|
||||
maxlen = 128,
|
||||
default = "")
|
||||
|
||||
NetRenderSettings.IntProperty( attr="chunks",
|
||||
name="Chunks",
|
||||
description="Number of frame to dispatch to each slave in one chunk",
|
||||
default = 5,
|
||||
min=1,
|
||||
max=65535)
|
||||
name="Chunks",
|
||||
description="Number of frame to dispatch to each slave in one chunk",
|
||||
default = 5,
|
||||
min=1,
|
||||
max=65535)
|
||||
|
||||
NetRenderSettings.IntProperty( attr="priority",
|
||||
name="Priority",
|
||||
description="Priority of the job",
|
||||
default = 1,
|
||||
min=1,
|
||||
max=10)
|
||||
name="Priority",
|
||||
description="Priority of the job",
|
||||
default = 1,
|
||||
min=1,
|
||||
max=10)
|
||||
|
||||
NetRenderSettings.StringProperty( attr="job_id",
|
||||
name="Network job id",
|
||||
description="id of the last sent render job",
|
||||
maxlen = 64,
|
||||
default = "")
|
||||
name="Network job id",
|
||||
description="id of the last sent render job",
|
||||
maxlen = 64,
|
||||
default = "")
|
||||
|
||||
NetRenderSettings.IntProperty( attr="active_slave_index",
|
||||
name="Index of the active slave",
|
||||
description="",
|
||||
default = -1,
|
||||
min= -1,
|
||||
max=65535)
|
||||
name="Index of the active slave",
|
||||
description="",
|
||||
default = -1,
|
||||
min= -1,
|
||||
max=65535)
|
||||
|
||||
NetRenderSettings.IntProperty( attr="active_blacklisted_slave_index",
|
||||
name="Index of the active slave",
|
||||
description="",
|
||||
default = -1,
|
||||
min= -1,
|
||||
max=65535)
|
||||
name="Index of the active slave",
|
||||
description="",
|
||||
default = -1,
|
||||
min= -1,
|
||||
max=65535)
|
||||
|
||||
NetRenderSettings.IntProperty( attr="active_job_index",
|
||||
name="Index of the active job",
|
||||
description="",
|
||||
default = -1,
|
||||
min= -1,
|
||||
max=65535)
|
||||
name="Index of the active job",
|
||||
description="",
|
||||
default = -1,
|
||||
min= -1,
|
||||
max=65535)
|
||||
|
||||
NetRenderSettings.EnumProperty(attr="mode",
|
||||
items=(
|
||||
("RENDER_CLIENT", "Client", "Act as render client"),
|
||||
("RENDER_MASTER", "Master", "Act as render master"),
|
||||
("RENDER_SLAVE", "Slave", "Act as render slave"),
|
||||
),
|
||||
name="Network mode",
|
||||
description="Mode of operation of this instance",
|
||||
default="RENDER_CLIENT")
|
||||
items=(
|
||||
("RENDER_CLIENT", "Client", "Act as render client"),
|
||||
("RENDER_MASTER", "Master", "Act as render master"),
|
||||
("RENDER_SLAVE", "Slave", "Act as render slave"),
|
||||
),
|
||||
name="Network mode",
|
||||
description="Mode of operation of this instance",
|
||||
default="RENDER_CLIENT")
|
||||
|
||||
NetRenderSettings.CollectionProperty(attr="slaves", type=NetRenderSlave, name="Slaves", description="")
|
||||
NetRenderSettings.CollectionProperty(attr="slaves_blacklist", type=NetRenderSlave, name="Slaves Blacklist", description="")
|
||||
NetRenderSettings.CollectionProperty(attr="jobs", type=NetRenderJob, name="Job List", description="")
|
||||
|
||||
NetRenderSlave.StringProperty( attr="name",
|
||||
name="Name of the slave",
|
||||
description="",
|
||||
maxlen = 64,
|
||||
default = "")
|
||||
name="Name of the slave",
|
||||
description="",
|
||||
maxlen = 64,
|
||||
default = "")
|
||||
|
||||
NetRenderJob.StringProperty( attr="name",
|
||||
name="Name of the job",
|
||||
description="",
|
||||
maxlen = 128,
|
||||
default = "")
|
||||
name="Name of the job",
|
||||
description="",
|
||||
maxlen = 128,
|
||||
default = "")
|
||||
|
@ -4,12 +4,12 @@
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
@ -51,22 +51,22 @@ DONE = 2
|
||||
ERROR = 3
|
||||
|
||||
FRAME_STATUS_TEXT = {
|
||||
QUEUED: "Queued",
|
||||
DISPATCHED: "Dispatched",
|
||||
DONE: "Done",
|
||||
ERROR: "Error"
|
||||
}
|
||||
QUEUED: "Queued",
|
||||
DISPATCHED: "Dispatched",
|
||||
DONE: "Done",
|
||||
ERROR: "Error"
|
||||
}
|
||||
|
||||
def rnaType(rna_type):
|
||||
if bpy: bpy.types.register(rna_type)
|
||||
return rna_type
|
||||
if bpy: bpy.types.register(rna_type)
|
||||
return rna_type
|
||||
|
||||
def reporting(report, message, errorType = None):
|
||||
if errorType:
|
||||
t = 'ERROR'
|
||||
else:
|
||||
t = 'INFO'
|
||||
|
||||
|
||||
if report:
|
||||
report(t, message)
|
||||
return None
|
||||
@ -82,14 +82,14 @@ def clientScan(report = None):
|
||||
s.settimeout(30)
|
||||
|
||||
s.bind(('', 8000))
|
||||
|
||||
|
||||
buf, address = s.recvfrom(64)
|
||||
|
||||
|
||||
address = address[0]
|
||||
port = int(str(buf, encoding='utf8'))
|
||||
|
||||
|
||||
reporting(report, "Master server found")
|
||||
|
||||
|
||||
return (address, port)
|
||||
except socket.timeout:
|
||||
reporting(report, "No master server on network", IOError)
|
||||
@ -105,10 +105,10 @@ def clientConnection(address, port, report = None):
|
||||
address, port = clientScan()
|
||||
if address == "":
|
||||
return None
|
||||
|
||||
try:
|
||||
|
||||
try:
|
||||
conn = http.client.HTTPConnection(address, port)
|
||||
|
||||
|
||||
if conn:
|
||||
if clientVerifyVersion(conn):
|
||||
return conn
|
||||
@ -123,21 +123,21 @@ def clientConnection(address, port, report = None):
|
||||
raise
|
||||
|
||||
def clientVerifyVersion(conn):
|
||||
conn.request("GET", "/version")
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status != http.client.OK:
|
||||
conn.close()
|
||||
return False
|
||||
|
||||
server_version = response.read()
|
||||
|
||||
if server_version != VERSION:
|
||||
print("Incorrect server version!")
|
||||
print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
|
||||
return False
|
||||
|
||||
return True
|
||||
conn.request("GET", "/version")
|
||||
response = conn.getresponse()
|
||||
|
||||
if response.status != http.client.OK:
|
||||
conn.close()
|
||||
return False
|
||||
|
||||
server_version = response.read()
|
||||
|
||||
if server_version != VERSION:
|
||||
print("Incorrect server version!")
|
||||
print("expected", str(VERSION, encoding='utf8'), "received", str(server_version, encoding='utf8'))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def fileURL(job_id, file_index):
|
||||
return "/file_%s_%i" % (job_id, file_index)
|
||||
@ -152,20 +152,20 @@ def cancelURL(job_id):
|
||||
return "/cancel_%s" % (job_id)
|
||||
|
||||
def prefixPath(prefix_directory, file_path, prefix_path):
|
||||
if os.path.isabs(file_path):
|
||||
# if an absolute path, make sure path exists, if it doesn't, use relative local path
|
||||
full_path = file_path
|
||||
if not os.path.exists(full_path):
|
||||
p, n = os.path.split(full_path)
|
||||
|
||||
if prefix_path and p.startswith(prefix_path):
|
||||
directory = prefix_directory + p[len(prefix_path):]
|
||||
full_path = directory + os.sep + n
|
||||
if not os.path.exists(directory):
|
||||
os.mkdir(directory)
|
||||
else:
|
||||
full_path = prefix_directory + n
|
||||
else:
|
||||
full_path = prefix_directory + file_path
|
||||
|
||||
return full_path
|
||||
if os.path.isabs(file_path):
|
||||
# if an absolute path, make sure path exists, if it doesn't, use relative local path
|
||||
full_path = file_path
|
||||
if not os.path.exists(full_path):
|
||||
p, n = os.path.split(full_path)
|
||||
|
||||
if prefix_path and p.startswith(prefix_path):
|
||||
directory = prefix_directory + p[len(prefix_path):]
|
||||
full_path = directory + os.sep + n
|
||||
if not os.path.exists(directory):
|
||||
os.mkdir(directory)
|
||||
else:
|
||||
full_path = prefix_directory + n
|
||||
else:
|
||||
full_path = prefix_directory + file_path
|
||||
|
||||
return full_path
|
||||
|
@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
||||
bpy.context.scene.render_data.pixel_aspect_x = 1
|
||||
bpy.context.scene.render_data.pixel_aspect_y = 1
|
||||
bpy.context.scene.render_data.fps = 24
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
|
@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
||||
bpy.context.scene.render_data.pixel_aspect_x = 1
|
||||
bpy.context.scene.render_data.pixel_aspect_y = 1
|
||||
bpy.context.scene.render_data.fps = 24
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
|
@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
||||
bpy.context.scene.render_data.pixel_aspect_x = 54
|
||||
bpy.context.scene.render_data.pixel_aspect_y = 51
|
||||
bpy.context.scene.render_data.fps = 25
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
|
@ -4,4 +4,4 @@ bpy.context.scene.render_data.resolution_percentage = 100
|
||||
bpy.context.scene.render_data.pixel_aspect_x = 64
|
||||
bpy.context.scene.render_data.pixel_aspect_y = 45
|
||||
bpy.context.scene.render_data.fps = 25
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
bpy.context.scene.render_data.fps_base = 1
|
||||
|
@ -1476,13 +1476,13 @@ class USERPREF_PT_input(bpy.types.Panel):
|
||||
|
||||
for km in kc.keymaps:
|
||||
filtered_items = [kmi for kmi in km.items if filter in kmi.name.lower()]
|
||||
|
||||
|
||||
if len(filtered_items) != 0:
|
||||
km = km.active()
|
||||
|
||||
layout.set_context_pointer("keymap", km)
|
||||
col = layout.column()
|
||||
|
||||
|
||||
row = col.row()
|
||||
row.label(text=km.name, icon="DOT")
|
||||
|
||||
@ -1496,7 +1496,7 @@ class USERPREF_PT_input(bpy.types.Panel):
|
||||
|
||||
for kmi in filtered_items:
|
||||
self.draw_kmi(kc, km, kmi, col, 1)
|
||||
|
||||
|
||||
# "Add New" at end of keymap item list
|
||||
col = self.indented_layout(layout, 1)
|
||||
subcol = col.split(percentage=0.2).column()
|
||||
@ -1813,18 +1813,18 @@ class WM_OT_keyitem_add(bpy.types.Operator):
|
||||
wm = context.manager
|
||||
km = context.keymap
|
||||
kc = wm.default_keyconfig
|
||||
|
||||
|
||||
if km.modal:
|
||||
km.add_modal_item("", 'A', 'PRESS') # kmi
|
||||
else:
|
||||
km.add_item("none", 'A', 'PRESS') # kmi
|
||||
|
||||
|
||||
# clear filter and expand keymap so we can see the newly added item
|
||||
if kc.filter != '':
|
||||
kc.filter = ''
|
||||
km.items_expanded = True
|
||||
km.children_expanded = True
|
||||
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
@ -498,14 +498,14 @@ class VIEW3D_PT_tools_brush(PaintPanel):
|
||||
if not context.particle_edit_object:
|
||||
col = layout.split().column()
|
||||
row = col.row()
|
||||
|
||||
|
||||
if context.sculpt_object and brush:
|
||||
defaulttools = 8
|
||||
elif context.texture_paint_object and brush:
|
||||
defaulttools = 4
|
||||
else:
|
||||
defaulttools = 2
|
||||
|
||||
|
||||
row.template_list(settings, "brushes", settings, "active_brush_index", rows=2, maxrows=defaulttools)
|
||||
|
||||
col.template_ID(settings, "brush", new="brush.add")
|
||||
@ -539,7 +539,7 @@ class VIEW3D_PT_tools_brush(PaintPanel):
|
||||
elif context.sculpt_object and brush:
|
||||
col = layout.column()
|
||||
col.separator()
|
||||
|
||||
|
||||
row = col.row(align=True)
|
||||
row.prop(brush, "size", slider=True)
|
||||
row.prop(brush, "use_size_pressure", toggle=True, text="")
|
||||
@ -642,7 +642,7 @@ class VIEW3D_PT_tools_brush_tool(PaintPanel):
|
||||
sculpt = context.sculpt_object
|
||||
|
||||
col = layout.column(align=True)
|
||||
|
||||
|
||||
if context.sculpt_object:
|
||||
col.prop(brush, "sculpt_tool", expand=True)
|
||||
elif context.texture_paint_object:
|
||||
|
Loading…
Reference in New Issue
Block a user