diff --git a/psychoblend/objects.py b/psychoblend/objects.py index 713cd6d..b503a78 100644 --- a/psychoblend/objects.py +++ b/psychoblend/objects.py @@ -1,6 +1,7 @@ import bpy from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb, ExportCancelled +from mathutils import Vector, Matrix def make_object_data_cache(render_engine, depsgraph, ob, name): if ob.type == 'MESH': @@ -18,6 +19,9 @@ class Mesh: """ def __init__(self, render_engine, depsgraph, ob, name): self.name = name + self.material_name = None + if len(ob.material_slots) >= 1 and ob.material_slots[0].material != None: + self.material_name = ob.material_slots[0].material.name self.is_subdiv = ob.data.psychopath.is_subdivision_surface self.needs_mb = needs_def_mb(ob) self.time_meshes = [] @@ -43,6 +47,10 @@ class Mesh: w.write("SubdivisionSurface $%s {\n" % self.name) w.indent() + # Material bindings. + if self.material_name != None: + w.write("SurfaceShaderBind [${}]\n".format(self.material_name)) + # Write vertices and (if it's smooth shaded) normals for ti in range(len(self.time_meshes)): w.write("Vertices [") @@ -132,12 +140,12 @@ class RectLamp: self.time_dim += [(ob.data.size, ob.data.size_y)] else: self.time_dim += [(ob.data.size, ob.data.size)] - + def cleanup(self): pass def export(self, render_engine, w): - render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name) + render_engine.update_stats("", "Psychopath: Exporting %s" % self.name) w.write("RectangleLight $%s {\n" % self.name) w.indent() @@ -175,8 +183,11 @@ class DistantDiskLamp: self.time_rad += [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) + render_engine.update_stats("", "Psychopath: Exporting %s" % self.name) w.write("DistantDiskLight $%s {\n" % self.name) w.indent() for direc in self.time_dir: @@ -197,7 +208,6 @@ class DistantDiskLamp: # class Instance: # def __init__(self, render_engine, depsgraph, ob, data_name): -# self.ob = ob # self.data_name = data_name # self.needs_mb = needs_xform_mb(self.ob) # self.time_xforms = [] diff --git a/psychoblend/psy_export.py b/psychoblend/psy_export.py index cf9652b..df888a3 100644 --- a/psychoblend/psy_export.py +++ b/psychoblend/psy_export.py @@ -2,6 +2,7 @@ import bpy from math import log +from .material import Material from .objects import make_object_data_cache, Mesh, DistantDiskLamp from .util import escape_name, mat2str, ExportCancelled from .world import World, Camera @@ -53,6 +54,9 @@ class PsychoExporter: self.sun_lamp_data = {} # name -> cached_data self.sun_lamp_instances = {} # instance_id -> [sun_lamp_data_name, transform_list] + # For all materials. + self.materials = {} # name -> cached_data + # Motion blur segments are rounded down to a power of two. if self.scene.psychopath.motion_blur_segments > 0: self.time_samples = (2**int(log(self.scene.psychopath.motion_blur_segments, 2))) + 1 @@ -124,6 +128,21 @@ class PsychoExporter: self.w.unindent() self.w.write("}\n") + #------------------------------------------------------ + # Collect materials. + + # TODO: handle situations where there are more than one + # material with the same name. This can happen through + # library linking. + + for inst in self.depsgraph.object_instances: + ob = inst.object + if ob.type in ['MESH']: + for ms in ob.material_slots: + if ms.material != None: + if ms.material.name not in self.materials: + self.materials[ms.material.name] = Material(self.render_engine, self.depsgraph, ms.material) + #------------------------------------------------------ # Collect world and object data. @@ -133,7 +152,10 @@ class PsychoExporter: if self.render_engine.test_break(): raise ExportCancelled() - time = self.fr + self.shutter_start + (self.shutter_diff*i) + subframe = self.shutter_start + (self.shutter_diff*i) + time = self.fr + subframe + self.depsgraph.scene.frame_set(self.fr, subframe=subframe) + self.depsgraph.update() # Collect camera and world data. self.camera.take_sample(self.render_engine, self.depsgraph, time) @@ -152,13 +174,22 @@ class PsychoExporter: # We use this a couple of times, so make a shorthand. is_sun_lamp = inst.object.type == 'LIGHT' and inst.object.data.type == 'SUN' + # TODO: handle situations where there are more than one + # object with the same name. This can happen through + # library linking. + # Get a unique id for the instance. This is surprisingly # tricky, because the instance's "persistent_id" property # isn't globally unique, as I would have expected from # the documentation. id = None if inst.is_instance: - id = (hash((inst.object.name, inst.parent.name)), inst.persistent_id) + id = ( + hash((inst.object.name, inst.parent.name)), + # Has to be turned into a tuple, otherwise it doesn't + # work as part of the ID for some reason. + tuple(inst.persistent_id), + ) else: id = inst.object.name @@ -195,6 +226,30 @@ class PsychoExporter: self.w.write("Assembly {\n") self.w.indent() + # Export materials. + for name in self.materials: + self.materials[name].export(self.render_engine, self.w) + + # Export objects. + for name in self.object_data: + self.object_data[name].export(self.render_engine, self.w) + + # Export instances. + for id in self.instances: + [obj_name, xforms] = self.instances[id] + self.render_engine.update_stats("", "Psychopath: Exporting %s instance" % obj_name) + + prefix = str(hex(hash(id))) + name = "inst_{}__{}".format(prefix, obj_name) + + self.w.write("Instance {\n") + self.w.indent() + self.w.write("Data [${}]\n".format(obj_name)) + for mat in xforms: + self.w.write("Transform [{}]\n".format(mat2str(mat))) + self.w.unindent() + self.w.write("}\n") + self.w.unindent() self.w.write("}\n") finally: diff --git a/psychoblend/world.py b/psychoblend/world.py index 17c269b..ead824a 100644 --- a/psychoblend/world.py +++ b/psychoblend/world.py @@ -65,7 +65,7 @@ class Camera: mat = self.ob.matrix_world.copy() matz = Matrix() matz[2][2] = -1 - self.xforms += [(mat * matz).inverted()] + self.xforms += [(mat @ matz).inverted()] def cleanup(self): pass