216 lines
7.0 KiB
Python
216 lines
7.0 KiB
Python
import bpy
|
|
|
|
from math import degrees, pi, log
|
|
from mathutils import Vector, Matrix
|
|
|
|
from .assembly import Assembly
|
|
from .util import escape_name, mat2str, ExportCancelled
|
|
|
|
|
|
class IndentedWriter:
|
|
def __init__(self, file_handle):
|
|
self.f = file_handle
|
|
self.indent_level = 0
|
|
self.indent_size = 4
|
|
|
|
def indent(self):
|
|
self.indent_level += self.indent_size
|
|
|
|
def unindent(self):
|
|
self.indent_level -= self.indent_size
|
|
if self.indent_level < 0:
|
|
self.indent_level = 0
|
|
|
|
def write(self, text, do_indent=True):
|
|
if do_indent:
|
|
self.f.write(bytes(' '*self.indent_level + text, "utf-8"))
|
|
else:
|
|
self.f.write(bytes(text, "utf-8"))
|
|
|
|
|
|
class PsychoExporter:
|
|
def __init__(self, f, render_engine, scene):
|
|
self.w = IndentedWriter(f)
|
|
self.render_engine = render_engine
|
|
self.scene = scene
|
|
|
|
self.mesh_names = {}
|
|
self.group_names = {}
|
|
|
|
# Motion blur segments are rounded down to a power of two
|
|
if scene.psychopath.motion_blur_segments > 0:
|
|
self.time_samples = (2**int(log(scene.psychopath.motion_blur_segments, 2))) + 1
|
|
else:
|
|
self.time_samples = 1
|
|
|
|
# pre-calculate useful values for exporting motion blur
|
|
self.shutter_start = scene.psychopath.shutter_start
|
|
self.shutter_diff = (scene.psychopath.shutter_end - scene.psychopath.shutter_start) / max(1, (self.time_samples-1))
|
|
|
|
self.fr = scene.frame_current
|
|
|
|
|
|
def set_frame(self, frame, fraction):
|
|
if fraction >= 0:
|
|
self.scene.frame_set(frame, fraction)
|
|
else:
|
|
self.scene.frame_set(frame-1, 1.0+fraction)
|
|
|
|
def export_psy(self):
|
|
try:
|
|
self._export_psy()
|
|
except ExportCancelled:
|
|
# Cleanup
|
|
self.scene.frame_set(self.fr)
|
|
return False
|
|
else:
|
|
# Cleanup
|
|
self.scene.frame_set(self.fr)
|
|
return True
|
|
|
|
def _export_psy(self):
|
|
# Info
|
|
self.w.write("# Exported from Blender 2.7x\n")
|
|
|
|
# Scene begin
|
|
self.w.write("\n\nScene $%s_fr%d {\n" % (escape_name(self.scene.name), self.fr))
|
|
self.w.indent()
|
|
|
|
#######################
|
|
# Output section begin
|
|
self.w.write("Output {\n")
|
|
self.w.indent()
|
|
|
|
self.w.write('Path [""]\n')
|
|
|
|
# Output section end
|
|
self.w.unindent()
|
|
self.w.write("}\n")
|
|
|
|
###############################
|
|
# RenderSettings section begin
|
|
self.w.write("RenderSettings {\n")
|
|
self.w.indent()
|
|
|
|
res_x = int(self.scene.render.resolution_x * (self.scene.render.resolution_percentage / 100))
|
|
res_y = int(self.scene.render.resolution_y * (self.scene.render.resolution_percentage / 100))
|
|
self.w.write('Resolution [%d %d]\n' % (res_x, res_y))
|
|
self.w.write("SamplesPerPixel [%d]\n" % self.scene.psychopath.spp)
|
|
self.w.write("DicingRate [%f]\n" % self.scene.psychopath.dicing_rate)
|
|
self.w.write('Seed [%d]\n' % self.fr)
|
|
|
|
# RenderSettings section end
|
|
self.w.unindent()
|
|
self.w.write("}\n")
|
|
|
|
#######################
|
|
# Camera section begin
|
|
self.w.write("Camera {\n")
|
|
self.w.indent()
|
|
|
|
cam = self.scene.camera
|
|
|
|
if cam.data.dof_object == None:
|
|
dof_distance = cam.data.dof_distance
|
|
else:
|
|
# TODO: implement DoF object tracking here
|
|
dof_distance = 0.0
|
|
print("WARNING: DoF object tracking not yet implemented.")
|
|
|
|
matz = Matrix()
|
|
matz[2][2] = -1
|
|
for i in range(self.time_samples):
|
|
# Check if render is cancelled
|
|
if self.render_engine.test_break():
|
|
raise ExportCancelled()
|
|
|
|
if res_x >= res_y:
|
|
self.w.write("Fov [%f]\n" % degrees(cam.data.angle))
|
|
else:
|
|
self.w.write("Fov [%f]\n" % (degrees(cam.data.angle) * res_x / res_y))
|
|
self.w.write("FocalDistance [%f]\n" % dof_distance)
|
|
self.w.write("ApertureRadius [%f]\n" % (cam.data.psychopath.aperture_radius))
|
|
if self.time_samples > 1:
|
|
self.set_frame(self.fr, self.shutter_start + (self.shutter_diff*i))
|
|
mat = cam.matrix_world.copy()
|
|
mat = mat * matz
|
|
self.w.write("Transform [%s]\n" % mat2str(mat))
|
|
|
|
# Camera section end
|
|
self.w.unindent()
|
|
self.w.write("}\n")
|
|
|
|
#######################
|
|
# World section begin
|
|
self.w.write("World {\n")
|
|
self.w.indent()
|
|
|
|
world = self.scene.world
|
|
|
|
if world != None:
|
|
self.w.write("BackgroundShader {\n")
|
|
self.w.indent();
|
|
self.w.write("Type [Color]\n")
|
|
self.w.write("Color [%f %f %f]\n" % (world.horizon_color[0], world.horizon_color[1], world.horizon_color[2]))
|
|
self.w.unindent();
|
|
self.w.write("}\n")
|
|
|
|
# Infinite light sources
|
|
for ob in self.scene.objects:
|
|
if ob.type == 'LAMP' and ob.data.type == 'SUN':
|
|
self.export_world_distant_disk_lamp(ob, "")
|
|
|
|
# World section end
|
|
self.w.unindent()
|
|
self.w.write("}\n")
|
|
|
|
#######################
|
|
# Export objects and materials
|
|
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
|
|
else:
|
|
root_assembly.cleanup()
|
|
|
|
# Scene end
|
|
self.w.unindent()
|
|
self.w.write("}\n")
|
|
|
|
def export_world_distant_disk_lamp(self, ob, group_prefix):
|
|
name = group_prefix + "__" + escape_name(ob.name)
|
|
|
|
# Collect data over time
|
|
time_dir = []
|
|
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_dir += [tuple(ob.matrix_world.to_3x3() * Vector((0, 0, -1)))]
|
|
time_col += [ob.data.color * ob.data.energy]
|
|
time_rad += [ob.data.shadow_soft_size]
|
|
|
|
# Write out sphere light
|
|
self.w.write("DistantDiskLight $%s {\n" % name)
|
|
self.w.indent()
|
|
for direc in time_dir:
|
|
self.w.write("Direction [%f %f %f]\n" % (direc[0], direc[1], direc[2]))
|
|
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
|