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