diff --git a/psychoblend/assembly.py b/psychoblend/assembly.py index ce90d7a..b4369c4 100644 --- a/psychoblend/assembly.py +++ b/psychoblend/assembly.py @@ -1,6 +1,6 @@ import bpy -from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb +from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb, ExportCancelled class Assembly: def __init__(self, render_engine, objects, visible_layers, group_prefix="", translation_offset=(0,0,0)): @@ -80,10 +80,21 @@ class Assembly: def take_sample(self, render_engine, scene, time): for mat in self.materials: + # Check if render is cancelled + if render_engine.test_break(): + raise ExportCancelled() mat.take_sample(render_engine, scene, time) + for ob in self.objects: + # Check if render is cancelled + if render_engine.test_break(): + raise ExportCancelled() ob.take_sample(render_engine, scene, time) + for inst in self.instances: + # Check if render is cancelled + if render_engine.test_break(): + raise ExportCancelled() inst.take_sample(render_engine, time, self.translation_offset) def cleanup(self): @@ -131,11 +142,13 @@ class Mesh: def __init__(self, render_engine, ob, name): self.ob = ob self.name = name + self.needs_mb = needs_def_mb(self.ob) self.time_meshes = [] def take_sample(self, render_engine, scene, time): - render_engine.update_stats("", "Psychopath: Collecting '%s' at time %f" % (self.ob.name, time)) - self.time_meshes += [self.ob.to_mesh(scene, True, 'RENDER')] + if len(self.time_meshes) == 0 or self.needs_mb: + render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time)) + self.time_meshes += [self.ob.to_mesh(scene, True, 'RENDER')] def cleanup(self): for mesh in self.time_meshes: @@ -184,7 +197,7 @@ class SphereLamp: self.time_rad = [] def take_sample(self, render_engine, scene, time): - render_engine.update_stats("", "Psychopath: Collecting '%s' at time %f" % (self.ob.name, time)) + render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time)) self.time_col += [self.ob.data.color * self.ob.data.energy] self.time_rad += [self.ob.data.shadow_soft_size] @@ -215,7 +228,7 @@ class RectLamp: self.time_dim = [] def take_sample(self, render_engine, scene, time): - render_engine.update_stats("", "Psychopath: Collecting '%s' at time %f" % (self.ob.name, time)) + render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time)) self.time_col += [self.ob.data.color * self.ob.data.energy] if ob.data.shape == 'RECTANGLE': self.time_dim += [(self.ob.data.size, self.ob.data.size_y)] @@ -243,15 +256,17 @@ class Instance: def __init__(self, render_engine, ob, data_name): self.ob = ob self.data_name = data_name + self.needs_mb = needs_xform_mb(self.ob) self.time_xforms = [] def take_sample(self, render_engine, time, translation_offset): - render_engine.update_stats("", "Psychopath: Collecting '%s' at time %f" % (self.ob.name, time)) - mat = self.ob.matrix_world.copy() - mat[0][3] += translation_offset[0] - mat[1][3] += translation_offset[1] - mat[2][3] += translation_offset[2] - self.time_xforms += [mat] + if len(self.time_xforms) == 0 or self.needs_mb: + render_engine.update_stats("", "Psychopath: Collecting '{}' xforms at time {}".format(self.ob.name, time)) + mat = self.ob.matrix_world.copy() + mat[0][3] += translation_offset[0] + mat[1][3] += translation_offset[1] + mat[2][3] += translation_offset[2] + self.time_xforms += [mat] def export(self, render_engine, w): render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name) diff --git a/psychoblend/psy_export.py b/psychoblend/psy_export.py index 6480894..cfa4546 100644 --- a/psychoblend/psy_export.py +++ b/psychoblend/psy_export.py @@ -4,13 +4,7 @@ from math import degrees, pi, log from mathutils import Vector, Matrix from .assembly import Assembly -from .util import escape_name, mat2str - -class ExportCancelled(Exception): - """ Indicates that the render was cancelled in the middle of exporting - the scene file. - """ - pass +from .util import escape_name, mat2str, ExportCancelled class IndentedWriter: @@ -181,7 +175,7 @@ class PsychoExporter: root_assembly.export(self.render_engine, self.w) except ExportCancelled: root_assembly.cleanup() - raise ExportCancelled() + raise else: root_assembly.cleanup() diff --git a/psychoblend/render.py b/psychoblend/render.py index acb4b32..895ea2f 100644 --- a/psychoblend/render.py +++ b/psychoblend/render.py @@ -34,7 +34,7 @@ class PsychopathRender(bpy.types.RenderEngine): return psy_binary return "" - def _render(self, scene, psy_filepath, use_stdin, crop): + def _start_psychopath(self, scene, psy_filepath, use_stdin, crop): psy_binary = PsychopathRender._locate_binary() if not psy_binary: print("Psychopath: could not execute psychopath, possibly Psychopath isn't installed") @@ -86,6 +86,15 @@ class PsychopathRender(bpy.types.RenderEngine): self.end_result(result) def render(self, scene): + self._process = None + try: + self._render(scene) + except: + if self.process != None: + self._process.terminate() + raise + + def _render(self, scene): # has to be called to update the frame on exporting animations scene.frame_set(scene.frame_current) @@ -116,22 +125,19 @@ class PsychopathRender(bpy.types.RenderEngine): if use_stdin: # Start rendering - if not self._render(scene, export_path, use_stdin, crop): + if not self._start_psychopath(scene, export_path, use_stdin, crop): self.update_stats("", "Psychopath: Not found") return self.update_stats("", "Psychopath: Collecting...") # Export to Psychopath's stdin - try: - if not psy_export.PsychoExporter(self._process.stdin, self, scene).export_psy(): - # Render cancelled in the middle of exporting, - # so just return. - return - self._process.stdin.write(bytes("__PSY_EOF__", "utf-8")) - self._process.stdin.flush() - except: + if not psy_export.PsychoExporter(self._process.stdin, self, scene).export_psy(): + # Render cancelled in the middle of exporting, + # so just return. self._process.terminate() - raise + return + self._process.stdin.write(bytes("__PSY_EOF__", "utf-8")) + self._process.stdin.flush() self.update_stats("", "Psychopath: Building") else: @@ -145,7 +151,7 @@ class PsychopathRender(bpy.types.RenderEngine): # Start rendering self.update_stats("", "Psychopath: Rendering from %s" % export_path) - if not self._render(scene, export_path, use_stdin, crop): + if not self._start_psychopath(scene, export_path, use_stdin, crop): self.update_stats("", "Psychopath: Not found") return diff --git a/psychoblend/util.py b/psychoblend/util.py index bd1721f..e51a430 100644 --- a/psychoblend/util.py +++ b/psychoblend/util.py @@ -1,3 +1,10 @@ +class ExportCancelled(Exception): + """ Indicates that the render was cancelled in the middle of exporting + the scene file. + """ + pass + + def mat2str(m): """ Converts a matrix into a single-line string of values. """ @@ -12,14 +19,33 @@ def needs_def_mb(ob): """ Determines if the given object needs to be exported with deformation motion blur or not. """ + anim = ob.animation_data + no_anim_data = anim == None or (anim.action == None and len(anim.nla_tracks) == 0 and len(anim.drivers) == 0) + for mod in ob.modifiers: if mod.type == 'SUBSURF': pass + elif mod.type == 'MULTIRES': + pass elif mod.type == 'MIRROR': if mod.mirror_object == None: pass else: return True + elif mod.type == 'BEVEL' and no_anim_data: + pass + elif mod.type == 'EDGE_SPLIT' and no_anim_data: + pass + elif mod.type == 'SOLIDIFY' and no_anim_data: + pass + elif mod.type == 'MASK' and no_anim_data: + pass + elif mod.type == 'REMESH' and no_anim_data: + pass + elif mod.type == 'TRIANGULATE' and no_anim_data: + pass + elif mod.type == 'WIREFRAME' and no_anim_data: + pass else: return True