Compare commits

..

No commits in common. "e244664b322ab328cac7d785b11d7742a8f764cf" and "d132e6a015d39bc250a8f96d16f5574590358ae1" have entirely different histories.

11 changed files with 102 additions and 125 deletions

View File

@ -1,7 +1,6 @@
import bpy import bpy
from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb, ExportCancelled from .util import escape_name, mat2str, needs_def_mb, needs_xform_mb, ExportCancelled
from mathutils import Vector, Matrix
def make_object_data_cache(render_engine, depsgraph, ob, name): def make_object_data_cache(render_engine, depsgraph, ob, name):
if ob.type == 'MESH': if ob.type == 'MESH':
@ -19,9 +18,6 @@ class Mesh:
""" """
def __init__(self, render_engine, depsgraph, ob, name): def __init__(self, render_engine, depsgraph, ob, name):
self.name = name self.name = name
self.material_name = None
if len(ob.material_slots) >= 1 and ob.material_slots[0].material != None:
self.material_name = ob.material_slots[0].material.name
self.is_subdiv = ob.data.psychopath.is_subdivision_surface self.is_subdiv = ob.data.psychopath.is_subdivision_surface
self.needs_mb = needs_def_mb(ob) self.needs_mb = needs_def_mb(ob)
self.time_meshes = [] self.time_meshes = []
@ -40,17 +36,13 @@ class Mesh:
if self.is_subdiv == False: if self.is_subdiv == False:
# Exporting normal mesh # Exporting normal mesh
w.write("MeshSurface $%s {\n" % escape_name(self.name)) w.write("MeshSurface $%s {\n" % self.name)
w.indent() w.indent()
else: else:
# Exporting subdivision surface cage # Exporting subdivision surface cage
w.write("SubdivisionSurface $%s {\n" % escape_name(self.name)) w.write("SubdivisionSurface $%s {\n" % self.name)
w.indent() w.indent()
# Material bindings.
if self.material_name != None:
w.write("SurfaceShaderBind [${}]\n".format(escape_name(self.material_name)))
# Write vertices and (if it's smooth shaded) normals # Write vertices and (if it's smooth shaded) normals
for ti in range(len(self.time_meshes)): for ti in range(len(self.time_meshes)):
w.write("Vertices [") w.write("Vertices [")
@ -102,7 +94,7 @@ class SphereLamp:
def export(self, render_engine, w): def export(self, render_engine, w):
render_engine.update_stats("", "Psychopath: Exporting %s" % self.name) render_engine.update_stats("", "Psychopath: Exporting %s" % self.name)
w.write("SphereLight $%s {\n" % escape_name(self.name)) w.write("SphereLight $%s {\n" % self.name)
w.indent() w.indent()
for col in self.time_col: for col in self.time_col:
if col[0] == 'Rec709': if col[0] == 'Rec709':
@ -145,9 +137,9 @@ class RectLamp:
pass pass
def export(self, render_engine, w): def export(self, render_engine, w):
render_engine.update_stats("", "Psychopath: Exporting %s" % self.name) render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
w.write("RectangleLight $%s {\n" % escape_name(self.name)) w.write("RectangleLight $%s {\n" % self.name)
w.indent() w.indent()
for col in self.time_col: for col in self.time_col:
if col[0] == 'Rec709': if col[0] == 'Rec709':
@ -183,11 +175,8 @@ class DistantDiskLamp:
self.time_rad += [ob.data.shadow_soft_size] self.time_rad += [ob.data.shadow_soft_size]
def cleanup(self):
pass
def export(self, render_engine, w): def export(self, render_engine, w):
render_engine.update_stats("", "Psychopath: Exporting %s" % escape_name(self.name)) render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
w.write("DistantDiskLight $%s {\n" % self.name) w.write("DistantDiskLight $%s {\n" % self.name)
w.indent() w.indent()
for direc in self.time_dir: for direc in self.time_dir:
@ -204,3 +193,35 @@ class DistantDiskLamp:
w.unindent() w.unindent()
w.write("}\n") w.write("}\n")
# class Instance:
# def __init__(self, render_engine, depsgraph, ob, data_name):
# self.ob = ob
# self.data_name = data_name
# self.needs_mb = needs_xform_mb(self.ob)
# self.time_xforms = []
# def take_sample(self, render_engine, time, translation_offset):
# if len(self.time_xforms) == 0 or self.needs_mb:
# render_engine.update_stats("", "Psychopath: Collecting '{}' xforms at time {}".format(self.ob.name, time))
# mat = self.ob.matrix_world.copy()
# mat[0][3] += translation_offset[0]
# mat[1][3] += translation_offset[1]
# mat[2][3] += translation_offset[2]
# self.time_xforms += [mat]
# def export(self, render_engine, w):
# render_engine.update_stats("", "Psychopath: Exporting %s" % self.ob.name)
# w.write("Instance {\n")
# w.indent()
# w.write("Data [$%s]\n" % self.data_name)
# for mat in self.time_xforms:
# w.write("Transform [%s]\n" % mat2str(mat))
# for ms in self.ob.material_slots:
# if ms != None:
# w.write("SurfaceShaderBind [$%s]\n" % escape_name(ms.material.name))
# break
# w.unindent()
# w.write("}\n")

