diff --git a/src/assembly.rs b/src/assembly.rs index 62efe32..e07e51f 100644 --- a/src/assembly.rs +++ b/src/assembly.rs @@ -2,8 +2,10 @@ use std::collections::HashMap; use math::Matrix4x4; use bvh::BVH; +use boundable::Boundable; use surface::{Surface, SurfaceIntersection}; use ray::Ray; +use bbox::{BBox, transform_bbox_slice_from}; #[derive(Debug)] @@ -43,6 +45,111 @@ impl Assembly { self.object_map.insert(name.to_string(), self.objects.len()); self.objects.push(obj); } + + pub fn add_assembly(&mut self, name: &str, asmb: Assembly) { + self.assembly_map.insert(name.to_string(), self.assemblies.len()); + self.assemblies.push(asmb); + } + + pub fn add_object_instance(&mut self, name: &str, xforms: Option<&[Matrix4x4]>) { + let instance = Instance { + instance_type: InstanceType::Object, + data_index: self.object_map[name], + id: self.instances.len(), + transform_indices: xforms.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())), + }; + + self.instances.push(instance); + + if let Some(xf) = xforms { + self.xforms.extend(xf); + } + } + + pub fn add_assembly_instance(&mut self, name: &str, xforms: Option<&[Matrix4x4]>) { + let instance = Instance { + instance_type: InstanceType::Assembly, + data_index: self.object_map[name], + id: self.instances.len(), + transform_indices: xforms.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())), + }; + + self.instances.push(instance); + + if let Some(xf) = xforms { + self.xforms.extend(xf); + } + } + + 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(); + + // 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]]); + } + + + + /// Returns a pair of vectors with the bounds of all instances. + /// This is used for building the assembly's BVH. + fn instance_bounds(&self) -> (Vec, Vec) { + let mut indices = vec![0]; + let mut bounds = Vec::new(); + + for inst in self.instances.iter() { + let mut bbs = Vec::new(); + let mut bbs2 = Vec::new(); + + // Get bounding boxes + match inst.instance_type { + InstanceType::Object => { + let obj = &self.objects[inst.data_index]; + // TODO: push bounds onto bbs + match obj { + &Object::Surface(ref s) => bbs.extend(s.bounds()), + } + } + + InstanceType::Assembly => { + let asmb = &self.assemblies[inst.data_index]; + // TODO: push bounds onto bbs + } + } + + // Transform the bounding boxes, if necessary + if let Some((xstart, xend)) = inst.transform_indices { + let xf = &self.xforms[xstart..xend]; + transform_bbox_slice_from(&bbs, &xf, &mut bbs2); + } else { + bbs2.clear(); + bbs2.extend(bbs); + } + + // Push transformed bounds onto vec + bounds.extend(bbs2); + indices.push(bounds.len()); + } + + return (indices, bounds); + } } @@ -53,15 +160,15 @@ pub enum Object { #[derive(Debug, Copy, Clone)] -pub enum Instance { - Object { - data_index: usize, - transform_indices: (usize, usize), - shader_index: usize, - }, - - Assembly { - data_index: usize, - transform_indices: (usize, usize), - }, +pub struct Instance { + pub instance_type: InstanceType, + pub data_index: usize, + pub id: usize, + pub transform_indices: Option<(usize, usize)>, +} + +#[derive(Debug, Copy, Clone)] +pub enum InstanceType { + Object, + Assembly, } diff --git a/src/bbox.rs b/src/bbox.rs index 5a58f5e..85d3213 100644 --- a/src/bbox.rs +++ b/src/bbox.rs @@ -2,9 +2,10 @@ use std; use std::ops::BitOr; +use std::iter::Iterator; -use math::Point; -use lerp::{lerp, Lerp}; +use math::{Point, Matrix4x4}; +use lerp::{lerp, lerp_slice, Lerp}; use ray::Ray; const BBOX_MAXT_ADJUST: f32 = 1.00000024; @@ -53,6 +54,29 @@ impl BBox { // Did we hit? return hitt0.max(0.0) <= hitt1.min(ray.max_t); } + + // Creates a new BBox transformed into a different space. + pub fn transformed(&self, xform: Matrix4x4) -> BBox { + // BBox corners + let vs = [Point::new(self.min[0], self.min[1], self.min[2]), + Point::new(self.min[0], self.min[1], self.max[2]), + Point::new(self.min[0], self.max[1], self.min[2]), + Point::new(self.min[0], self.max[1], self.max[2]), + Point::new(self.max[0], self.min[1], self.min[2]), + Point::new(self.max[0], self.min[1], self.max[2]), + Point::new(self.max[0], self.max[1], self.min[2]), + Point::new(self.max[0], self.max[1], self.max[2])]; + + // Transform BBox corners and make new bbox + let mut b = BBox::new(); + for v in vs.iter() { + let v = *v * xform; + b.min = v.min(b.min); + b.max = v.max(b.max); + } + + return b; + } } @@ -75,3 +99,27 @@ impl Lerp for BBox { } } } + + +pub fn transform_bbox_slice_from(bbs_in: &[BBox], xforms: &[Matrix4x4], bbs_out: &mut Vec) { + bbs_out.clear(); + + // Transform the bounding boxes + if xforms.len() == 0 { + return; + } else if bbs_in.len() == xforms.len() { + for (bb, xf) in Iterator::zip(bbs_in.iter(), xforms.iter()) { + bbs_out.push(bb.transformed(xf.inverse())); + } + } else if bbs_in.len() > xforms.len() { + let s = (bbs_in.len() - 1) as f32; + for (i, bb) in bbs_in.iter().enumerate() { + bbs_out.push(bb.transformed(lerp_slice(xforms, i as f32 / s).inverse())); + } + } else if bbs_in.len() < xforms.len() { + let s = (xforms.len() - 1) as f32; + for (i, xf) in xforms.iter().enumerate() { + bbs_out.push(lerp_slice(bbs_in, i as f32 / s).transformed(xf.inverse())); + } + } +} diff --git a/src/boundable.rs b/src/boundable.rs new file mode 100644 index 0000000..ff30d8e --- /dev/null +++ b/src/boundable.rs @@ -0,0 +1,7 @@ +#![allow(dead_code)] + +use bbox::BBox; + +pub trait Boundable { + fn bounds<'a>(&'a self) -> &'a [BBox]; +} diff --git a/src/bvh.rs b/src/bvh.rs index 18e4668..84d51ad 100644 --- a/src/bvh.rs +++ b/src/bvh.rs @@ -2,6 +2,7 @@ use lerp::lerp_slice; use bbox::BBox; +use boundable::Boundable; use ray::Ray; use algorithm::partition; @@ -217,3 +218,14 @@ impl BVH { } } } + + +impl Boundable for BVH { + fn bounds<'a>(&'a self) -> &'a [BBox] { + match self.nodes[0] { + BVHNode::Internal{bounds_range, ..} => &self.bounds[bounds_range.0..bounds_range.1], + + BVHNode::Leaf{bounds_range, ..} => &self.bounds[bounds_range.0..bounds_range.1], + } + } +} diff --git a/src/camera.rs b/src/camera.rs index a26808b..8156cad 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -66,9 +66,8 @@ impl Camera { // Ray origin let orig = { - let (u, v) = square_to_circle(aperture_radius * ((u * 2.0) - 1.0), - aperture_radius * ((v * 2.0) - 1.0)); - Point::new(u, v, 0.0) + let (u, v) = square_to_circle((u * 2.0) - 1.0, (v * 2.0) - 1.0); + Point::new(aperture_radius * u, aperture_radius * v, 0.0) }; // Ray direction diff --git a/src/main.rs b/src/main.rs index 8911086..c567c5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ mod parse; mod renderer; mod tracer; mod image; +mod boundable; mod triangle; mod surface; mod bvh; @@ -133,6 +134,9 @@ fn main() { 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 scene = Scene { name: None, diff --git a/src/surface/mod.rs b/src/surface/mod.rs index bb0abdc..41672b4 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -6,6 +6,7 @@ pub mod triangle_mesh; use ray::Ray; use math::{Point, Normal, Matrix4x4}; +use boundable::Boundable; #[derive(Debug, Clone)] @@ -21,6 +22,6 @@ pub enum SurfaceIntersection { }, } -pub trait Surface: Debug { +pub trait Surface: Boundable + Debug { fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]); } diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index 7e8c6f2..9878ac0 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -5,6 +5,7 @@ use math::{Point, Normal, Matrix4x4}; use ray::Ray; use triangle; use bbox::BBox; +use boundable::Boundable; use bvh::BVH; use super::{Surface, SurfaceIntersection}; @@ -50,6 +51,12 @@ impl TriangleMesh { } } +impl Boundable for TriangleMesh { + fn bounds<'a>(&'a self) -> &'a [BBox] { + self.accel.bounds() + } +} + impl Surface for TriangleMesh { fn intersect_rays(&self, rays: &mut [Ray], isects: &mut [SurfaceIntersection]) { diff --git a/src/tracer.rs b/src/tracer.rs index 7532a55..84f3fd8 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -3,7 +3,7 @@ use std::slice; use std::cell::UnsafeCell; use math::Matrix4x4; -use assembly::{Assembly, Object}; +use assembly::{Assembly, Object, Instance, InstanceType}; use ray::Ray; use surface::SurfaceIntersection; @@ -56,11 +56,24 @@ impl<'a> Tracer<'a> { } fn trace_assembly<'b>(&'b mut self, assembly: &Assembly, wrays: &[Ray], rays: &mut [Ray]) { - for obj in assembly.objects.iter() { - match obj { - &Object::Surface(ref surface) => { - surface.intersect_rays(rays, &mut self.isects); + assembly.object_accel.traverse(&mut rays[..], &assembly.instances[..], |inst, rs| { + // TODO: transform rays + match inst.instance_type { + InstanceType::Object => { + self.trace_object(&assembly.objects[inst.data_index], wrays, rs); } + + InstanceType::Assembly => { + self.trace_assembly(&assembly.assemblies[inst.data_index], wrays, rs); + } + } + }); + } + + fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [Ray]) { + match obj { + &Object::Surface(ref surface) => { + surface.intersect_rays(rays, &mut self.isects); } } }