psychopath/psychoblend/world.py

125 lines
3.8 KiB
Python

import bpy
from math import degrees, sin, asin, tan, atan
from mathutils import Vector, Matrix
from .util import escape_name, mat2str, ExportCancelled
class World:
def __init__(self, render_engine, depsgraph):
scene = depsgraph.scene
self.background_shader = BackgroundShader(render_engine, scene.world)
def take_sample(self, render_engine, depsgraph, time):
if render_engine.test_break():
raise ExportCancelled()
self.background_shader.take_sample(render_engine, depsgraph, time)
def cleanup(self):
pass
def export(self, render_engine, w):
w.write("World {\n")
w.indent()
self.background_shader.export(render_engine, w)
w.unindent()
w.write("}\n")
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, depsgraph, time):
render_engine.update_stats("", "Psychopath: Collecting '{}' at time {}".format(self.ob.name, time))
# Fov.
# TODO: account for the various ways sensor size can be specified.
x_extent = depsgraph.scene.render.resolution_x / depsgraph.scene.render.pixel_aspect_x
y_extent = depsgraph.scene.render.resolution_y / depsgraph.scene.render.pixel_aspect_y
aspect_ratio = x_extent / y_extent
if aspect_ratio >= 1.0:
self.fovs += [degrees(self.ob.data.angle_x)]
else:
self.fovs += [degrees(2.0 * atan(tan(self.ob.data.angle_x * 0.5) * aspect_ratio))]
if self.ob.data.dof.use_dof:
# Aperture radius.
radius = self.ob.data.lens / 2000.0 / self.ob.data.dof.aperture_fstop
self.aperture_radii += [radius]
# Dof distance
if self.ob.data.dof.focus_object == None:
self.focal_distances += [self.ob.data.dof.focus_distance]
else:
# TODO: implement DoF object tracking here
self.focal_distances += [0.0]
print("WARNING: DoF object tracking not yet implemented.")
else:
self.aperture_radii += [0.0]
self.focal_distances += [1.0]
# Transform
mat = self.ob.matrix_world.copy()
matz = Matrix()
matz[2][2] = -1
self.xforms += [(mat @ matz).inverted()]
def cleanup(self):
pass
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
self.color = []
def take_sample(self, render_engine, depsgraph, time):
if self.world != None:
self.color += [(
self.world.psychopath.background_color[0],
self.world.psychopath.background_color[1],
self.world.psychopath.background_color[2],
)]
def export(self, render_engine, w):
if self.world != None:
w.write("BackgroundShader {\n")
w.indent();
w.write("Type [Color]\n")
for c in self.color:
w.write("Color [rec709, %f %f %f]\n" % c)
w.unindent()
w.write("}\n")