View File

@ -2,7 +2,6 @@ import bpy
from math import log from math import log
from .material import Material
from .objects import make_object_data_cache, Mesh, DistantDiskLamp from .objects import make_object_data_cache, Mesh, DistantDiskLamp
from .util import escape_name, mat2str, ExportCancelled from .util import escape_name, mat2str, ExportCancelled
from .world import World, Camera from .world import World, Camera
@ -54,9 +53,6 @@ class PsychoExporter:
self.sun_lamp_data = {} # name -> cached_data self.sun_lamp_data = {} # name -> cached_data
self.sun_lamp_instances = {} # instance_id -> [sun_lamp_data_name, transform_list] self.sun_lamp_instances = {} # instance_id -> [sun_lamp_data_name, transform_list]
# For all materials.
self.materials = {} # name -> cached_data
# Motion blur segments are rounded down to a power of two. # Motion blur segments are rounded down to a power of two.
if self.scene.psychopath.motion_blur_segments > 0: if self.scene.psychopath.motion_blur_segments > 0:
self.time_samples = (2**int(log(self.scene.psychopath.motion_blur_segments, 2))) + 1 self.time_samples = (2**int(log(self.scene.psychopath.motion_blur_segments, 2))) + 1
@ -128,21 +124,6 @@ class PsychoExporter:
self.w.unindent() self.w.unindent()
self.w.write("}\n") self.w.write("}\n")
#------------------------------------------------------
# Collect materials.
# TODO: handle situations where there are more than one
# material with the same name. This can happen through
# library linking.
for inst in self.depsgraph.object_instances:
ob = inst.object
if ob.type in ['MESH']:
for ms in ob.material_slots:
if ms.material != None:
if ms.material.name not in self.materials:
self.materials[ms.material.name] = Material(self.render_engine, self.depsgraph, ms.material)
#------------------------------------------------------ #------------------------------------------------------
# Collect world and object data. # Collect world and object data.
@ -152,10 +133,7 @@ class PsychoExporter:
if self.render_engine.test_break(): if self.render_engine.test_break():
raise ExportCancelled() raise ExportCancelled()
subframe = self.shutter_start + (self.shutter_diff*i) time = self.fr + self.shutter_start + (self.shutter_diff*i)
time = self.fr + subframe
self.depsgraph.scene.frame_set(self.fr, subframe=subframe)
self.depsgraph.update()
# Collect camera and world data. # Collect camera and world data.
self.camera.take_sample(self.render_engine, self.depsgraph, time) self.camera.take_sample(self.render_engine, self.depsgraph, time)
@ -174,22 +152,13 @@ class PsychoExporter:
# We use this a couple of times, so make a shorthand. # We use this a couple of times, so make a shorthand.
is_sun_lamp = inst.object.type == 'LIGHT' and inst.object.data.type == 'SUN' is_sun_lamp = inst.object.type == 'LIGHT' and inst.object.data.type == 'SUN'
# TODO: handle situations where there are more than one
# object with the same name. This can happen through
# library linking.
# Get a unique id for the instance. This is surprisingly # Get a unique id for the instance. This is surprisingly
# tricky, because the instance's "persistent_id" property # tricky, because the instance's "persistent_id" property
# isn't globally unique, as I would have expected from # isn't globally unique, as I would have expected from
# the documentation. # the documentation.
id = None id = None
if inst.is_instance: if inst.is_instance:
id = ( id = (hash((inst.object.name, inst.parent.name)), inst.persistent_id)
hash((inst.object.name, inst.parent.name)),
# Has to be turned into a tuple, otherwise it doesn't
# work as part of the ID for some reason.
tuple(inst.persistent_id),
)
else: else:
id = inst.object.name id = inst.object.name
@ -226,30 +195,6 @@ class PsychoExporter:
self.w.write("Assembly {\n") self.w.write("Assembly {\n")
self.w.indent() self.w.indent()
# Export materials.
for name in self.materials:
self.materials[name].export(self.render_engine, self.w)
# Export objects.
for name in self.object_data:
self.object_data[name].export(self.render_engine, self.w)
# Export instances.
for id in self.instances:
[obj_name, xforms] = self.instances[id]
self.render_engine.update_stats("", "Psychopath: Exporting %s instance" % obj_name)
prefix = str(hex(hash(id)))
name = "inst_{}__{}".format(prefix, escape_name(obj_name))
self.w.write("Instance {\n")
self.w.indent()
self.w.write("Data [${}]\n".format(escape_name(obj_name)))
for mat in xforms:
self.w.write("Transform [{}]\n".format(mat2str(mat)))
self.w.unindent()
self.w.write("}\n")
self.w.unindent() self.w.unindent()
self.w.write("}\n") self.w.write("}\n")
finally: finally:

