PsychoBlend: fixed some bugs and made exporting even faster.

This commit is contained in:
Nathan Vegdahl 2017-06-10 01:38:33 -07:00
parent 9025715335
commit 7236d2e666
4 changed files with 72 additions and 31 deletions

View File

@ -1,6 +1,6 @@
import bpy 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: class Assembly:
def __init__(self, render_engine, objects, visible_layers, group_prefix="", translation_offset=(0,0,0)): 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): def take_sample(self, render_engine, scene, time):
for mat in self.materials: for mat in self.materials:
# Check if render is cancelled
if render_engine.test_break():
raise ExportCancelled()
mat.take_sample(render_engine, scene, time) mat.take_sample(render_engine, scene, time)
for ob in self.objects: for ob in self.objects:
# Check if render is cancelled
if render_engine.test_break():
raise ExportCancelled()
ob.take_sample(render_engine, scene, time) ob.take_sample(render_engine, scene, time)
for inst in self.instances: 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) inst.take_sample(render_engine, time, self.translation_offset)
def cleanup(self): def cleanup(self):
@ -131,11 +142,13 @@ class Mesh:
def __init__(self, render_engine, ob, name): def __init__(self, render_engine, ob, name):
self.ob = ob self.ob = ob
self.name = name self.name = name
self.needs_mb = needs_def_mb(self.ob)
self.time_meshes = [] self.time_meshes = []
def take_sample(self, render_engine, scene, time): def take_sample(self, render_engine, scene, time):
render_engine.update_stats("", "Psychopath: Collecting '%s' at time %f" % (self.ob.name, time)) if len(self.time_meshes) == 0 or self.needs_mb:
self.time_meshes += [self.ob.to_mesh(scene, True, 'RENDER')] 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): def cleanup(self):
for mesh in self.time_meshes: for mesh in self.time_meshes:
@ -184,7 +197,7 @@ class SphereLamp:
self.time_rad = [] self.time_rad = []
def take_sample(self, render_engine, scene, time): 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_col += [self.ob.data.color * self.ob.data.energy]
self.time_rad += [self.ob.data.shadow_soft_size] self.time_rad += [self.ob.data.shadow_soft_size]
@ -215,7 +228,7 @@ class RectLamp:
self.time_dim = [] self.time_dim = []
def take_sample(self, render_engine, scene, time): 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_col += [self.ob.data.color * self.ob.data.energy]
if ob.data.shape == 'RECTANGLE': if ob.data.shape == 'RECTANGLE':
self.time_dim += [(self.ob.data.size, self.ob.data.size_y)] 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): def __init__(self, render_engine, ob, data_name):
self.ob = ob self.ob = ob
self.data_name = data_name self.data_name = data_name
self.needs_mb = needs_xform_mb(self.ob)
self.time_xforms = [] self.time_xforms = []
def take_sample(self, render_engine, time, translation_offset): def take_sample(self, render_engine, time, translation_offset):
render_engine.update_stats("", "Psychopath: Collecting '%s' at time %f" % (self.ob.name, time)) if len(self.time_xforms) == 0 or self.needs_mb:
mat = self.ob.matrix_world.copy() render_engine.update_stats("", "Psychopath: Collecting '{}' xforms at time {}".format(self.ob.name, time))
mat[0][3] += translation_offset[0] mat = self.ob.matrix_world.copy()
mat[1][3] += translation_offset[1] mat[0][3] += translation_offset[0]
mat[2][3] += translation_offset[2] mat[1][3] += translation_offset[1]
self.time_xforms += [mat] mat[2][3] += translation_offset[2]
self.time_xforms += [mat]
def export(self, render_engine, w): def export(self, render_engine, w):
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name) render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)

View File

@ -4,13 +4,7 @@ from math import degrees, pi, log
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from .assembly import Assembly from .assembly import Assembly
from .util import escape_name, mat2str from .util import escape_name, mat2str, ExportCancelled
class ExportCancelled(Exception):
""" Indicates that the render was cancelled in the middle of exporting
the scene file.
"""
pass
class IndentedWriter: class IndentedWriter:
@ -181,7 +175,7 @@ class PsychoExporter:
root_assembly.export(self.render_engine, self.w) root_assembly.export(self.render_engine, self.w)
except ExportCancelled: except ExportCancelled:
root_assembly.cleanup() root_assembly.cleanup()
raise ExportCancelled() raise
else: else:
root_assembly.cleanup() root_assembly.cleanup()

View File

@ -34,7 +34,7 @@ class PsychopathRender(bpy.types.RenderEngine):
return psy_binary return psy_binary
return "" 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() psy_binary = PsychopathRender._locate_binary()
if not psy_binary: if not psy_binary:
print("Psychopath: could not execute psychopath, possibly Psychopath isn't installed") print("Psychopath: could not execute psychopath, possibly Psychopath isn't installed")
@ -86,6 +86,15 @@ class PsychopathRender(bpy.types.RenderEngine):
self.end_result(result) self.end_result(result)
def render(self, scene): 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 # has to be called to update the frame on exporting animations
scene.frame_set(scene.frame_current) scene.frame_set(scene.frame_current)
@ -116,22 +125,19 @@ class PsychopathRender(bpy.types.RenderEngine):
if use_stdin: if use_stdin:
# Start rendering # 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") self.update_stats("", "Psychopath: Not found")
return return
self.update_stats("", "Psychopath: Collecting...") self.update_stats("", "Psychopath: Collecting...")
# Export to Psychopath's stdin # Export to Psychopath's stdin
try: if not psy_export.PsychoExporter(self._process.stdin, self, scene).export_psy():
if not psy_export.PsychoExporter(self._process.stdin, self, scene).export_psy(): # Render cancelled in the middle of exporting,
# Render cancelled in the middle of exporting, # so just return.
# so just return.
return
self._process.stdin.write(bytes("__PSY_EOF__", "utf-8"))
self._process.stdin.flush()
except:
self._process.terminate() self._process.terminate()
raise return
self._process.stdin.write(bytes("__PSY_EOF__", "utf-8"))
self._process.stdin.flush()
self.update_stats("", "Psychopath: Building") self.update_stats("", "Psychopath: Building")
else: else:
@ -145,7 +151,7 @@ class PsychopathRender(bpy.types.RenderEngine):
# Start rendering # Start rendering
self.update_stats("", "Psychopath: Rendering from %s" % export_path) 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") self.update_stats("", "Psychopath: Not found")
return return

View File

@ -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): def mat2str(m):
""" Converts a matrix into a single-line string of values. """ 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 """ Determines if the given object needs to be exported with
deformation motion blur or not. 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: for mod in ob.modifiers:
if mod.type == 'SUBSURF': if mod.type == 'SUBSURF':
pass pass
elif mod.type == 'MULTIRES':
pass
elif mod.type == 'MIRROR': elif mod.type == 'MIRROR':
if mod.mirror_object == None: if mod.mirror_object == None:
pass pass
else: else:
return True 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: else:
return True return True