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.
134 lines
4.2 KiB
Python
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")
|