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.
This commit is contained in:
parent
914a13f899
commit
13ee6066b8
|
@ -1,10 +1,10 @@
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from math import degrees, pi, log
|
from math import log
|
||||||
from mathutils import Vector, Matrix
|
|
||||||
|
|
||||||
from .assembly import Assembly
|
from .assembly import Assembly
|
||||||
from .util import escape_name, mat2str, ExportCancelled
|
from .util import escape_name, mat2str, ExportCancelled
|
||||||
|
from .world import World
|
||||||
|
|
||||||
|
|
||||||
class IndentedWriter:
|
class IndentedWriter:
|
||||||
|
@ -103,113 +103,31 @@ class PsychoExporter:
|
||||||
self.w.unindent()
|
self.w.unindent()
|
||||||
self.w.write("}\n")
|
self.w.write("}\n")
|
||||||
|
|
||||||
#######################
|
###############################
|
||||||
# Camera section begin
|
# Export world and object data
|
||||||
self.w.write("Camera {\n")
|
world = None
|
||||||
self.w.indent()
|
root_assembly = None
|
||||||
|
|
||||||
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:
|
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)
|
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):
|
for i in range(self.time_samples):
|
||||||
time = self.fr + self.shutter_start + (self.shutter_diff*i)
|
time = self.fr + self.shutter_start + (self.shutter_diff*i)
|
||||||
self.set_frame(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)
|
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)
|
root_assembly.export(self.render_engine, self.w)
|
||||||
except ExportCancelled:
|
finally:
|
||||||
root_assembly.cleanup()
|
if world != None:
|
||||||
raise
|
world.cleanup()
|
||||||
else:
|
if root_assembly != None:
|
||||||
root_assembly.cleanup()
|
root_assembly.cleanup()
|
||||||
|
|
||||||
# Scene end
|
# Scene end
|
||||||
self.w.unindent()
|
self.w.unindent()
|
||||||
self.w.write("}\n")
|
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
|
|
||||||
|
|
|
@ -138,8 +138,6 @@ class PsychopathRender(bpy.types.RenderEngine):
|
||||||
return
|
return
|
||||||
self._process.stdin.write(bytes("__PSY_EOF__", "utf-8"))
|
self._process.stdin.write(bytes("__PSY_EOF__", "utf-8"))
|
||||||
self._process.stdin.flush()
|
self._process.stdin.flush()
|
||||||
|
|
||||||
self.update_stats("", "Psychopath: Building")
|
|
||||||
else:
|
else:
|
||||||
# Export to file
|
# Export to file
|
||||||
self.update_stats("", "Psychopath: Exporting data from Blender")
|
self.update_stats("", "Psychopath: Exporting data from Blender")
|
||||||
|
@ -155,6 +153,8 @@ class PsychopathRender(bpy.types.RenderEngine):
|
||||||
self.update_stats("", "Psychopath: Not found")
|
self.update_stats("", "Psychopath: Not found")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.update_stats("", "Psychopath: Building")
|
||||||
|
|
||||||
# If we can, make the render process's stdout non-blocking. The
|
# If we can, make the render process's stdout non-blocking. The
|
||||||
# benefit of this is that canceling the render won't block waiting
|
# benefit of this is that canceling the render won't block waiting
|
||||||
# for the next piece of input.
|
# for the next piece of input.
|
||||||
|
|
150
psychoblend/world.py
Normal file
150
psychoblend/world.py
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from math import degrees, tan, atan
|
||||||
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
|
from .util import escape_name, mat2str, ExportCancelled
|
||||||
|
|
||||||
|
class World:
|
||||||
|
def __init__(self, render_engine, scene, visible_layers, aspect_ratio):
|
||||||
|
self.background_shader = BackgroundShader(render_engine, scene.world)
|
||||||
|
self.camera = Camera(render_engine, scene.camera, aspect_ratio)
|
||||||
|
self.lights = []
|
||||||
|
|
||||||
|
# Collect infinite-extent light sources.
|
||||||
|
# TODO: also get sun lamps inside group instances.
|
||||||
|
for ob in scene.objects:
|
||||||
|
if ob.type == 'LAMP' and ob.data.type == 'SUN':
|
||||||
|
name = escape_name(ob.name)
|
||||||
|
self.lights += [DistantDiskLamp(ob, name)]
|
||||||
|
|
||||||
|
def take_sample(self, render_engine, scene, time):
|
||||||
|
self.camera.take_sample(render_engine, scene, time)
|
||||||
|
|
||||||
|
for light in self.lights:
|
||||||
|
# Check if render is cancelled
|
||||||
|
if render_engine.test_break():
|
||||||
|
raise ExportCancelled()
|
||||||
|
light.take_sample(render_engine, scene, time)
|
||||||
|
|
||||||
|
def export(self, render_engine, w):
|
||||||
|
self.camera.export(render_engine, w)
|
||||||
|
|
||||||
|
w.write("World {\n")
|
||||||
|
w.indent()
|
||||||
|
|
||||||
|
self.background_shader.export(render_engine, w)
|
||||||
|
|
||||||
|
for light in self.lights:
|
||||||
|
light.export(render_engine, w)
|
||||||
|
|
||||||
|
w.unindent()
|
||||||
|
w.write("}\n")
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
# For future use. This is run by the calling code when finished,
|
||||||
|
# even if export did not succeed.
|
||||||
|
pass
|
||||||
|
|
||||||
|
#================================================================
|
||||||
|
|
||||||
|
class Camera:
|
||||||
|
def __init__(self, render_engine, ob, aspect_ratio):
|
||||||
|
self.ob = ob
|
||||||
|
self.aspect_ratio = aspect_ratio
|
||||||
|
|
||||||
|
self.fovs = []
|
||||||
|
self.aperture_radii = []
|
||||||
|
self.focal_distances = []
|
||||||
|
self.xforms = []
|
||||||
|
|
||||||
|
def take_sample(self, render_engine, scene, time):
|
||||||
|
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
|
||||||
|
|
||||||
|
# Fov
|
||||||
|
if self.aspect_ratio >= 1.0:
|
||||||
|
self.fovs += [degrees(self.ob.data.angle)]
|
||||||
|
else:
|
||||||
|
self.fovs += [degrees(2.0 * atan(tan(self.ob.data.angle * 0.5) * self.aspect_ratio))]
|
||||||
|
|
||||||
|
# Aperture radius
|
||||||
|
self.aperture_radii += [self.ob.data.psychopath.aperture_radius]
|
||||||
|
|
||||||
|
# Dof distance
|
||||||
|
if self.ob.data.dof_object == None:
|
||||||
|
self.focal_distances += [self.ob.data.dof_distance]
|
||||||
|
else:
|
||||||
|
# TODO: implement DoF object tracking here
|
||||||
|
self.focal_distances += [0.0]
|
||||||
|
print("WARNING: DoF object tracking not yet implemented.")
|
||||||
|
|
||||||
|
# Transform
|
||||||
|
mat = self.ob.matrix_world.copy()
|
||||||
|
matz = Matrix()
|
||||||
|
matz[2][2] = -1
|
||||||
|
self.xforms += [mat * matz]
|
||||||
|
|
||||||
|
def export(self, render_engine, w):
|
||||||
|
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
|
||||||
|
w.write("Camera {\n")
|
||||||
|
w.indent()
|
||||||
|
|
||||||
|
for fov in self.fovs:
|
||||||
|
w.write("Fov [%f]\n" % fov)
|
||||||
|
|
||||||
|
for rad in self.aperture_radii:
|
||||||
|
w.write("ApertureRadius [%f]\n" % rad)
|
||||||
|
|
||||||
|
for dist in self.focal_distances:
|
||||||
|
w.write("FocalDistance [%f]\n" % dist)
|
||||||
|
|
||||||
|
for mat in self.xforms:
|
||||||
|
w.write("Transform [%s]\n" % mat2str(mat))
|
||||||
|
|
||||||
|
w.unindent()
|
||||||
|
w.write("}\n")
|
||||||
|
|
||||||
|
|
||||||
|
class BackgroundShader:
|
||||||
|
def __init__(self, render_engine, world):
|
||||||
|
self.world = world
|
||||||
|
if self.world != None:
|
||||||
|
self.color = (world.horizon_color[0], world.horizon_color[1], world.horizon_color[2])
|
||||||
|
|
||||||
|
def export(self, render_engine, w):
|
||||||
|
if self.world != None:
|
||||||
|
w.write("BackgroundShader {\n")
|
||||||
|
w.indent();
|
||||||
|
w.write("Type [Color]\n")
|
||||||
|
w.write("Color [%f %f %f]\n" % self.color)
|
||||||
|
w.unindent()
|
||||||
|
w.write("}\n")
|
||||||
|
|
||||||
|
|
||||||
|
class DistantDiskLamp:
|
||||||
|
def __init__(self, ob, name):
|
||||||
|
self.ob = ob
|
||||||
|
self.name = name
|
||||||
|
self.time_col = []
|
||||||
|
self.time_dir = []
|
||||||
|
self.time_rad = []
|
||||||
|
|
||||||
|
def take_sample(self, render_engine, scene, time):
|
||||||
|
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
|
||||||
|
self.time_dir += [tuple(self.ob.matrix_world.to_3x3() * Vector((0, 0, -1)))]
|
||||||
|
self.time_col += [self.ob.data.color * self.ob.data.energy]
|
||||||
|
self.time_rad += [self.ob.data.shadow_soft_size]
|
||||||
|
|
||||||
|
def export(self, render_engine, w):
|
||||||
|
render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.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:
|
||||||
|
w.write("Color [%f %f %f]\n" % (col[0], col[1], col[2]))
|
||||||
|
for rad in self.time_rad:
|
||||||
|
w.write("Radius [%f]\n" % rad)
|
||||||
|
|
||||||
|
w.unindent()
|
||||||
|
w.write("}\n")
|
Loading…
Reference in New Issue
Block a user