From 4988a6d1e6e3faa60982b46f7cca908255c920d1 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Sat, 23 Apr 2016 20:14:10 -0700 Subject: [PATCH] Added AssemblyBuilder and got instancing working (sans testing...). The AssemblyBuilder is responsible for collecting the data needed to actually create an Assembly. AssemblyBuilders are now the only way to create an Assembly, which guarantees that Assemblies aren't half-baked. Also got instancing working with transforms and such. It may not be _really_ working because I don't have a complex test case for it yet. But that will come later. --- src/assembly.rs | 66 +++++++++++++++++++++++++++++----------------- src/main.rs | 12 ++++----- src/math/matrix.rs | 28 +++++++++++++++++++- src/math/mod.rs | 2 +- src/tracer.rs | 66 +++++++++++++++++++++++++++++++++++++++++++--- todo.txt | 2 -- 6 files changed, 139 insertions(+), 37 deletions(-) diff --git a/src/assembly.rs b/src/assembly.rs index e07e51f..fa56f81 100644 --- a/src/assembly.rs +++ b/src/assembly.rs @@ -16,31 +16,49 @@ pub struct Assembly { // Object list pub objects: Vec, - object_map: HashMap, // map Name -> Index // Assembly list pub assemblies: Vec, - assembly_map: HashMap, // map Name -> Index // Object accel pub object_accel: BVH, } -impl Assembly { - pub fn new() -> Assembly { - Assembly { +impl Boundable for Assembly { + fn bounds<'a>(&'a self) -> &'a [BBox] { + self.object_accel.bounds() + } +} + + +#[derive(Debug)] +pub struct AssemblyBuilder { + // Instance list + instances: Vec, + xforms: Vec, + + // Object list + objects: Vec, + object_map: HashMap, // map Name -> Index + + // Assembly list + assemblies: Vec, + assembly_map: HashMap, // map Name -> Index +} + + +impl AssemblyBuilder { + pub fn new() -> AssemblyBuilder { + AssemblyBuilder { instances: Vec::new(), xforms: Vec::new(), objects: Vec::new(), object_map: HashMap::new(), assemblies: Vec::new(), assembly_map: HashMap::new(), - object_accel: BVH::new_empty(), } } - - pub fn add_object(&mut self, name: &str, obj: Object) { self.object_map.insert(name.to_string(), self.objects.len()); self.objects.push(obj); @@ -81,31 +99,29 @@ impl Assembly { } } - pub fn finalize(&mut self) { - // Clear maps (no longer needed). - // However, don't clear shader maps, as they are still used by - // get_surface_shader() et al. - self.object_map.clear(); - self.assembly_map.clear(); - + pub fn build(mut self) -> Assembly { // Shrink storage to minimum. // However, don't shrink shader storage, because there are pointers to // that data that could get invalidated. self.instances.shrink_to_fit(); self.xforms.shrink_to_fit(); self.objects.shrink_to_fit(); - self.object_map.shrink_to_fit(); self.assemblies.shrink_to_fit(); - self.assembly_map.shrink_to_fit(); // Build object accel let (bis, bbs) = self.instance_bounds(); - println!("Len: {}, {}", bis.len(), bbs.len()); - self.object_accel = BVH::from_objects(&mut self.instances[..], - 1, - |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); - } + let object_accel = BVH::from_objects(&mut self.instances[..], + 1, + |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); + Assembly { + instances: self.instances, + xforms: self.xforms, + objects: self.objects, + assemblies: self.assemblies, + object_accel: object_accel, + } + } /// Returns a pair of vectors with the bounds of all instances. @@ -121,16 +137,17 @@ impl Assembly { // Get bounding boxes match inst.instance_type { InstanceType::Object => { + // Push bounds onto bbs let obj = &self.objects[inst.data_index]; - // TODO: push bounds onto bbs match obj { &Object::Surface(ref s) => bbs.extend(s.bounds()), } } InstanceType::Assembly => { + // Push bounds onto bbs let asmb = &self.assemblies[inst.data_index]; - // TODO: push bounds onto bbs + bbs.extend(asmb.bounds()); } } @@ -153,6 +170,7 @@ impl Assembly { } + #[derive(Debug)] pub enum Object { Surface(Box), diff --git a/src/main.rs b/src/main.rs index c567c5a..a7ca3a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ use math::{Point, Matrix4x4}; use ray::Ray; use camera::Camera; use scene::Scene; -use assembly::{Assembly, Object}; +use assembly::{AssemblyBuilder, Object}; use renderer::Renderer; use surface::triangle_mesh::TriangleMesh; use parse::DataTree; @@ -132,11 +132,11 @@ fn main() { vec![20.0], vec![1026.0]); - let mut assembly = Assembly::new(); - assembly.add_object("yar", Object::Surface(Box::new(mesh))); - assembly.add_object_instance("yar", - Some(&[Matrix4x4::from_location(Point::new(0.0, 0.0, 0.0))])); - assembly.finalize(); + let mut assembly_b = AssemblyBuilder::new(); + assembly_b.add_object("yar", Object::Surface(Box::new(mesh))); + assembly_b.add_object_instance("yar", + Some(&[Matrix4x4::from_location(Point::new(25.0, 0.0, 0.0))])); + let assembly = assembly_b.build(); let scene = Scene { name: None, diff --git a/src/math/matrix.rs b/src/math/matrix.rs index 58ea061..79e84aa 100644 --- a/src/math/matrix.rs +++ b/src/math/matrix.rs @@ -4,7 +4,7 @@ use std; use std::ops::{Index, IndexMut, Mul}; use float4::Float4; -use lerp::Lerp; +use lerp::{Lerp, lerp_slice}; use super::Point; @@ -248,6 +248,32 @@ impl Lerp for Matrix4x4 { } +pub fn multiply_matrix_slices(xforms1: &[Matrix4x4], + xforms2: &[Matrix4x4], + xforms_out: &mut Vec) { + xforms_out.clear(); + + // Transform the bounding boxes + if xforms1.len() == 0 || xforms2.len() == 0 { + return; + } else if xforms1.len() == xforms2.len() { + for (xf1, xf2) in Iterator::zip(xforms1.iter(), xforms2.iter()) { + xforms_out.push(*xf1 * *xf2); + } + } else if xforms1.len() > xforms2.len() { + let s = (xforms1.len() - 1) as f32; + for (i, xf) in xforms1.iter().enumerate() { + xforms_out.push(*xf * lerp_slice(xforms2, i as f32 / s)); + } + } else if xforms1.len() < xforms2.len() { + let s = (xforms2.len() - 1) as f32; + for (i, xf) in xforms2.iter().enumerate() { + xforms_out.push(lerp_slice(xforms1, i as f32 / s) * *xf); + } + } +} + + #[cfg(test)] mod tests { diff --git a/src/math/mod.rs b/src/math/mod.rs index 40671a3..3b5e154 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -8,7 +8,7 @@ mod matrix; pub use self::vector::Vector; pub use self::normal::Normal; pub use self::point::Point; -pub use self::matrix::Matrix4x4; +pub use self::matrix::{Matrix4x4, multiply_matrix_slices}; /// Trait for calculating dot products. pub trait DotProduct diff --git a/src/tracer.rs b/src/tracer.rs index 84f3fd8..ad5ae7c 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -2,7 +2,8 @@ use std::iter; use std::slice; use std::cell::UnsafeCell; -use math::Matrix4x4; +use math::{Matrix4x4, multiply_matrix_slices}; +use lerp::lerp_slice; use assembly::{Assembly, Object, Instance, InstanceType}; use ray::Ray; use surface::SurfaceIntersection; @@ -11,6 +12,7 @@ pub struct Tracer<'a> { root: &'a Assembly, rays: UnsafeCell>, // Should only be used from trace(), not any other methods xform_stack: Vec, + xform_stack_indices: Vec, isects: Vec, } @@ -18,8 +20,9 @@ impl<'a> Tracer<'a> { pub fn from_assembly(assembly: &'a Assembly) -> Tracer<'a> { Tracer { root: assembly, - xform_stack: Vec::new(), rays: UnsafeCell::new(Vec::new()), + xform_stack: Vec::new(), + xform_stack_indices: vec![0], isects: Vec::new(), } } @@ -57,7 +60,35 @@ impl<'a> Tracer<'a> { fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], rays: &mut [Ray]) { assembly.object_accel.traverse(&mut rays[..], &assembly.instances[..], |inst, rs| { - // TODO: transform rays + // Transform rays if needed + if let Some((xstart, xend)) = inst.transform_indices { + // Push transforms to stack + let mut combined = Vec::new(); + if self.xform_stack.len() == 0 { + self.xform_stack.extend(&assembly.xforms[xstart..xend]); + } else { + let x2start = self.xform_stack_indices[self.xform_stack_indices.len() - 2]; + let x2end = self.xform_stack_indices[self.xform_stack_indices.len() - 1]; + multiply_matrix_slices(&self.xform_stack[x2start..x2end], + &assembly.xforms[xstart..xend], + &mut combined); + self.xform_stack.extend(&combined); + } + self.xform_stack_indices.push(self.xform_stack.len()); + + // Do transforms + let xstart = self.xform_stack_indices[self.xform_stack_indices.len() - 2]; + let xend = self.xform_stack_indices[self.xform_stack_indices.len() - 1]; + let xforms = &self.xform_stack[xstart..xend]; + for ray in &mut rs[..] { + let id = ray.id; + let t = ray.time; + *ray = wrays[id as usize]; + ray.transform(&lerp_slice(xforms, t)); + } + } + + // Trace rays match inst.instance_type { InstanceType::Object => { self.trace_object(&assembly.objects[inst.data_index], wrays, rs); @@ -67,6 +98,35 @@ impl<'a> Tracer<'a> { self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, rs); } } + + // Un-transform rays if needed + if let Some(_) = inst.transform_indices { + // Pop transforms off stack + let xstart = self.xform_stack_indices[self.xform_stack_indices.len() - 2]; + let xend = self.xform_stack_indices[self.xform_stack_indices.len() - 1]; + let l = self.xform_stack.len(); + self.xform_stack.resize(l - (xend - xstart), Matrix4x4::new()); + self.xform_stack_indices.pop(); + + // Undo transforms + if self.xform_stack.len() > 0 { + let xstart = self.xform_stack_indices[self.xform_stack_indices.len() - 2]; + let xend = self.xform_stack_indices[self.xform_stack_indices.len() - 1]; + let xforms = &self.xform_stack[xstart..xend]; + for ray in &mut rs[..] { + let id = ray.id; + let t = ray.time; + *ray = wrays[id as usize]; + ray.transform(&lerp_slice(xforms, t)); + } + } else { + for ray in &mut rs[..] { + let id = ray.id; + let t = ray.time; + *ray = wrays[id as usize]; + } + } + } }); } diff --git a/todo.txt b/todo.txt index 3d9653a..f547868 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,2 @@ -- Implement a Tracer to handle ray tracing responsibilities, with hierarchical - instancing, etc. - Basic scene parsing with triangle meshes. - Unit tests for scene parsing. \ No newline at end of file