diff --git a/src/accel/bvh.rs b/src/accel/bvh.rs index 8a57592..c8c3ab5 100644 --- a/src/accel/bvh.rs +++ b/src/accel/bvh.rs @@ -128,9 +128,11 @@ impl<'a> BVH<'a> { -> &'a mut BVHNode<'a> { match &base.nodes[node_index] { &BVHBaseNode::Internal { bounds_range, children_indices, split_axis } => { - let mut node = unsafe { arena.alloc_uninitialized::() }; + let mut node = unsafe { arena.alloc_uninitialized_with_alignment::(32) }; - let bounds = arena.copy_slice(&base.bounds[bounds_range.0..bounds_range.1]); + let bounds = + arena.copy_slice_with_alignment(&base.bounds[bounds_range.0..bounds_range.1], + 32); let child1 = BVH::construct_from_base(arena, base, children_indices.0); let child2 = BVH::construct_from_base(arena, base, children_indices.1); diff --git a/src/accel/bvh_base.rs b/src/accel/bvh_base.rs index 56ed1dc..16cc720 100644 --- a/src/accel/bvh_base.rs +++ b/src/accel/bvh_base.rs @@ -10,6 +10,11 @@ use super::objects_split::{sah_split, median_split}; pub const BVH_MAX_DEPTH: usize = 42; +// Amount bigger the union of all time samples can be +// and still use the union rather than preserve the +// individual time samples. +const USE_UNION_FACTOR: f32 = 1.4; + /// An intermediary structure for creating a BVH. #[derive(Debug)] pub struct BVHBase { @@ -104,11 +109,24 @@ impl BVHBase { return (0, (0, 0)); } else if objects.len() <= objects_per_leaf { // Leaf node - self.acc_bounds(objects, bounder); let bi = self.bounds.len(); - for b in self.bounds_cache.iter() { - self.bounds.push(*b); + // Get bounds + { + // We make sure that it's worth having multiple time samples, and if not + // we reduce to the union of the time samples. + self.acc_bounds(objects, bounder); + let union_bounds = self.bounds_cache.iter().fold(BBox::new(), |b1, b2| (b1 | *b2)); + let average_area = + self.bounds_cache.iter().fold(0.0, |area, bb| area + bb.surface_area()) / + self.bounds_cache.len() as f32; + if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) { + self.bounds.push(union_bounds); + } else { + self.bounds.extend(&self.bounds_cache); + } } + + // Create node self.nodes.push(BVHBaseNode::Leaf { bounds_range: (bi, self.bounds.len()), object_range: (offset, offset + objects.len()), @@ -155,12 +173,24 @@ impl BVHBase { // Determine bounds // TODO: do merging without the temporary vec. let bi = self.bounds.len(); - let mut merged = Vec::new(); - merge_slices_append(&self.bounds[c1_bounds.0..c1_bounds.1], - &self.bounds[c2_bounds.0..c2_bounds.1], - &mut merged, - |b1, b2| *b1 | *b2); - self.bounds.extend(merged.drain(0..)); + { + let mut merged = Vec::new(); + merge_slices_append(&self.bounds[c1_bounds.0..c1_bounds.1], + &self.bounds[c2_bounds.0..c2_bounds.1], + &mut merged, + |b1, b2| *b1 | *b2); + // We make sure that it's worth having multiple time samples, and if not + // we reduce to the union of the time samples. + let union_bounds = merged.iter().fold(BBox::new(), |b1, b2| (b1 | *b2)); + let average_area = merged.iter().fold(0.0, |area, bb| area + bb.surface_area()) / + merged.len() as f32; + if union_bounds.surface_area() <= (average_area * USE_UNION_FACTOR) { + self.bounds.push(union_bounds); + } else { + self.bounds.extend(merged.drain(0..)); + } + } + // Set node self.nodes[me] = BVHBaseNode::Internal { diff --git a/src/bbox.rs b/src/bbox.rs index ae553a6..9bd8613 100644 --- a/src/bbox.rs +++ b/src/bbox.rs @@ -80,11 +80,8 @@ impl BBox { } pub fn surface_area(&self) -> f32 { - let x = self.max.x() - self.min.x(); - let y = self.max.y() - self.min.y(); - let z = self.max.z() - self.min.z(); - - ((x * y) + (y * z) + (z * x)) * 2.0 + let d = self.max - self.min; + ((d.x() * d.y()) + (d.y() * d.z()) + (d.z() * d.x())) * 2.0 } pub fn center(&self) -> Point { diff --git a/src/scene/assembly.rs b/src/scene/assembly.rs index 5c0a271..1883640 100644 --- a/src/scene/assembly.rs +++ b/src/scene/assembly.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use mem_arena::MemArena; use accel::{LightAccel, LightTree}; -use accel::BVH4; +use accel::BVH; use bbox::{BBox, transform_bbox_slice_from}; use boundable::Boundable; use color::SpectralSample; @@ -28,7 +28,7 @@ pub struct Assembly<'a> { pub assemblies: &'a [Assembly<'a>], // Object accel - pub object_accel: BVH4<'a>, + pub object_accel: BVH<'a>, // Light accel pub light_accel: LightTree<'a>, @@ -231,10 +231,10 @@ impl<'a> AssemblyBuilder<'a> { let (bis, bbs) = self.instance_bounds(); // Build object accel - let object_accel = BVH4::from_objects(self.arena, - &mut self.instances[..], - 1, - |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); + let object_accel = BVH::from_objects(self.arena, + &mut self.instances[..], + 1, + |inst| &bbs[bis[inst.id]..bis[inst.id + 1]]); // Get list of instances that are for light sources or assemblies that contain light // sources. @@ -288,7 +288,7 @@ impl<'a> AssemblyBuilder<'a> { /// Returns a pair of vectors with the bounds of all instances. - /// This is used for building the assembly's BVH4. + /// 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(); diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index c3971dc..728486f 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -2,7 +2,7 @@ use mem_arena::MemArena; -use accel::BVH4; +use accel::BVH; use bbox::BBox; use boundable::Boundable; use color::XYZ; @@ -20,7 +20,7 @@ pub struct TriangleMesh<'a> { time_samples: usize, geo: &'a [(Point, Point, Point)], indices: &'a [usize], - accel: BVH4<'a>, + accel: BVH<'a>, } impl<'a> TriangleMesh<'a> { @@ -44,10 +44,10 @@ impl<'a> TriangleMesh<'a> { bounds }; - let accel = BVH4::from_objects(arena, - &mut indices[..], - 3, - |tri_i| &bounds[*tri_i..(*tri_i + time_samples)]); + let accel = BVH::from_objects(arena, + &mut indices[..], + 3, + |tri_i| &bounds[*tri_i..(*tri_i + time_samples)]); TriangleMesh { time_samples: time_samples,