281 lines
9.7 KiB
Python
281 lines
9.7 KiB
Python
import bpy
|
|
|
|
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)):
|
|
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:
|
|
# 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):
|
|
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.needs_mb = needs_def_mb(self.ob)
|
|
self.time_meshes = []
|
|
|
|
def take_sample(self, render_engine, scene, time):
|
|
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:
|
|
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 '{}' 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]
|
|
|
|
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 '{}' 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)]
|
|
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.needs_mb = needs_xform_mb(self.ob)
|
|
self.time_xforms = []
|
|
|
|
def take_sample(self, render_engine, time, translation_offset):
|
|
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)
|
|
|
|
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")
|