View File

@ -65,7 +65,7 @@ class Camera:
mat = self.ob.matrix_world.copy() mat = self.ob.matrix_world.copy()
matz = Matrix() matz = Matrix()
matz[2][2] = -1 matz[2][2] = -1
self.xforms += [(mat @ matz).inverted()] self.xforms += [(mat * matz).inverted()]
def cleanup(self): def cleanup(self):
pass pass

View File

@ -257,7 +257,7 @@ impl<'a> Surface for RectangleLight<'a> {
_local_ray: &LocalRay, _local_ray: &LocalRay,
space: &XformFull, space: &XformFull,
isect: &mut SurfaceIntersection, isect: &mut SurfaceIntersection,
_shaders: &[&dyn SurfaceShader], _shader: &dyn SurfaceShader,
) { ) {
let time = ray.time; let time = ray.time;

View File

@ -207,7 +207,7 @@ impl<'a> Surface for SphereLight<'a> {
local_ray: &LocalRay, local_ray: &LocalRay,
space: &XformFull, space: &XformFull,
isect: &mut SurfaceIntersection, isect: &mut SurfaceIntersection,
_shaders: &[&dyn SurfaceShader], _shader: &dyn SurfaceShader,
) { ) {
let time = ray.time; let time = ray.time;

View File

@ -50,6 +50,23 @@ pub fn parse_assembly<'a>(
child.iter_leaf_children_with_type("Data").nth(0).unwrap().1 child.iter_leaf_children_with_type("Data").nth(0).unwrap().1
}; };
// Get surface shader binding, if any.
let surface_shader_name = if child
.iter_leaf_children_with_type("SurfaceShaderBind")
.count()
> 0
{
Some(
child
.iter_leaf_children_with_type("SurfaceShaderBind")
.nth(0)
.unwrap()
.1,
)
} else {
None
};
// Get xforms // Get xforms
let mut xforms = Vec::new(); let mut xforms = Vec::new();
for (_, contents, _) in child.iter_leaf_children_with_type("Transform") { for (_, contents, _) in child.iter_leaf_children_with_type("Transform") {
@ -58,7 +75,7 @@ pub fn parse_assembly<'a>(
// Add instance // Add instance
if builder.name_exists(name) { if builder.name_exists(name) {
builder.add_instance(name, Some(&xforms)); builder.add_instance(name, surface_shader_name, Some(&xforms));
} else { } else {
return Err(PsyParseError::InstancedMissingData( return Err(PsyParseError::InstancedMissingData(
child.iter_leaf_children_with_type("Data").nth(0).unwrap().2, child.iter_leaf_children_with_type("Data").nth(0).unwrap().2,
@ -96,11 +113,7 @@ pub fn parse_assembly<'a>(
{ {
builder.add_object( builder.add_object(
ident, ident,
Object::Surface(arena.alloc(parse_mesh_surface( Object::Surface(arena.alloc(parse_mesh_surface(arena, child)?)),
arena,
child,
&builder.surface_shader_map,
)?)),
); );
} else { } else {
// TODO: error condition of some kind, because no ident // TODO: error condition of some kind, because no ident

View File

@ -1,6 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
use std::{collections::HashMap, result::Result}; use std::result::Result;
use nom::{sequence::tuple, IResult}; use nom::{sequence::tuple, IResult};
@ -27,9 +27,7 @@ use super::{
pub fn parse_mesh_surface<'a>( pub fn parse_mesh_surface<'a>(
arena: &'a Arena, arena: &'a Arena,
tree: &'a DataTree, tree: &'a DataTree,
surface_shader_map: &HashMap<String, usize>,
) -> Result<TriangleMesh<'a>, PsyParseError> { ) -> Result<TriangleMesh<'a>, PsyParseError> {
let mut shader_idx = None;
let mut verts = Vec::new(); // Vec of vecs, one for each time sample let mut verts = Vec::new(); // Vec of vecs, one for each time sample
let mut normals = Vec::new(); // Vec of vecs, on for each time sample let mut normals = Vec::new(); // Vec of vecs, on for each time sample
let mut face_vert_counts = Vec::new(); let mut face_vert_counts = Vec::new();
@ -38,20 +36,6 @@ pub fn parse_mesh_surface<'a>(
// TODO: make sure there are the right number of various children, // TODO: make sure there are the right number of various children,
// and other validation. // and other validation.
// Get surface shader binding, if any.
if tree
.iter_leaf_children_with_type("SurfaceShaderBind")
.count()
> 0
{
let name = tree
.iter_leaf_children_with_type("SurfaceShaderBind")
.nth(0)
.unwrap()
.1;
shader_idx = surface_shader_map.get(name).map(|i| *i);
}
// Get verts // Get verts
for (_, mut text, _) in tree.iter_leaf_children_with_type("Vertices") { for (_, mut text, _) in tree.iter_leaf_children_with_type("Vertices") {
// Collect verts for this time sample // Collect verts for this time sample
@ -132,7 +116,6 @@ pub fn parse_mesh_surface<'a>(
Ok(TriangleMesh::from_verts_and_indices( Ok(TriangleMesh::from_verts_and_indices(
arena, arena,
shader_idx,
&verts, &verts,
&if normals.is_empty() { &if normals.is_empty() {
None None

View File

@ -142,7 +142,7 @@ pub struct AssemblyBuilder<'a> {
// Shader list // Shader list
surface_shaders: Vec<&'a dyn SurfaceShader>, surface_shaders: Vec<&'a dyn SurfaceShader>,
pub surface_shader_map: HashMap<String, usize>, // map Name -> Index surface_shader_map: HashMap<String, usize>, // map Name -> Index
// Object list // Object list
objects: Vec<Object<'a>>, objects: Vec<Object<'a>>,
@ -206,7 +206,12 @@ impl<'a> AssemblyBuilder<'a> {
self.assemblies.push(asmb); self.assemblies.push(asmb);
} }
pub fn add_instance(&mut self, name: &str, xforms: Option<&[Xform]>) { pub fn add_instance(
&mut self,
name: &str,
surface_shader_name: Option<&str>,
xforms: Option<&[Xform]>,
) {
// Make sure name exists // Make sure name exists
if !self.name_exists(name) { if !self.name_exists(name) {
panic!("Attempted to add instance with a name that doesn't exist."); panic!("Attempted to add instance with a name that doesn't exist.");
@ -228,6 +233,12 @@ impl<'a> AssemblyBuilder<'a> {
Instance { Instance {
instance_type: InstanceType::Object, instance_type: InstanceType::Object,
data_index: self.object_map[name], data_index: self.object_map[name],
surface_shader_index: surface_shader_name.map(|name| {
*self
.surface_shader_map
.get(name)
.unwrap_or_else(|| panic!("Unknown surface shader '{}'.", name))
}),
id: self.instances.len(), id: self.instances.len(),
transform_indices: xforms transform_indices: xforms
.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())), .map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())),
@ -236,6 +247,12 @@ impl<'a> AssemblyBuilder<'a> {
Instance { Instance {
instance_type: InstanceType::Assembly, instance_type: InstanceType::Assembly,
data_index: self.assembly_map[name], data_index: self.assembly_map[name],
surface_shader_index: surface_shader_name.map(|name| {
*self
.surface_shader_map
.get(name)
.unwrap_or_else(|| panic!("Unknown surface shader '{}'.", name))
}),
id: self.instances.len(), id: self.instances.len(),
transform_indices: xforms transform_indices: xforms
.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())), .map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())),
@ -374,6 +391,7 @@ pub enum Object<'a> {
pub struct Instance { pub struct Instance {
pub instance_type: InstanceType, pub instance_type: InstanceType,
pub data_index: usize, pub data_index: usize,
pub surface_shader_index: Option<usize>,
pub id: usize, pub id: usize,
pub transform_indices: Option<(usize, usize)>, pub transform_indices: Option<(usize, usize)>,
} }

View File

@ -24,7 +24,7 @@ pub trait Surface: Boundable + Debug + Sync {
local_ray: &LocalRay, local_ray: &LocalRay,
space: &XformFull, space: &XformFull,
isect: &mut SurfaceIntersection, isect: &mut SurfaceIntersection,
shaders: &[&dyn SurfaceShader], shader: &dyn SurfaceShader,
); );
} }

View File

@ -6,11 +6,10 @@ use crate::{
accel::BVH4, accel::BVH4,
bbox::BBox, bbox::BBox,
boundable::Boundable, boundable::Boundable,
color::Color,
lerp::lerp_slice, lerp::lerp_slice,
math::{cross, dot, Normal, Point, XformFull}, math::{cross, dot, Normal, Point, XformFull},
ray::{LocalRay, Ray}, ray::{LocalRay, Ray},
shading::{SimpleSurfaceShader, SurfaceShader}, shading::SurfaceShader,
}; };
use super::{triangle, Surface, SurfaceIntersection, SurfaceIntersectionData}; use super::{triangle, Surface, SurfaceIntersection, SurfaceIntersectionData};
@ -19,7 +18,6 @@ const MAX_LEAF_TRIANGLE_COUNT: usize = 3;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct TriangleMesh<'a> { pub struct TriangleMesh<'a> {
pub shader_idx: Option<usize>,
time_sample_count: usize, time_sample_count: usize,
vertices: &'a [Point], // Vertices, with the time samples for each vertex stored contiguously vertices: &'a [Point], // Vertices, with the time samples for each vertex stored contiguously
normals: Option<&'a [Normal]>, // Vertex normals, organized the same as `vertices` normals: Option<&'a [Normal]>, // Vertex normals, organized the same as `vertices`
@ -30,7 +28,6 @@ pub struct TriangleMesh<'a> {
impl<'a> TriangleMesh<'a> { impl<'a> TriangleMesh<'a> {
pub fn from_verts_and_indices<'b>( pub fn from_verts_and_indices<'b>(
arena: &'b Arena, arena: &'b Arena,
shader_idx: Option<usize>,
verts: &[Vec<Point>], verts: &[Vec<Point>],
vert_normals: &Option<Vec<Vec<Normal>>>, vert_normals: &Option<Vec<Vec<Normal>>>,
tri_indices: &[(usize, usize, usize)], tri_indices: &[(usize, usize, usize)],
@ -109,7 +106,6 @@ impl<'a> TriangleMesh<'a> {
}); });
TriangleMesh { TriangleMesh {
shader_idx: shader_idx,
time_sample_count: time_sample_count, time_sample_count: time_sample_count,
vertices: vertices, vertices: vertices,
normals: normals, normals: normals,
@ -132,18 +128,8 @@ impl<'a> Surface for TriangleMesh<'a> {
local_ray: &LocalRay, local_ray: &LocalRay,
space: &XformFull, space: &XformFull,
isect: &mut SurfaceIntersection, isect: &mut SurfaceIntersection,
shaders: &[&dyn SurfaceShader], shader: &dyn SurfaceShader,
) { ) {
let unassigned_shader = SimpleSurfaceShader::Emit {
color: Color::new_xyz(color::rec709_to_xyz((1.0, 0.0, 1.0))),
};
let shader = if let Some(idx) = self.shader_idx {
shaders[idx]
} else {
&unassigned_shader
};
self.accel.traverse(ray, local_ray, |idx_range, ray| { self.accel.traverse(ray, local_ray, |idx_range, ray| {
// Iterate through the triangles and test the ray against them. // Iterate through the triangles and test the ray against them.
let mut non_shadow_hit = false; let mut non_shadow_hit = false;

View File

@ -73,11 +73,12 @@ impl<'a> Tracer<'a> {
InstanceType::Object => { InstanceType::Object => {
self.trace_object( self.trace_object(
&assembly.objects[inst.data_index], &assembly.objects[inst.data_index],
inst.surface_shader_index
.map(|i| assembly.surface_shaders[i]),
ray, ray,
&local_ray, &local_ray,
&local_space, &local_space,
isect, isect,
assembly.surface_shaders,
); );
} }
@ -102,19 +103,29 @@ impl<'a> Tracer<'a> {
fn trace_object<'b>( fn trace_object<'b>(
&mut self, &mut self,
obj: &Object, obj: &Object,
surface_shader: Option<&dyn SurfaceShader>,
ray: &mut Ray, ray: &mut Ray,
local_ray: &LocalRay, local_ray: &LocalRay,
space: &XformFull, space: &XformFull,
isect: &mut SurfaceIntersection, isect: &mut SurfaceIntersection,
shaders: &[&dyn SurfaceShader],
) { ) {
match *obj { match *obj {
Object::Surface(surface) => { Object::Surface(surface) => {
surface.intersect_ray(ray, local_ray, space, isect, shaders); let unassigned_shader = SimpleSurfaceShader::Emit {
color: Color::new_xyz(color::rec709_to_xyz((1.0, 0.0, 1.0))),
};
let shader = surface_shader.unwrap_or(&unassigned_shader);
surface.intersect_ray(ray, local_ray, space, isect, shader);
} }
Object::SurfaceLight(surface) => { Object::SurfaceLight(surface) => {
surface.intersect_ray(ray, local_ray, space, isect, shaders); // Lights don't use shaders
let bogus_shader = SimpleSurfaceShader::Emit {
color: Color::new_xyz(color::rec709_to_xyz((1.0, 0.0, 1.0))),
};
surface.intersect_ray(ray, local_ray, space, isect, &bogus_shader);
} }
} }
} }