Significant PsychoBlend improvements.
- Improved export time by quite a bit. - Added more fine-grained status updates during export so it doesn't feel like it's hanging.
This commit is contained in:
parent
9d92bd099d
commit
9025715335
265
psychoblend/assembly.py
Normal file
265
psychoblend/assembly.py
Normal file
|
@ -0,0 +1,265 @@
|
|||
import bpy
|
||||
|
||||
from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb
|
||||
|
||||
class Assembly:
|
||||
def __init__(self, render_engine, objects, visible_layers, group_prefix="", translation_offset=(0,0,0)):
|
||||
self.name = group_prefix
|
||||
self.translation_offset = translation_offset
|
||||
self.render_engine = render_engine
|
||||
|
||||
self.materials = []
|
||||
self.objects = []
|
||||
self.instances = []
|
||||
|
||||
self.mesh_names = set()
|
||||
self.assembly_names = set()
|
||||
|
||||
# Collect all the objects, materials, instances, etc.
|
||||
for ob in objects:
|
||||
# Check if render is cancelled
|
||||
if render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
|
||||
# Check if the object is visible for rendering
|
||||
vis_layer = False
|
||||
for i in range(len(ob.layers)):
|
||||
vis_layer = vis_layer or (ob.layers[i] and visible_layers[i])
|
||||
if ob.hide_render or not vis_layer:
|
||||
continue
|
||||
|
||||
# Store object data
|
||||
name = None
|
||||
|
||||
if ob.type == 'EMPTY':
|
||||
if ob.dupli_type == 'GROUP':
|
||||
name = group_prefix + "__" + escape_name(ob.dupli_group.name)
|
||||
if name not in self.assembly_names:
|
||||
self.assembly_names.add(name)
|
||||
self.objects += [Assembly(self.render_engine, ob.dupli_group.objects, ob.dupli_group.layers, name, ob.dupli_group.dupli_offset*-1)]
|
||||
elif ob.type == 'MESH':
|
||||
name = self.get_mesh(ob, group_prefix)
|
||||
elif ob.type == 'LAMP' and ob.data.type == 'POINT':
|
||||
name = self.get_sphere_lamp(ob, group_prefix)
|
||||
elif ob.type == 'LAMP' and ob.data.type == 'AREA':
|
||||
name = self.get_rect_lamp(ob, group_prefix)
|
||||
|
||||
# Store instance
|
||||
if name != None:
|
||||
self.instances += [Instance(render_engine, ob, name)]
|
||||
|
||||
def export(self, render_engine, w):
|
||||
if self.name == "":
|
||||
w.write("Assembly {\n")
|
||||
else:
|
||||
w.write("Assembly $%s {\n" % self.name)
|
||||
w.indent()
|
||||
|
||||
for mat in self.materials:
|
||||
# Check if render is cancelled
|
||||
if render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
mat.export(render_engine, w)
|
||||
|
||||
for ob in self.objects:
|
||||
# Check if render is cancelled
|
||||
if render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
ob.export(render_engine, w)
|
||||
|
||||
for inst in self.instances:
|
||||
# Check if render is cancelled
|
||||
if render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
inst.export(render_engine, w)
|
||||
|
||||
w.unindent()
|
||||
w.write("}\n")
|
||||
|
||||
#----------------
|
||||
|
||||
def take_sample(self, render_engine, scene, time):
|
||||
for mat in self.materials:
|
||||
mat.take_sample(render_engine, scene, time)
|
||||
for ob in self.objects:
|
||||
ob.take_sample(render_engine, scene, time)
|
||||
for inst in self.instances:
|
||||
inst.take_sample(render_engine, time, self.translation_offset)
|
||||
|
||||
def cleanup(self):
|
||||
for mat in self.materials:
|
||||
mat.cleanup()
|
||||
for ob in self.objects:
|
||||
ob.cleanup()
|
||||
|
||||
def get_mesh(self, ob, group_prefix):
|
||||
# Figure out if we need to export or not and figure out what name to
|
||||
# export with.
|
||||
has_modifiers = len(ob.modifiers) > 0
|
||||
deform_mb = needs_def_mb(ob)
|
||||
if has_modifiers or deform_mb:
|
||||
mesh_name = group_prefix + escape_name("__" + ob.name + "__" + ob.data.name + "_")
|
||||
else:
|
||||
mesh_name = group_prefix + escape_name("__" + ob.data.name + "_")
|
||||
should_export_mesh = mesh_name not in self.mesh_names
|
||||
|
||||
# Get mesh
|
||||
if should_export_mesh:
|
||||
self.mesh_names.add(mesh_name)
|
||||
self.objects += [Mesh(self.render_engine, ob, mesh_name)]
|
||||
return mesh_name
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_sphere_lamp(self, ob, group_prefix):
|
||||
name = group_prefix + "__" + escape_name(ob.name)
|
||||
self.objects += [SphereLamp(self.render_engine, ob, name)]
|
||||
return name
|
||||
|
||||
def get_rect_lamp(self, ob, group_prefix):
|
||||
name = group_prefix + "__" + escape_name(ob.name)
|
||||
self.objects += [RectLamp(self.render_engine, ob, name)]
|
||||
return name
|
||||
|
||||
|
||||
#=========================================================================
|
||||
|
||||
|
||||
class Mesh:
|
||||
""" Holds data for a mesh to be exported.
|
||||
"""
|
||||
def __init__(self, render_engine, ob, name):
|
||||
self.ob = ob
|
||||
self.name = name
|
||||
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')]
|
||||
|
||||
def cleanup(self):
|
||||
for mesh in self.time_meshes:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
def export(self, render_engine, w):
|
||||
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
|
||||
|
||||
if self.ob.data.psychopath.is_subdivision_surface == False:
|
||||
# Exporting normal mesh
|
||||
w.write("MeshSurface $%s {\n" % self.name)
|
||||
w.indent()
|
||||
else:
|
||||
# Exporting subdivision surface cage
|
||||
w.write("SubdivisionSurface $%s {\n" % self.name)
|
||||
w.indent()
|
||||
|
||||
# Write vertices
|
||||
for ti in range(len(self.time_meshes)):
|
||||
w.write("Vertices [")
|
||||
w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), False)
|
||||
w.write("]\n", False)
|
||||
|
||||
# Write face vertex counts
|
||||
w.write("FaceVertCounts [")
|
||||
w.write(" ".join([("%d" % len(p.vertices)) for p in self.time_meshes[0].polygons]), False)
|
||||
w.write("]\n", False)
|
||||
|
||||
# Write face vertex indices
|
||||
w.write("FaceVertIndices [")
|
||||
w.write(" ".join([("%d"%v) for p in self.time_meshes[0].polygons for v in p.vertices]), False)
|
||||
w.write("]\n", False)
|
||||
|
||||
# MeshSurface/SubdivisionSurface section end
|
||||
w.unindent()
|
||||
w.write("}\n")
|
||||
|
||||
|
||||
class SphereLamp:
|
||||
""" Holds data for a sphere light to be exported.
|
||||
"""
|
||||
def __init__(self, render_engine, ob, name):
|
||||
self.ob = ob
|
||||
self.name = name
|
||||
self.time_col = []
|
||||
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))
|
||||
self.time_col += [self.ob.data.color * self.ob.data.energy]
|
||||
self.time_rad += [self.ob.data.shadow_soft_size]
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
def export(self, render_engine, w):
|
||||
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
|
||||
|
||||
w.write("SphereLight $%s {\n" % self.name)
|
||||
w.indent()
|
||||
for col in self.time_col:
|
||||
w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||
for rad in self.time_rad:
|
||||
w.write("Radius [%f]\n" % rad)
|
||||
|
||||
w.unindent()
|
||||
w.write("}\n")
|
||||
|
||||
|
||||
class RectLamp:
|
||||
""" Holds data for a rectangular light to be exported.
|
||||
"""
|
||||
def __init__(self, render_engine, ob, name):
|
||||
self.ob = ob
|
||||
self.name = name
|
||||
self.time_col = []
|
||||
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))
|
||||
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)]
|
||||
else:
|
||||
self.time_dim += [(self.ob.data.size, self.ob.data.size)]
|
||||
|
||||
def cleanup(self):
|
||||
pass
|
||||
|
||||
def export(self, render_engine, w):
|
||||
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
|
||||
|
||||
w.write("RectangleLight $%s {\n" % self.name)
|
||||
w.indent()
|
||||
for col in self.time_col:
|
||||
w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||
for dim in self.time_dim:
|
||||
w.write("Dimensions [%f %f]\n" % dim)
|
||||
|
||||
w.unindent()
|
||||
w.write("}\n")
|
||||
|
||||
|
||||
class Instance:
|
||||
def __init__(self, render_engine, ob, data_name):
|
||||
self.ob = ob
|
||||
self.data_name = data_name
|
||||
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]
|
||||
|
||||
def export(self, render_engine, w):
|
||||
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
|
||||
|
||||
w.write("Instance {\n")
|
||||
w.indent()
|
||||
w.write("Data [$%s]\n" % self.data_name)
|
||||
for mat in self.time_xforms:
|
||||
w.write("Transform [%s]\n" % mat2str(mat.inverted()))
|
||||
w.unindent()
|
||||
w.write("}\n")
|
|
@ -3,6 +3,9 @@ import bpy
|
|||
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.
|
||||
|
@ -10,66 +13,6 @@ class ExportCancelled(Exception):
|
|||
pass
|
||||
|
||||
|
||||
def mat2str(m):
|
||||
""" Converts a matrix into a single-line string of values.
|
||||
"""
|
||||
s = ""
|
||||
for j in range(4):
|
||||
for i in range(4):
|
||||
s += (" %f" % m[i][j])
|
||||
return s[1:]
|
||||
|
||||
|
||||
def needs_def_mb(ob):
|
||||
""" Determines if the given object needs to be exported with
|
||||
deformation motion blur or not.
|
||||
"""
|
||||
for mod in ob.modifiers:
|
||||
if mod.type == 'SUBSURF':
|
||||
pass
|
||||
elif mod.type == 'MIRROR':
|
||||
if mod.mirror_object == None:
|
||||
pass
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
if ob.type == 'MESH':
|
||||
if ob.data.shape_keys == None:
|
||||
pass
|
||||
else:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def escape_name(name):
|
||||
name = name.replace("\\", "\\\\")
|
||||
name = name.replace(" ", "\\ ")
|
||||
name = name.replace("$", "\\$")
|
||||
name = name.replace("[", "\\[")
|
||||
name = name.replace("]", "\\]")
|
||||
name = name.replace("{", "\\{")
|
||||
name = name.replace("}", "\\}")
|
||||
return name
|
||||
|
||||
|
||||
def needs_xform_mb(ob):
|
||||
""" Determines if the given object needs to be exported with
|
||||
transformation motion blur or not.
|
||||
"""
|
||||
if ob.animation_data != None:
|
||||
return True
|
||||
|
||||
if len(ob.constraints) > 0:
|
||||
return True
|
||||
|
||||
if ob.parent != None:
|
||||
return needs_xform_mb(ob.parent)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class IndentedWriter:
|
||||
def __init__(self, file_handle):
|
||||
self.f = file_handle
|
||||
|
@ -91,8 +34,6 @@ class IndentedWriter:
|
|||
self.f.write(bytes(text, "utf-8"))
|
||||
|
||||
|
||||
|
||||
|
||||
class PsychoExporter:
|
||||
def __init__(self, f, render_engine, scene):
|
||||
self.w = IndentedWriter(f)
|
||||
|
@ -231,253 +172,23 @@ class PsychoExporter:
|
|||
|
||||
#######################
|
||||
# Export objects and materials
|
||||
# TODO: handle materials from linked files (as used in group
|
||||
# instances) properly.
|
||||
self.w.write("Assembly {\n")
|
||||
self.w.indent()
|
||||
self.export_materials(bpy.data.materials)
|
||||
self.export_objects(self.scene.objects, self.scene.layers)
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
try:
|
||||
root_assembly = Assembly(self.render_engine, self.scene.objects, self.scene.layers)
|
||||
for i in range(self.time_samples):
|
||||
time = self.fr + self.shutter_start + (self.shutter_diff*i)
|
||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||
root_assembly.take_sample(self.render_engine, self.scene, time)
|
||||
root_assembly.export(self.render_engine, self.w)
|
||||
except ExportCancelled:
|
||||
root_assembly.cleanup()
|
||||
raise ExportCancelled()
|
||||
else:
|
||||
root_assembly.cleanup()
|
||||
|
||||
# Scene end
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
|
||||
|
||||
def export_materials(self, materials):
|
||||
for m in materials:
|
||||
self.w.write("SurfaceShader $%s {\n" % escape_name(m.name))
|
||||
self.w.indent()
|
||||
self.w.write("Type [%s]\n" % m.psychopath.surface_shader_type)
|
||||
self.w.write("Color [%f %f %f]\n" % (m.psychopath.color[0], m.psychopath.color[1], m.psychopath.color[2]))
|
||||
if m.psychopath.surface_shader_type == 'GTR':
|
||||
self.w.write("Roughness [%f]\n" % m.psychopath.roughness)
|
||||
self.w.write("TailShape [%f]\n" % m.psychopath.tail_shape)
|
||||
self.w.write("Fresnel [%f]\n" % m.psychopath.fresnel)
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
|
||||
def export_objects(self, objects, visible_layers, group_prefix="", translation_offset=(0,0,0)):
|
||||
for ob in objects:
|
||||
# Check if render is cancelled
|
||||
if self.render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
|
||||
# Check if the object is visible for rendering
|
||||
vis_layer = False
|
||||
for i in range(len(ob.layers)):
|
||||
vis_layer = vis_layer or (ob.layers[i] and visible_layers[i])
|
||||
if ob.hide_render or not vis_layer:
|
||||
continue
|
||||
|
||||
name = None
|
||||
|
||||
# Write object data
|
||||
if ob.type == 'EMPTY':
|
||||
if ob.dupli_type == 'GROUP':
|
||||
name = group_prefix + "__" + escape_name(ob.dupli_group.name)
|
||||
if name not in self.group_names:
|
||||
self.group_names[name] = True
|
||||
self.w.write("Assembly $%s {\n" % name)
|
||||
self.w.indent()
|
||||
self.export_objects(ob.dupli_group.objects, ob.dupli_group.layers, name, ob.dupli_group.dupli_offset*-1)
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
elif ob.type == 'MESH':
|
||||
name = self.export_mesh_object(ob, group_prefix)
|
||||
elif ob.type == 'SURFACE':
|
||||
name = self.export_surface_object(ob, group_prefix)
|
||||
elif ob.type == 'LAMP' and ob.data.type == 'POINT':
|
||||
name = self.export_sphere_lamp(ob, group_prefix)
|
||||
elif ob.type == 'LAMP' and ob.data.type == 'AREA':
|
||||
name = self.export_area_lamp(ob, group_prefix)
|
||||
|
||||
# Write object instance, with transforms
|
||||
if name != None:
|
||||
time_mats = []
|
||||
|
||||
if needs_xform_mb(ob):
|
||||
for i in range(self.time_samples):
|
||||
# Check if render is cancelled
|
||||
if self.render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||
mat = ob.matrix_world.copy()
|
||||
mat[0][3] += translation_offset[0]
|
||||
mat[1][3] += translation_offset[1]
|
||||
mat[2][3] += translation_offset[2]
|
||||
time_mats += [mat]
|
||||
else:
|
||||
mat = ob.matrix_world.copy()
|
||||
mat[0][3] += translation_offset[0]
|
||||
mat[1][3] += translation_offset[1]
|
||||
mat[2][3] += translation_offset[2]
|
||||
time_mats += [mat]
|
||||
|
||||
self.w.write("Instance {\n")
|
||||
self.w.indent()
|
||||
self.w.write("Data [$%s]\n" % name)
|
||||
if len(ob.material_slots) > 0 and ob.material_slots[0].material != None:
|
||||
self.w.write("SurfaceShaderBind [$%s]\n" % escape_name(ob.material_slots[0].material.name))
|
||||
for i in range(len(time_mats)):
|
||||
mat = time_mats[i].inverted()
|
||||
self.w.write("Transform [%s]\n" % mat2str(mat))
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
|
||||
def export_mesh_object(self, ob, group_prefix):
|
||||
# Determine if and how to export the mesh data
|
||||
has_modifiers = len(ob.modifiers) > 0
|
||||
deform_mb = needs_def_mb(ob)
|
||||
if has_modifiers or deform_mb:
|
||||
mesh_name = group_prefix + escape_name("__" + ob.name + "__" + ob.data.name + "_")
|
||||
else:
|
||||
mesh_name = group_prefix + escape_name("__" + ob.data.name + "_")
|
||||
export_mesh = (mesh_name not in self.mesh_names) or has_modifiers or deform_mb
|
||||
|
||||
# Collect time samples
|
||||
time_meshes = []
|
||||
if deform_mb:
|
||||
for i in range(self.time_samples):
|
||||
# Check if render is cancelled
|
||||
if self.render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||
if export_mesh and (deform_mb or i == 0):
|
||||
time_meshes += [ob.to_mesh(self.scene, True, 'RENDER')]
|
||||
elif export_mesh:
|
||||
time_meshes += [ob.to_mesh(self.scene, True, 'RENDER')]
|
||||
|
||||
# Export mesh data if necessary
|
||||
if export_mesh:
|
||||
if ob.data.psychopath.is_subdivision_surface == False:
|
||||
# Exporting normal mesh
|
||||
self.mesh_names[mesh_name] = True
|
||||
self.w.write("MeshSurface $%s {\n" % mesh_name)
|
||||
self.w.indent()
|
||||
elif ob.data.psychopath.is_subdivision_surface == True:
|
||||
# Exporting subdivision surface cage
|
||||
self.mesh_names[mesh_name] = True
|
||||
self.w.write("SubdivisionSurface $%s {\n" % mesh_name)
|
||||
self.w.indent()
|
||||
|
||||
# Write vertices
|
||||
for ti in range(len(time_meshes)):
|
||||
self.w.write("Vertices [")
|
||||
self.w.write(" ".join([("%f" % i) for vert in time_meshes[ti].vertices for i in vert.co]), False)
|
||||
self.w.write("]\n", False)
|
||||
|
||||
# Write face vertex counts
|
||||
self.w.write("FaceVertCounts [")
|
||||
self.w.write(" ".join([("%d" % len(p.vertices)) for p in time_meshes[0].polygons]), False)
|
||||
self.w.write("]\n", False)
|
||||
|
||||
# Write face vertex indices
|
||||
self.w.write("FaceVertIndices [")
|
||||
self.w.write(" ".join([("%d"%v) for p in time_meshes[0].polygons for v in p.vertices]), False)
|
||||
self.w.write("]\n", False)
|
||||
|
||||
# MeshSurface/SubdivisionSurface section end
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
for mesh in time_meshes:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
|
||||
return mesh_name
|
||||
|
||||
|
||||
def export_surface_object(self, ob, group_prefix):
|
||||
name = group_prefix + "__" + escape_name(ob.name)
|
||||
|
||||
# Collect time samples
|
||||
time_surfaces = []
|
||||
for i in range(self.time_samples):
|
||||
# Check if render is cancelled
|
||||
if self.render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||
time_surfaces += [ob.data.copy()]
|
||||
|
||||
# Write patch
|
||||
self.w.write("BicubicPatch $" + name + " {\n")
|
||||
self.w.indent()
|
||||
for i in range(self.time_samples):
|
||||
verts = time_surfaces[i].splines[0].points
|
||||
vstr = ""
|
||||
for v in verts:
|
||||
vstr += ("%f %f %f " % (v.co[0], v.co[1], v.co[2]))
|
||||
self.w.write("Vertices [%s]\n" % vstr[:-1])
|
||||
for s in time_surfaces:
|
||||
bpy.data.curves.remove(s)
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def export_sphere_lamp(self, ob, group_prefix):
|
||||
name = group_prefix + "__" + escape_name(ob.name)
|
||||
|
||||
# Collect data over time
|
||||
time_col = []
|
||||
time_rad = []
|
||||
for i in range(self.time_samples):
|
||||
# Check if render is cancelled
|
||||
if self.render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||
time_col += [ob.data.color * ob.data.energy]
|
||||
time_rad += [ob.data.shadow_soft_size]
|
||||
|
||||
# Write out sphere light
|
||||
self.w.write("SphereLight $%s {\n" % name)
|
||||
self.w.indent()
|
||||
for col in time_col:
|
||||
self.w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||
for rad in time_rad:
|
||||
self.w.write("Radius [%f]\n" % rad)
|
||||
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
return name
|
||||
|
||||
def export_area_lamp(self, ob, group_prefix):
|
||||
name = group_prefix + "__" + escape_name(ob.name)
|
||||
|
||||
# Collect data over time
|
||||
time_col = []
|
||||
time_dim = []
|
||||
for i in range(self.time_samples):
|
||||
# Check if render is cancelled
|
||||
if self.render_engine.test_break():
|
||||
raise ExportCancelled()
|
||||
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
||||
time_col += [ob.data.color * ob.data.energy]
|
||||
if ob.data.shape == 'RECTANGLE':
|
||||
time_dim += [(ob.data.size, ob.data.size_y)]
|
||||
else:
|
||||
time_dim += [(ob.data.size, ob.data.size)]
|
||||
|
||||
|
||||
# Write out sphere light
|
||||
self.w.write("RectangleLight $%s {\n" % name)
|
||||
self.w.indent()
|
||||
for col in time_col:
|
||||
self.w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||
for dim in time_dim:
|
||||
self.w.write("Dimensions [%f %f]\n" % dim)
|
||||
|
||||
self.w.unindent()
|
||||
self.w.write("}\n")
|
||||
|
||||
return name
|
||||
|
||||
def export_world_distant_disk_lamp(self, ob, group_prefix):
|
||||
name = group_prefix + "__" + escape_name(ob.name)
|
||||
|
||||
|
|
|
@ -120,16 +120,20 @@ class PsychopathRender(bpy.types.RenderEngine):
|
|||
self.update_stats("", "Psychopath: Not found")
|
||||
return
|
||||
|
||||
self.update_stats("", "Psychopath: Exporting data from Blender")
|
||||
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:
|
||||
self._process.terminate()
|
||||
raise
|
||||
|
||||
self.update_stats("", "Psychopath: Rendering")
|
||||
self.update_stats("", "Psychopath: Building")
|
||||
else:
|
||||
# Export to file
|
||||
self.update_stats("", "Psychopath: Exporting data from Blender")
|
||||
|
@ -189,6 +193,8 @@ class PsychopathRender(bpy.types.RenderEngine):
|
|||
else:
|
||||
continue
|
||||
|
||||
self.update_stats("", "Psychopath: Rendering")
|
||||
|
||||
# Clear output buffer, since it's all in 'outputs' now.
|
||||
output = b""
|
||||
|
||||
|
|
58
psychoblend/util.py
Normal file
58
psychoblend/util.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
def mat2str(m):
|
||||
""" Converts a matrix into a single-line string of values.
|
||||
"""
|
||||
s = ""
|
||||
for j in range(4):
|
||||
for i in range(4):
|
||||
s += (" %f" % m[i][j])
|
||||
return s[1:]
|
||||
|
||||
|
||||
def needs_def_mb(ob):
|
||||
""" Determines if the given object needs to be exported with
|
||||
deformation motion blur or not.
|
||||
"""
|
||||
for mod in ob.modifiers:
|
||||
if mod.type == 'SUBSURF':
|
||||
pass
|
||||
elif mod.type == 'MIRROR':
|
||||
if mod.mirror_object == None:
|
||||
pass
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
|
||||
if ob.type == 'MESH':
|
||||
if ob.data.shape_keys == None:
|
||||
pass
|
||||
else:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def escape_name(name):
|
||||
name = name.replace("\\", "\\\\")
|
||||
name = name.replace(" ", "\\ ")
|
||||
name = name.replace("$", "\\$")
|
||||
name = name.replace("[", "\\[")
|
||||
name = name.replace("]", "\\]")
|
||||
name = name.replace("{", "\\{")
|
||||
name = name.replace("}", "\\}")
|
||||
return name
|
||||
|
||||
|
||||
def needs_xform_mb(ob):
|
||||
""" Determines if the given object needs to be exported with
|
||||
transformation motion blur or not.
|
||||
"""
|
||||
if ob.animation_data != None:
|
||||
return True
|
||||
|
||||
if len(ob.constraints) > 0:
|
||||
return True
|
||||
|
||||
if ob.parent != None:
|
||||
return needs_xform_mb(ob.parent)
|
||||
|
||||
return False
|
Loading…
Reference in New Issue
Block a user