psychopath/psychoblend/objects.py
Nathan Vegdahl 1c801ee605 PsychoBlend: implement object and material export.
Material bindings don't work, since they're now on the objects
themselves rather than the instances, and I haven't updated
Psychopath itself for that yet.
2022-08-07 10:23:54 -07:00

238 lines
9.4 KiB
Python

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':
return Mesh(render_engine, depsgraph, ob, name)
elif ob.type == 'LIGHT':
if ob.data.type == 'POINT':
return SphereLamp(render_engine, depsgraph, ob, name)
elif ob.data.type == 'AREA':
return RectLamp(render_engine, depsgraph, ob, name)
elif ob.data.type == 'AREA':
return RectLamp(render_engine, depsgraph, ob, name)
class Mesh:
""" Holds data for a mesh to be exported.
"""
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 = []
def take_sample(self, render_engine, depsgraph, ob, time):
if len(self.time_meshes) == 0 or self.needs_mb:
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.name, time))
self.time_meshes += [ob.to_mesh(depsgraph=depsgraph).copy()]
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.name)
if self.is_subdiv == 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()
# 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 [")
w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), False)
w.write("]\n", False)
if self.time_meshes[0].polygons[0].use_smooth and self.is_subdiv == False:
w.write("Normals [")
w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.normal]), 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, depsgraph, ob, name):
self.name = name
self.time_col = []
self.time_rad = []
def take_sample(self, render_engine, depsgraph, ob, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(ob.name, time))
if ob.data.psychopath.color_type == 'Rec709':
self.time_col += [('Rec709', ob.data.color * ob.data.energy)]
elif ob.data.psychopath.color_type == 'Blackbody':
self.time_col += [('Blackbody', ob.data.psychopath.color_blackbody_temp, ob.data.energy)]
elif ob.data.psychopath.color_type == 'ColorTemperature':
self.time_col += [('ColorTemperature', ob.data.psychopath.color_blackbody_temp, ob.data.energy)]
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.name)
w.write("SphereLight $%s {\n" % self.name)
w.indent()
for col in self.time_col:
if col[0] == 'Rec709':
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
elif col[0] == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
elif col[0] == 'ColorTemperature':
w.write("Color [color_temperature, %f %f]\n" % (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, depsgraph, ob, name):
self.name = name
self.time_col = []
self.time_dim = []
def take_sample(self, render_engine, depsgraph, ob, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.name, time))
if ob.data.psychopath.color_type == 'Rec709':
self.time_col += [('Rec709', ob.data.color * ob.data.energy)]
elif ob.data.psychopath.color_type == 'Blackbody':
self.time_col += [('Blackbody', ob.data.psychopath.color_blackbody_temp, ob.data.energy)]
elif ob.data.psychopath.color_type == 'ColorTemperature':
self.time_col += [('ColorTemperature', ob.data.psychopath.color_blackbody_temp, ob.data.energy)]
if ob.data.shape == 'RECTANGLE':
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.name)
w.write("RectangleLight $%s {\n" % self.name)
w.indent()
for col in self.time_col:
if col[0] == 'Rec709':
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
elif col[0] == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
elif col[0] == 'ColorTemperature':
w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
for dim in self.time_dim:
w.write("Dimensions [%f %f]\n" % dim)
w.unindent()
w.write("}\n")
class DistantDiskLamp:
def __init__(self, render_engine, depsgraph, ob, name):
self.name = name
self.time_col = []
self.time_dir = []
self.time_rad = []
def take_sample(self, render_engine, depsgraph, ob, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.name, time))
self.time_dir += [tuple(ob.matrix_world.to_3x3() @ Vector((0, 0, -1)))]
if ob.data.psychopath.color_type == 'Rec709':
self.time_col += [('Rec709', ob.data.color * ob.data.energy)]
elif ob.data.psychopath.color_type == 'Blackbody':
self.time_col += [('Blackbody', ob.data.psychopath.color_blackbody_temp, ob.data.energy)]
elif ob.data.psychopath.color_type == 'ColorTemperature':
self.time_col += [('ColorTemperature', ob.data.psychopath.color_blackbody_temp, ob.data.energy)]
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.name)
w.write("DistantDiskLight $%s {\n" % self.name)
w.indent()
for direc in self.time_dir:
w.write("Direction [%f %f %f]\n" % (direc[0], direc[1], direc[2]))
for col in self.time_col:
if col[0] == 'Rec709':
w.write("Color [rec709, %f %f %f]\n" % (col[1][0], col[1][1], col[1][2]))
elif col[0] == 'Blackbody':
w.write("Color [blackbody, %f %f]\n" % (col[1], col[2]))
elif col[0] == 'ColorTemperature':
w.write("Color [color_temperature, %f %f]\n" % (col[1], col[2]))
for rad in self.time_rad:
w.write("Radius [%f]\n" % rad)
w.unindent()
w.write("}\n")
# class Instance:
# def __init__(self, render_engine, depsgraph, ob, data_name):
# 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))
# for ms in self.ob.material_slots:
# if ms != None:
# w.write("SurfaceShaderBind [$%s]\n" % escape_name(ms.material.name))
# break
# w.unindent()
# w.write("}\n")