psychopath/psychoblend/psy_export.py
Nathan Vegdahl 13ee6066b8 PsychoBlend: further preformance improvements, and minor fixes.
All scene data collection is now done in a single sweep of frame
changing.  Previous commits were already working towards this, and
but now it's done.  Yay!

Over-all, switching to this approach gives huge speed boosts on
large scenes with animation, rigs, dependencies, etc.  For such
scenes, frame changing is very expensive.
2017-06-10 16:11:28 -07:00

134 lines
4.2 KiB
Python

import bpy
from math import log
from .assembly import Assembly
from .util import escape_name, mat2str, ExportCancelled
from .world import World
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")
###############################
# Export world and object data
world = None
root_assembly = None
try:
# Prep for data collection
world = World(self.render_engine, self.scene, self.scene.layers, float(res_x) / float(res_y))
root_assembly = Assembly(self.render_engine, self.scene.objects, self.scene.layers)
# Collect data for each time sample
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))
world.take_sample(self.render_engine, self.scene, time)
root_assembly.take_sample(self.render_engine, self.scene, time)
# Export collected data
world.export(self.render_engine, self.w)
root_assembly.export(self.render_engine, self.w)
finally:
if world != None:
world.cleanup()
if root_assembly != None:
root_assembly.cleanup()
# Scene end
self.w.unindent()
self.w.write("}\n")