diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index caaa962..0b5254c 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -30,9 +30,6 @@ pub enum BvhNode { /// The node's parent. parent_index: usize, - /// The node's depth. - depth: u32, - /// The shape contained in this leaf. shape_index: usize, }, @@ -41,9 +38,6 @@ pub enum BvhNode { /// The node's parent. parent_index: usize, - /// The node's depth. - depth: u32, - /// Index of the left subtree's root node. child_l_index: usize, @@ -65,40 +59,31 @@ impl PartialEq for BvhNode { ( &BvhNode::Node { parent_index: self_parent_index, - depth: self_depth, child_l_index: self_child_l_index, child_r_index: self_child_r_index, .. }, &BvhNode::Node { parent_index: other_parent_index, - depth: other_depth, child_l_index: other_child_l_index, child_r_index: other_child_r_index, .. }, ) => { self_parent_index == other_parent_index - && self_depth == other_depth && self_child_l_index == other_child_l_index && self_child_r_index == other_child_r_index } ( &BvhNode::Leaf { parent_index: self_parent_index, - depth: self_depth, shape_index: self_shape_index, }, &BvhNode::Leaf { parent_index: other_parent_index, - depth: other_depth, shape_index: other_shape_index, }, - ) => { - self_parent_index == other_parent_index - && self_depth == other_depth - && self_shape_index == other_shape_index - } + ) => self_parent_index == other_parent_index && self_shape_index == other_shape_index, _ => false, } } @@ -134,6 +119,17 @@ impl BvhNode { } } + /// Returns the index of the left child node. + pub fn child_l_mut(&mut self) -> &mut usize { + match *self { + BvhNode::Node { + ref mut child_l_index, + .. + } => child_l_index, + _ => panic!("Tried to get the left child of a leaf node."), + } + } + /// Returns the `Aabb` of the right child node. pub fn child_l_aabb(&self) -> Aabb { match *self { @@ -161,6 +157,17 @@ impl BvhNode { } } + /// Returns the index of the right child node. + pub fn child_r_mut(&mut self) -> &mut usize { + match *self { + BvhNode::Node { + ref mut child_r_index, + .. + } => child_r_index, + _ => panic!("Tried to get the right child of a leaf node."), + } + } + /// Returns the [`Aabb`] of the right child node. pub fn child_r_aabb(&self) -> Aabb { match *self { @@ -180,13 +187,6 @@ impl BvhNode { } } - /// Returns the depth of the node. The root node has depth `0`. - pub fn depth(&self) -> u32 { - match *self { - BvhNode::Node { depth, .. } | BvhNode::Leaf { depth, .. } => depth, - } - } - /// Gets the [`Aabb`] for a [`BvhNode`]. /// Returns the shape's [`Aabb`] for leaves, and the joined [`Aabb`] of /// the two children's [`Aabb`]'s for non-leaves. @@ -213,12 +213,23 @@ impl BvhNode { } } + /// Returns the index of the shape contained within the node if is a leaf, + /// or `None` if it is an interior node. + pub fn shape_index_mut(&mut self) -> Option<&mut usize> { + match *self { + BvhNode::Leaf { + ref mut shape_index, + .. + } => Some(shape_index), + _ => None, + } + } + /// The build function sometimes needs to add nodes while their data is not available yet. /// A dummy cerated by this function serves the purpose of being changed later on. fn create_dummy() -> BvhNode { BvhNode::Leaf { parent_index: 0, - depth: 0, shape_index: 0, } } @@ -233,7 +244,6 @@ impl BvhNode { indices: &[usize], nodes: &mut Vec>, parent_index: usize, - depth: u32, ) -> usize where T: FromPrimitive + ClosedSub + ClosedAdd + SimdPartialOrd + ClosedMul + Float, @@ -274,7 +284,6 @@ impl BvhNode { let node_index = nodes.len(); nodes.push(BvhNode::Leaf { parent_index, - depth, shape_index, }); // Let the shape know the index of the node that represents it. @@ -302,10 +311,8 @@ impl BvhNode { let child_r_aabb = joint_aabb_of_shapes(child_r_indices, shapes); // Proceed recursively. - let child_l_index = - BvhNode::build(shapes, child_l_indices, nodes, node_index, depth + 1); - let child_r_index = - BvhNode::build(shapes, child_r_indices, nodes, node_index, depth + 1); + let child_l_index = BvhNode::build(shapes, child_l_indices, nodes, node_index); + let child_r_index = BvhNode::build(shapes, child_r_indices, nodes, node_index); (child_l_index, child_l_aabb, child_r_index, child_r_aabb) } else { // Create six `Bucket`s, and six index assignment vector. @@ -363,10 +370,8 @@ impl BvhNode { let child_r_indices = concatenate_vectors(r_assignments); // Proceed recursively. - let child_l_index = - BvhNode::build(shapes, &child_l_indices, nodes, node_index, depth + 1); - let child_r_index = - BvhNode::build(shapes, &child_r_indices, nodes, node_index, depth + 1); + let child_l_index = BvhNode::build(shapes, &child_l_indices, nodes, node_index); + let child_r_index = BvhNode::build(shapes, &child_r_indices, nodes, node_index); (child_l_index, child_l_aabb, child_r_index, child_r_aabb) }; @@ -375,7 +380,6 @@ impl BvhNode { assert!(!child_r_aabb.is_empty()); nodes[node_index] = BvhNode::Node { parent_index, - depth, child_l_aabb, child_l_index, child_r_aabb, @@ -448,7 +452,7 @@ impl Bvh { let indices = (0..shapes.len()).collect::>(); let expected_node_count = shapes.len() * 2; let mut nodes = Vec::with_capacity(expected_node_count); - BvhNode::build(shapes, &indices, &mut nodes, 0, 0); + BvhNode::build(shapes, &indices, &mut nodes, 0); Bvh { nodes } } @@ -500,45 +504,44 @@ impl Bvh { T: std::fmt::Display, { let nodes = &self.nodes; - fn print_node(nodes: &[BvhNode], node_index: usize) - where + fn print_node( + nodes: &[BvhNode], + node_index: usize, + depth: usize, + ) where T: std::fmt::Display, { match nodes[node_index] { BvhNode::Node { child_l_index, child_r_index, - depth, child_l_aabb, child_r_aabb, .. } => { - let padding: String = " ".repeat(depth as usize); + let padding: String = " ".repeat(depth); println!("{}child_l {}", padding, child_l_aabb); - print_node(nodes, child_l_index); + print_node(nodes, child_l_index, depth + 1); println!("{}child_r {}", padding, child_r_aabb); - print_node(nodes, child_r_index); + print_node(nodes, child_r_index, depth + 1); } - BvhNode::Leaf { - shape_index, depth, .. - } => { - let padding: String = " ".repeat(depth as usize); + BvhNode::Leaf { shape_index, .. } => { + let padding: String = " ".repeat(depth); println!("{}shape\t{:?}", padding, shape_index); } } } - print_node(nodes, 0); + print_node(nodes, 0, 0); } /// Verifies that the node at index `node_index` lies inside `expected_outer_aabb`, /// its parent index is equal to `expected_parent_index`, its depth is equal to - /// `expected_depth`. Increares `node_count` by the number of visited nodes. + /// `expected_depth`. Increases `node_count` by the number of visited nodes. fn is_consistent_subtree>( &self, node_index: usize, expected_parent_index: usize, expected_outer_aabb: &Aabb, - expected_depth: u32, node_count: &mut usize, shapes: &[Shape], ) -> bool @@ -549,14 +552,12 @@ impl Bvh { match self.nodes[node_index] { BvhNode::Node { parent_index, - depth, child_l_index, child_l_aabb, child_r_index, child_r_aabb, } => { let correct_parent_index = expected_parent_index == parent_index; - let correct_depth = expected_depth == depth; let left_aabb_in_parent = expected_outer_aabb.approx_contains_aabb_eps(&child_l_aabb, T::epsilon()); let right_aabb_in_parent = @@ -565,7 +566,6 @@ impl Bvh { child_l_index, node_index, &child_l_aabb, - expected_depth + 1, node_count, shapes, ); @@ -573,13 +573,11 @@ impl Bvh { child_r_index, node_index, &child_r_aabb, - expected_depth + 1, node_count, shapes, ); correct_parent_index - && correct_depth && left_aabb_in_parent && right_aabb_in_parent && left_subtree_consistent @@ -587,16 +585,14 @@ impl Bvh { } BvhNode::Leaf { parent_index, - depth, shape_index, } => { let correct_parent_index = expected_parent_index == parent_index; - let correct_depth = expected_depth == depth; let shape_aabb = shapes[shape_index].aabb(); let shape_aabb_in_parent = expected_outer_aabb.approx_contains_aabb_eps(&shape_aabb, T::epsilon()); - correct_parent_index && correct_depth && shape_aabb_in_parent + correct_parent_index && shape_aabb_in_parent } } } @@ -612,8 +608,7 @@ impl Bvh { // The counter for all nodes. let mut node_count = 0; - let subtree_consistent = - self.is_consistent_subtree(0, 0, &space, 0, &mut node_count, shapes); + let subtree_consistent = self.is_consistent_subtree(0, 0, &space, &mut node_count, shapes); // Check if all nodes have been counted from the root node. // If this is false, it means we have a detached subtree. @@ -627,7 +622,6 @@ impl Bvh { node_index: usize, expected_parent_index: usize, expected_outer_aabb: &Aabb, - expected_depth: u32, node_count: &mut usize, shapes: &[Shape], ) where @@ -642,12 +636,6 @@ impl Bvh { "Wrong parent index. Expected: {}; Actual: {}", expected_parent_index, parent ); - let depth = node.depth(); - assert_eq!( - expected_depth, depth, - "Wrong depth. Expected: {}; Actual: {}", - expected_depth, depth - ); match *node { BvhNode::Node { @@ -677,7 +665,6 @@ impl Bvh { child_l_index, node_index, &child_l_aabb, - expected_depth + 1, node_count, shapes, ); @@ -685,7 +672,6 @@ impl Bvh { child_r_index, node_index, &child_r_aabb, - expected_depth + 1, node_count, shapes, ); @@ -712,7 +698,7 @@ impl Bvh { // The counter for all nodes. let mut node_count = 0; - self.assert_consistent_subtree(0, 0, &space, 0, &mut node_count, shapes); + self.assert_consistent_subtree(0, 0, &space, &mut node_count, shapes); // Check if all nodes have been counted from the root node. // If this is false, it means we have a detached subtree. diff --git a/src/bvh/mod.rs b/src/bvh/mod.rs index 214deb8..49a8f0d 100644 --- a/src/bvh/mod.rs +++ b/src/bvh/mod.rs @@ -9,4 +9,3 @@ mod optimization; pub use self::bvh_impl::*; pub use self::iter::*; -pub use self::optimization::*; diff --git a/src/bvh/optimization.rs b/src/bvh/optimization.rs index 0be5604..21651c4 100644 --- a/src/bvh/optimization.rs +++ b/src/bvh/optimization.rs @@ -6,66 +6,18 @@ //! [`Bvh`]: struct.Bvh.html //! -use crate::aabb::Aabb; use crate::bounding_hierarchy::BHShape; use crate::bvh::*; use log::info; -use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; -use num::{FromPrimitive, Zero}; -use rand::{thread_rng, Rng}; -use std::collections::HashSet; +use nalgebra::{ClosedAdd, ClosedDiv, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::{FromPrimitive, Signed, Zero}; // TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an `Aabb` // that is updated from the outside, perhaps by passing not only the indices of the changed -// shapes, but also their new `Aabb`'s into optimize(). +// shapes, but also their new `Aabb`'s into update_shapes(). // TODO Consider: Stop updating `Aabb`'s upwards the tree once an `Aabb` didn't get changed. -#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -enum OptimizationIndex { - Refit(usize), - FixAabbs(usize), -} - -impl OptimizationIndex { - fn index(&self) -> usize { - match *self { - OptimizationIndex::Refit(index) | OptimizationIndex::FixAabbs(index) => index, - } - } -} - -#[derive(Debug, Copy, Clone)] -struct NodeData { - index: usize, - aabb: Aabb, -} - -impl BvhNode { - // Get the grandchildren's NodeData. - fn get_children_node_data(&self) -> Option<(NodeData, NodeData)> { - match *self { - BvhNode::Node { - child_l_index, - child_l_aabb, - child_r_index, - child_r_aabb, - .. - } => Some(( - NodeData { - index: child_l_index, - aabb: child_l_aabb, - }, - NodeData { - index: child_r_index, - aabb: child_r_aabb, - }, - )), - BvhNode::Leaf { .. } => None, - } - } -} - impl Bvh where T: Scalar @@ -74,459 +26,382 @@ where + ClosedSub + ClosedMul + ClosedAdd + + ClosedDiv + Zero + SimdPartialOrd + PartialOrd + + Signed + std::fmt::Display, { - /// Optimizes the [`Bvh`] by batch-reorganizing updated nodes. - /// Based on - /// [`https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs`] - /// - /// Needs all the scene's shapes, plus the indices of the shapes that were updated. - /// - pub fn optimize>( + fn node_is_left_child(&self, node_index: usize) -> bool { + // Get the index of the parent. + let node_parent_index = self.nodes[node_index].parent(); + // Get the index of te left child of the parent. + let child_l_index = self.nodes[node_parent_index].child_l(); + child_l_index == node_index + } + + fn connect_nodes>( &mut self, - refit_shape_indices: &HashSet, + child_index: usize, + parent_index: usize, + left_child: bool, shapes: &[Shape], ) { - // `refit_node_indices` will contain the indices of the leaf nodes - // that reference the given shapes, sorted by their depth - // in increasing order. - let mut refit_node_indices: Vec<_> = { - let mut raw_indices = refit_shape_indices - .iter() - .map(|x| shapes[*x].bh_node_index()) - .collect::>(); - - // Sorts the Vector to have the greatest depth nodes last. - raw_indices.sort_by(|a, b| { - let depth_a = self.nodes[*a].depth(); - let depth_b = self.nodes[*b].depth(); - depth_a.cmp(&depth_b) - }); - - raw_indices - .iter() - .map(|x| OptimizationIndex::Refit(*x)) - .collect() - }; - - // As long as we have refit nodes left, take the list of refit nodes - // with the greatest depth (sweep nodes) and try to rotate them all. - while !refit_node_indices.is_empty() { - let mut sweep_node_indices = Vec::new(); - let max_depth = { - let last_node_index = refit_node_indices.last().unwrap(); - self.nodes[last_node_index.index()].depth() - }; - while !refit_node_indices.is_empty() { - let last_node_depth = { - let last_node_index = refit_node_indices.last().unwrap(); - self.nodes[last_node_index.index()].depth() - }; - if last_node_depth == max_depth { - sweep_node_indices.push(refit_node_indices.pop().unwrap()); + let child_aabb = self.nodes[child_index].get_node_aabb(shapes); + info!("\tConnecting: {} < {}.", child_index, parent_index); + // Set parent's child aabb and index. + match self.nodes[parent_index] { + BvhNode::Node { + ref mut child_l_index, + ref mut child_r_index, + ref mut child_l_aabb, + ref mut child_r_aabb, + .. + } => { + if left_child { + *child_l_index = child_index; + *child_l_aabb = child_aabb; } else { - break; - } - } - - info!( - "{} sweep nodes, depth {}.", - sweep_node_indices.len(), - max_depth - ); - - // Try to find a useful tree rotation with all previously found nodes. - for sweep_node_index in sweep_node_indices { - // TODO There might be multithreading potential here - // In order to have threads working seperately without having to write on - // the nodes vector (which would lock other threads), - // write the results of a thread into a small data structure. - // Apply the changes to the nodes vector represented by the data structure - // in a quick, sequential loop after all threads finished their work. - let new_refit_node_index = match sweep_node_index { - OptimizationIndex::Refit(index) => self.update(index, shapes), - OptimizationIndex::FixAabbs(index) => self.fix_aabbs(index, shapes), - }; - - // Instead of finding a useful tree rotation, we found another node - // that we should check, so we add its index to the refit_node_indices. - if let Some(index) = new_refit_node_index { - assert!({ - let new_node_depth = self.nodes[index.index()].depth(); - new_node_depth == max_depth - 1 - }); - refit_node_indices.push(index); + *child_r_index = child_index; + *child_r_aabb = child_aabb; } + info!("\t {}'s new {}", parent_index, child_aabb); } + // Assuming that our `Bvh` is correct, the parent cannot be a leaf. + _ => unreachable!(), } + + // Set child's parent. + *self.nodes[child_index].parent_mut() = parent_index; } - /// This method is called for each node which has been modified and needs to be updated. - /// If the specified node is a grandparent, then try to optimize the [`Bvh`] by rotating its - /// children. - fn update>( - &mut self, - node_index: usize, - shapes: &[Shape], - ) -> Option { - info!(" [{}]\t", node_index); + /// Adds a shape with the given index to the `BVH` + /// Significantly slower at building a `BVH` than the full build or rebuild option + /// Useful for moving a small subset of nodes around in a large `BVH` + pub fn add_shape>(&mut self, shapes: &mut [Shape], new_shape_index: usize) + where + T: std::ops::Div, + { + let mut node_index = 0; + let new_shape = &shapes[new_shape_index]; + let shape_aabb = new_shape.aabb(); + let shape_sa = shape_aabb.surface_area(); + + if self.nodes.is_empty() { + self.nodes.push(BvhNode::Leaf { + parent_index: 0, + shape_index: new_shape_index, + }); + shapes[new_shape_index].set_bh_node_index(0); + return; + } - match self.nodes[node_index] { - BvhNode::Leaf { - parent_index, - shape_index, - .. - } => { - // The current node is a leaf. - info!( - "Leaf node. Queueing parent ({}). {}.", + loop { + match self.nodes[node_index] { + BvhNode::Node { + child_l_aabb, + child_l_index, + child_r_aabb, + child_r_index, parent_index, - shapes[shape_index].aabb() - ); - Some(OptimizationIndex::Refit(parent_index)) - } - BvhNode::Node { - parent_index, - child_l_index, - child_r_index, - .. - } => { - // The current node is a parent. - if let ( - &BvhNode::Leaf { - shape_index: shape_l_index, - .. - }, - &BvhNode::Leaf { - shape_index: shape_r_index, - .. - }, - ) = (&self.nodes[child_l_index], &self.nodes[child_r_index]) - { - // The current node is a final parent. Update its `Aabb`s, because at least - // one of its children was updated and queue its parent for refitting. - if let BvhNode::Node { - ref mut child_l_aabb, - ref mut child_r_aabb, - .. - } = self.nodes[node_index] - { - *child_l_aabb = shapes[shape_l_index].aabb(); - *child_r_aabb = shapes[shape_r_index].aabb(); - info!("Setting {} from {}", child_l_aabb, child_l_index); - info!("\tand {} from {}.", child_r_aabb, child_r_index); - return Some(OptimizationIndex::Refit(parent_index)); - } - unreachable!(); - } + } => { + let left_expand = child_l_aabb.join(&shape_aabb); - // The current node is a grandparent and can be optimized by rotating. - self.try_rotate(node_index, shapes) - } - } - } + let right_expand = child_r_aabb.join(&shape_aabb); - fn find_better_rotation( - &self, - child_l_index: usize, - child_l_aabb: &Aabb, - child_r_index: usize, - child_r_aabb: &Aabb, - ) -> Option<(usize, usize)> { - // Get indices and `Aabb`s of all grandchildren. - let left_children_nodes = self.nodes[child_l_index].get_children_node_data(); - let right_children_nodes = self.nodes[child_r_index].get_children_node_data(); - - // Contains the surface area that would result from applying the currently favored rotation. - // The rotation with the smallest surface area will be applied in the end. - // The value is calculated by `child_l_aabb.surface_area() + child_r_aabb.surface_area()`. - let mut best_surface_area = child_l_aabb.surface_area() + child_r_aabb.surface_area(); - - // Stores the rotation that would result in the surface area `best_surface_area`, - // thus being the favored rotation that will be executed after considering all rotations. - let mut best_rotation: Option<(usize, usize)> = None; - { - let mut consider_rotation = |new_rotation: (usize, usize), surface_area: T| { - if surface_area < best_surface_area { - best_surface_area = surface_area; - best_rotation = Some(new_rotation); - } - }; + let send_left = child_r_aabb.surface_area() + left_expand.surface_area(); + let send_right = child_l_aabb.surface_area() + right_expand.surface_area(); + let merged_aabb = child_r_aabb.join(&child_l_aabb); + let merged = merged_aabb.surface_area() + shape_sa; - // Child to grandchild rotations - if let Some((child_rl, child_rr)) = right_children_nodes { - let surface_area_l_rl = - child_rl.aabb.surface_area() + child_l_aabb.join(&child_rr.aabb).surface_area(); - consider_rotation((child_l_index, child_rl.index), surface_area_l_rl); - let surface_area_l_rr = - child_rr.aabb.surface_area() + child_l_aabb.join(&child_rl.aabb).surface_area(); - consider_rotation((child_l_index, child_rr.index), surface_area_l_rr); - } - if let Some((child_ll, child_lr)) = left_children_nodes { - let surface_area_r_ll = - child_ll.aabb.surface_area() + child_r_aabb.join(&child_lr.aabb).surface_area(); - consider_rotation((child_r_index, child_ll.index), surface_area_r_ll); - let surface_area_r_lr = - child_lr.aabb.surface_area() + child_r_aabb.join(&child_ll.aabb).surface_area(); - consider_rotation((child_r_index, child_lr.index), surface_area_r_lr); - - // Grandchild to grandchild rotations - if let Some((child_rl, child_rr)) = right_children_nodes { - let surface_area_ll_rl = child_rl.aabb.join(&child_lr.aabb).surface_area() - + child_ll.aabb.join(&child_rr.aabb).surface_area(); - consider_rotation((child_ll.index, child_rl.index), surface_area_ll_rl); - let surface_area_ll_rr = child_ll.aabb.join(&child_rl.aabb).surface_area() - + child_lr.aabb.join(&child_rr.aabb).surface_area(); - consider_rotation((child_ll.index, child_rr.index), surface_area_ll_rr); + // compared SA of the options + let min_send = if send_left < send_right { + send_left + } else { + send_right + }; + // merge is more expensive only do when it's significantly better + + if merged < min_send * T::from_i8(3).unwrap() / T::from_i8(10).unwrap() { + // Merge left and right trees + let l_index = self.nodes.len(); + let new_left = BvhNode::Leaf { + parent_index: node_index, + shape_index: new_shape_index, + }; + shapes[new_shape_index].set_bh_node_index(l_index); + self.nodes.push(new_left); + + let r_index = self.nodes.len(); + let new_right = BvhNode::Node { + child_l_aabb, + child_l_index, + child_r_aabb, + child_r_index, + parent_index: node_index, + }; + self.nodes.push(new_right); + *self.nodes[child_r_index].parent_mut() = r_index; + *self.nodes[child_l_index].parent_mut() = r_index; + + self.nodes[node_index] = BvhNode::Node { + child_l_aabb: shape_aabb, + child_l_index: l_index, + child_r_aabb: merged_aabb, + child_r_index: r_index, + parent_index, + }; + return; + } else if send_left < send_right { + // Send new box down left side + if node_index == child_l_index { + panic!("broken loop"); + } + let child_l_aabb = left_expand; + self.nodes[node_index] = BvhNode::Node { + child_l_aabb, + child_l_index, + child_r_aabb, + child_r_index, + parent_index, + }; + node_index = child_l_index; + } else { + // Send new box down right + if node_index == child_r_index { + panic!("broken loop"); + } + let child_r_aabb = right_expand; + self.nodes[node_index] = BvhNode::Node { + child_l_aabb, + child_l_index, + child_r_aabb, + child_r_index, + parent_index, + }; + node_index = child_r_index; + } + } + BvhNode::Leaf { + shape_index, + parent_index, + } => { + // Split leaf into 2 nodes and insert the new box + let l_index = self.nodes.len(); + let new_left = BvhNode::Leaf { + parent_index: node_index, + shape_index: new_shape_index, + }; + shapes[new_shape_index].set_bh_node_index(l_index); + self.nodes.push(new_left); + + let child_r_aabb = shapes[shape_index].aabb(); + let child_r_index = self.nodes.len(); + let new_right = BvhNode::Leaf { + parent_index: node_index, + shape_index, + }; + shapes[shape_index].set_bh_node_index(child_r_index); + self.nodes.push(new_right); + + let new_node = BvhNode::Node { + child_l_aabb: shape_aabb, + child_l_index: l_index, + child_r_aabb, + child_r_index, + parent_index, + }; + self.nodes[node_index] = new_node; + self.fix_aabbs_ascending(shapes, parent_index); + return; } } } - best_rotation } - /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of - /// the given node (specified by `node_index`) that would improve the [`Bvh`]. - /// If there is, the best rotation found is performed. - /// - /// # Preconditions - /// - /// This function requires that the subtree at `node_index` has correct [`Aabb`]s. - /// - /// # Returns - /// - /// `Some(index_of_node)` if a new node was found that should be used for optimization. - /// - fn try_rotate>( + /// Removes a shape from the `BVH` + /// if swap_shape is true, it swaps the shape you are removing with the last shape in the shape slice + /// truncation of the data structure backing the shapes slice must be performed by the user + pub fn remove_shape>( &mut self, - node_index: usize, - shapes: &[Shape], - ) -> Option { - let (parent_index, child_l_index, child_r_index) = if let BvhNode::Node { - parent_index, - child_l_index, - child_r_index, - .. - } = self.nodes[node_index] - { - (parent_index, child_l_index, child_r_index) - } else { - unreachable!() - }; - - // Recalculate `Aabb`s for the children since at least one of them changed. Don't update - // the `Aabb`s in the node yet because they're still subject to change during potential - // upcoming rotations. - let child_l_aabb = self.nodes[child_l_index].get_node_aabb(shapes); - let child_r_aabb = self.nodes[child_r_index].get_node_aabb(shapes); - - let best_rotation = - self.find_better_rotation(child_l_index, &child_l_aabb, child_r_index, &child_r_aabb); - - if let Some((rotation_node_a, rotation_node_b)) = best_rotation { - self.rotate(rotation_node_a, rotation_node_b, shapes); + shapes: &mut [Shape], + deleted_shape_index: usize, + swap_shape: bool, + ) { + if self.nodes.is_empty() { + panic!("can't remove a node from a bvh with only one node"); + } + let bad_shape = &shapes[deleted_shape_index]; - // Update the children's children `Aabb`s and the children `Aabb`s of node. - self.fix_children_and_own_aabbs(node_index, shapes); + // to remove a node, delete it from the tree, remove the parent and replace it with the sibling + // swap the node being removed to the end of the slice and adjust the index of the node that was removed + // update the removed nodes index + // swap the shape to the end and update the node to still point at the right shape + let dead_node_index = bad_shape.bh_node_index(); - // Return parent node's index for upcoming refitting, - // since this node just changed its `Aabb`. - if node_index != 0 { - Some(OptimizationIndex::Refit(parent_index)) - } else { - None + if self.nodes.len() == 1 { + if dead_node_index == 0 { + self.nodes.clear(); } } else { - info!(" No useful rotation."); - // Set the correct children `Aabb`s, which have been computed earlier. - *self.nodes[node_index].child_l_aabb_mut() = child_l_aabb; - *self.nodes[node_index].child_r_aabb_mut() = child_r_aabb; - - // Only execute the following block, if `node_index` does not reference the root node. - if node_index != 0 { - // Even with no rotation being useful for this node, a parent node's rotation - // could be beneficial, so queue the parent *sometimes*. For reference see: - // https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307 - // TODO Evaluate whether this is a smart thing to do. - let mut rng = thread_rng(); - if rng.gen_bool(0.01) { - Some(OptimizationIndex::Refit(parent_index)) - } else { - // Otherwise, we still have to fix the parent's Aabbs - Some(OptimizationIndex::FixAabbs(parent_index)) - } + let dead_node = self.nodes[dead_node_index]; + + let parent_index = dead_node.parent(); + let gp_index = self.nodes[parent_index].parent(); + + let sibling_index = if self.node_is_left_child(dead_node_index) { + self.nodes[parent_index].child_r() } else { - None - } - } - } + self.nodes[parent_index].child_l() + }; - /// Sets `child_l_aabb` and child_r_aabb of a [`BvhNode::Node`] to match its children, - /// right after updating the children themselves. Not recursive. - fn fix_children_and_own_aabbs>( - &mut self, - node_index: usize, - shapes: &[Shape], - ) { - let (child_l_index, child_r_index) = if let BvhNode::Node { - child_l_index, - child_r_index, - .. - } = self.nodes[node_index] - { - (child_l_index, child_r_index) - } else { - unreachable!() - }; + // TODO: fix potential issue leaving empty spot in self.nodes + // the node swapped to sibling_index should probably be swapped to the end + // of the vector and the vector truncated + if parent_index == gp_index { + // We are removing one of the children of the root node + // The other child needs to become the root node + // The old root node and the dead child then have to be moved + assert!( + parent_index != 0, + "Circular node that wasn't root parent={} node={}", + parent_index, + dead_node_index + ); + + match self.nodes[sibling_index] { + BvhNode::Node { + child_l_index, + child_r_index, + .. + } => { + self.connect_nodes(child_l_index, parent_index, true, shapes); + self.connect_nodes(child_r_index, parent_index, false, shapes); + } + _ => { + self.nodes[0] = self.nodes[sibling_index]; + *self.nodes[0].parent_mut() = 0; + shapes[self.nodes[0].shape_index().unwrap()].set_bh_node_index(0); + } + } - self.fix_aabbs(child_l_index, shapes); - self.fix_aabbs(child_r_index, shapes); + self.swap_and_remove_index(shapes, sibling_index.max(dead_node_index)); + self.swap_and_remove_index(shapes, sibling_index.min(dead_node_index)); + } else { + let parent_is_left = self.node_is_left_child(parent_index); - *self.nodes[node_index].child_l_aabb_mut() = - self.nodes[child_l_index].get_node_aabb(shapes); - *self.nodes[node_index].child_r_aabb_mut() = - self.nodes[child_r_index].get_node_aabb(shapes); - } + self.connect_nodes(sibling_index, gp_index, parent_is_left, shapes); - /// Updates `child_l_aabb` and `child_r_aabb` of the `BvhNode::Node` - /// with the index `node_index` from its children. - fn fix_aabbs>( - &mut self, - node_index: usize, - shapes: &[Shape], - ) -> Option { - match self.nodes[node_index] { - BvhNode::Node { - parent_index, - child_l_index, - child_r_index, - .. - } => { - *self.nodes[node_index].child_l_aabb_mut() = - self.nodes[child_l_index].get_node_aabb(shapes); - *self.nodes[node_index].child_r_aabb_mut() = - self.nodes[child_r_index].get_node_aabb(shapes); + self.fix_aabbs_ascending(shapes, gp_index); + self.swap_and_remove_index(shapes, dead_node_index.max(parent_index)); + self.swap_and_remove_index(shapes, parent_index.min(dead_node_index)); + } + } - if node_index > 0 { - Some(OptimizationIndex::FixAabbs(parent_index)) - } else { - None + if swap_shape { + let end_shape = shapes.len() - 1; + if deleted_shape_index < end_shape { + shapes.swap(deleted_shape_index, end_shape); + let node_index = shapes[deleted_shape_index].bh_node_index(); + if let Some(index) = self.nodes[node_index].shape_index_mut() { + *index = deleted_shape_index } } - // Don't do anything if the node is a leaf. - _ => None, } } - /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice). - /// Also updates the [`Aabbs`]'s of the parents. - fn rotate>( + /// Fixes bvh + pub fn update_shapes<'a, Shape: BHShape>( &mut self, - node_a_index: usize, - node_b_index: usize, - shapes: &[Shape], + changed_shape_indices: impl IntoIterator + Copy, + shapes: &mut [Shape], ) { - info!(" ROTATING {} and {}", node_a_index, node_b_index); - - // Get parent indices - let node_a_parent_index = self.nodes[node_a_index].parent(); - let node_b_parent_index = self.nodes[node_b_index].parent(); - - // Get info about the nodes being a left or right child - let node_a_is_left_child = self.node_is_left_child(node_a_index); - let node_b_is_left_child = self.node_is_left_child(node_b_index); - - // Perform the switch - self.connect_nodes( - node_a_index, - node_b_parent_index, - node_b_is_left_child, - shapes, - ); - self.connect_nodes( - node_b_index, - node_a_parent_index, - node_a_is_left_child, - shapes, - ); + for i in changed_shape_indices { + self.remove_shape(shapes, *i, false); + } + for i in changed_shape_indices { + self.add_shape(shapes, *i); + } } - /// Updates the depth of a node, and sets the depth of its descendants accordingly. - fn update_depth_recursively(&mut self, node_index: usize, new_depth: u32) { - let children = { - let node = &mut self.nodes[node_index]; - match *node { + fn fix_aabbs_ascending>(&mut self, shapes: &[Shape], node_index: usize) { + let mut index_to_fix = node_index; + while index_to_fix != 0 { + let parent = self.nodes[index_to_fix].parent(); + match self.nodes[parent] { BvhNode::Node { - ref mut depth, child_l_index, child_r_index, + child_l_aabb, + child_r_aabb, .. } => { - *depth = new_depth; - Some((child_l_index, child_r_index)) - } - BvhNode::Leaf { ref mut depth, .. } => { - *depth = new_depth; - None + let l_aabb = self.nodes[child_l_index].get_node_aabb(shapes); + let r_aabb = self.nodes[child_r_index].get_node_aabb(shapes); + let mut stop = true; + let epsilon = T::from_f32(0.00001).unwrap_or(T::zero()); + if !l_aabb.relative_eq(&child_l_aabb, epsilon) { + stop = false; + *self.nodes[parent].child_l_aabb_mut() = l_aabb; + } + if !r_aabb.relative_eq(&child_r_aabb, epsilon) { + stop = false; + *self.nodes[parent].child_r_aabb_mut() = r_aabb; + } + if !stop { + index_to_fix = parent; + } else { + index_to_fix = 0; + } } + _ => index_to_fix = 0, } - }; - if let Some((child_l_index, child_r_index)) = children { - self.update_depth_recursively(child_l_index, new_depth + 1); - self.update_depth_recursively(child_r_index, new_depth + 1); } } - fn node_is_left_child(&self, node_index: usize) -> bool { - // Get the index of the parent. - let node_parent_index = self.nodes[node_index].parent(); - // Get the index of te left child of the parent. - let child_l_index = self.nodes[node_parent_index].child_l(); - child_l_index == node_index - } - - fn connect_nodes>( + fn swap_and_remove_index>( &mut self, - child_index: usize, - parent_index: usize, - left_child: bool, - shapes: &[Shape], + shapes: &mut [Shape], + node_index: usize, ) { - let child_aabb = self.nodes[child_index].get_node_aabb(shapes); - info!("\tConnecting: {} < {}.", child_index, parent_index); - // Set parent's child and `child_aabb`; and get its depth. - let parent_depth = { - match self.nodes[parent_index] { + let end = self.nodes.len() - 1; + if node_index != end { + self.nodes[node_index] = self.nodes[end]; + let parent_index = self.nodes[node_index].parent(); + + if let BvhNode::Leaf { .. } = self.nodes[parent_index] { + self.nodes.truncate(end); + return; + } + let parent = self.nodes[parent_index]; + let moved_left = parent.child_l() == end; + if !moved_left && parent.child_r() != end { + self.nodes.truncate(end); + return; + } + let ref_to_change = if moved_left { + self.nodes[parent_index].child_l_mut() + } else { + self.nodes[parent_index].child_r_mut() + }; + *ref_to_change = node_index; + + match self.nodes[node_index] { + BvhNode::Leaf { shape_index, .. } => { + shapes[shape_index].set_bh_node_index(node_index); + } BvhNode::Node { - ref mut child_l_index, - ref mut child_r_index, - ref mut child_l_aabb, - ref mut child_r_aabb, - depth, + child_l_index, + child_r_index, .. } => { - if left_child { - *child_l_index = child_index; - *child_l_aabb = child_aabb; - } else { - *child_r_index = child_index; - *child_r_aabb = child_aabb; - } - info!("\t {}'s new {}", parent_index, child_aabb); - depth + *self.nodes[child_l_index].parent_mut() = node_index; + *self.nodes[child_r_index].parent_mut() = node_index; } - // Assuming that our `Bvh` is correct, the parent cannot be a leaf. - _ => unreachable!(), } - }; - - // Set child's parent. - *self.nodes[child_index].parent_mut() = parent_index; - - // Update the node's and the node's descendants' depth values. - self.update_depth_recursively(child_index, parent_depth + 1); + } + self.nodes.truncate(end); } } @@ -540,25 +415,9 @@ mod tests { }; use std::collections::HashSet; - #[test] - /// Tests if [`Bvh::optimize()`] does not modify a fresh [`Bvh`]. - fn test_optimizing_new_bvh() { - let (shapes, mut bvh) = build_some_bh::(); - let original_nodes = bvh.nodes.clone(); - - // Query an update for all nodes. - let refit_shape_indices: HashSet = (0..shapes.len()).collect(); - bvh.optimize(&refit_shape_indices, &shapes); - - // Assert that all nodes are the same as before the update. - for (optimized, original) in bvh.nodes.iter().zip(original_nodes.iter()) { - assert_eq!(optimized, original); - } - } - #[test] /// Tests whether a Bvh is still consistent after a few optimization calls. - fn test_consistent_after_optimize() { + fn test_consistent_after_update_shapes() { let (mut shapes, mut bvh) = build_some_bh::(); shapes[0].pos = TPoint3::new(10.0, 1.0, 2.0); shapes[1].pos = TPoint3::new(-10.0, -10.0, 10.0); @@ -567,14 +426,14 @@ mod tests { shapes[4].pos = TPoint3::new(11.0, 1.0, 2.0); shapes[5].pos = TPoint3::new(11.0, 2.0, 2.0); - let refit_shape_indices = (0..6).collect(); - bvh.optimize(&refit_shape_indices, &shapes); + let refit_shape_indices: Vec<_> = (0..6).collect(); + bvh.update_shapes(&refit_shape_indices, &mut shapes); bvh.assert_consistent(&shapes); } #[test] /// Test whether a simple update on a simple [`Bvh]` yields the expected optimization result. - fn test_optimize_simple_update() { + fn test_update_shapes_simple_update() { let mut shapes = vec![ UnitBox::new(0, TPoint3::new(-50.0, 0.0, 0.0)), UnitBox::new(1, TPoint3::new(-40.0, 0.0, 0.0)), @@ -612,7 +471,7 @@ mod tests { // Move the first shape so that it is closer to shape #2. shapes[1].pos = TPoint3::new(40.0, 0.0, 0.0); let refit_shape_indices: HashSet = (1..2).collect(); - bvh.optimize(&refit_shape_indices, &shapes); + bvh.update_shapes(&refit_shape_indices, &mut shapes); bvh.pretty_print(); bvh.assert_consistent(&shapes); @@ -655,7 +514,6 @@ mod tests { // Root node. TBvhNode3::Node { parent_index: 0, - depth: 0, child_l_aabb: shapes[0].aabb().join(&shapes[1].aabb()), child_l_index: 1, child_r_aabb: shapes[2].aabb().join(&shapes[3].aabb()), @@ -664,7 +522,6 @@ mod tests { // Depth 1 nodes. TBvhNode3::Node { parent_index: 0, - depth: 1, child_l_aabb: shapes[0].aabb(), child_l_index: 3, child_r_aabb: shapes[1].aabb(), @@ -672,7 +529,6 @@ mod tests { }, TBvhNode3::Node { parent_index: 0, - depth: 1, child_l_aabb: shapes[2].aabb(), child_l_index: 5, child_r_aabb: shapes[3].aabb(), @@ -681,22 +537,18 @@ mod tests { // Depth 2 nodes (leaves). TBvhNode3::Leaf { parent_index: 1, - depth: 2, shape_index: 0, }, TBvhNode3::Leaf { parent_index: 1, - depth: 2, shape_index: 1, }, TBvhNode3::Leaf { parent_index: 2, - depth: 2, shape_index: 2, }, TBvhNode3::Leaf { parent_index: 2, - depth: 2, shape_index: 3, }, ]; @@ -788,146 +640,9 @@ mod tests { .relative_eq(&shapes[1].aabb(), f32::EPSILON)); } - #[test] - fn test_rotate_grandchildren() { - let (shapes, mut bvh) = create_predictable_bvh(); - - // Switch two nodes. - bvh.rotate(3, 5, &shapes); - - // Check if the resulting tree is as expected. - let TBvh3 { nodes } = bvh; - - assert_eq!(nodes[0].parent(), 0); - assert_eq!(nodes[0].child_l(), 1); - assert_eq!(nodes[0].child_r(), 2); - - assert_eq!(nodes[1].parent(), 0); - assert_eq!(nodes[1].child_l(), 5); - assert_eq!(nodes[1].child_r(), 4); - - assert_eq!(nodes[2].parent(), 0); - assert_eq!(nodes[2].child_l(), 3); - assert_eq!(nodes[2].child_r(), 6); - - assert_eq!(nodes[3].parent(), 2); - assert_eq!(nodes[4].parent(), 1); - assert_eq!(nodes[5].parent(), 1); - assert_eq!(nodes[6].parent(), 2); - - assert!(nodes[1] - .child_l_aabb() - .relative_eq(&shapes[2].aabb(), f32::EPSILON)); - assert!(nodes[1] - .child_r_aabb() - .relative_eq(&shapes[1].aabb(), f32::EPSILON)); - assert!(nodes[2] - .child_l_aabb() - .relative_eq(&shapes[0].aabb(), f32::EPSILON)); - assert!(nodes[2] - .child_r_aabb() - .relative_eq(&shapes[3].aabb(), f32::EPSILON)); - } - - #[test] - fn test_rotate_child_grandchild() { - let (shapes, mut bvh) = create_predictable_bvh(); - - // Switch two nodes. - bvh.rotate(1, 5, &shapes); - - // Check if the resulting tree is as expected. - let TBvh3 { nodes } = bvh; - - assert_eq!(nodes[0].parent(), 0); - assert_eq!(nodes[0].child_l(), 5); - assert_eq!(nodes[0].child_r(), 2); - - assert_eq!(nodes[1].parent(), 2); - assert_eq!(nodes[1].child_l(), 3); - assert_eq!(nodes[1].child_r(), 4); - - assert_eq!(nodes[2].parent(), 0); - assert_eq!(nodes[2].child_l(), 1); - assert_eq!(nodes[2].child_r(), 6); - - assert_eq!(nodes[3].parent(), 1); - assert_eq!(nodes[4].parent(), 1); - assert_eq!(nodes[5].parent(), 0); - assert_eq!(nodes[6].parent(), 2); - - assert!(nodes[0] - .child_l_aabb() - .relative_eq(&shapes[2].aabb(), f32::EPSILON)); - assert!(nodes[2] - .child_r_aabb() - .relative_eq(&shapes[3].aabb(), f32::EPSILON)); - assert!(nodes[1] - .child_l_aabb() - .relative_eq(&shapes[0].aabb(), f32::EPSILON)); - assert!(nodes[1] - .child_r_aabb() - .relative_eq(&shapes[1].aabb(), f32::EPSILON)); - } - - #[test] - fn test_try_rotate_child_grandchild() { - let (mut shapes, mut bvh) = create_predictable_bvh(); - - // Move the second shape. - shapes[2].pos = TPoint3::new(-40.0, 0.0, 0.0); - - // Try to rotate node 2 because node 5 changed. - bvh.try_rotate(2, &shapes); - - // Try to rotate node 0 because rotating node 2 should not have yielded a result. - bvh.try_rotate(0, &shapes); - - // Check if the resulting tree is as expected. - let TBvh3 { nodes } = bvh; - - assert_eq!(nodes[0].parent(), 0); - assert_eq!(nodes[0].child_l(), 5); - assert_eq!(nodes[0].child_r(), 2); - - assert_eq!(nodes[1].parent(), 2); - assert_eq!(nodes[1].child_l(), 3); - assert_eq!(nodes[1].child_r(), 4); - - assert_eq!(nodes[2].parent(), 0); - assert_eq!(nodes[2].child_l(), 1); - assert_eq!(nodes[2].child_r(), 6); - - assert_eq!(nodes[3].parent(), 1); - assert_eq!(nodes[4].parent(), 1); - assert_eq!(nodes[5].parent(), 0); - assert_eq!(nodes[6].parent(), 2); - - assert!(nodes[0] - .child_l_aabb() - .relative_eq(&shapes[2].aabb(), f32::EPSILON)); - let right_subtree_aabb = &shapes[0] - .aabb() - .join(&shapes[1].aabb()) - .join(&shapes[3].aabb()); - assert!(nodes[0] - .child_r_aabb() - .relative_eq(right_subtree_aabb, f32::EPSILON)); - - assert!(nodes[2] - .child_r_aabb() - .relative_eq(&shapes[3].aabb(), f32::EPSILON)); - assert!(nodes[1] - .child_l_aabb() - .relative_eq(&shapes[0].aabb(), f32::EPSILON)); - assert!(nodes[1] - .child_r_aabb() - .relative_eq(&shapes[1].aabb(), f32::EPSILON)); - } - #[test] /// Test optimizing [`Bvh`] after randomizing 50% of the shapes. - fn test_optimize_bvh_12k_75p() { + fn test_update_shapes_bvh_12k_75p() { let bounds = default_bounds(); let mut triangles = create_n_cubes(1_000, &bounds); @@ -945,7 +660,7 @@ mod tests { assert!(!bvh.is_consistent(&triangles), "Bvh is consistent."); // After fixing the `Aabb` consistency should be restored. - bvh.optimize(&updated, &triangles); + bvh.update_shapes(&updated, &mut triangles); bvh.assert_consistent(&triangles); bvh.assert_tight(); } @@ -972,7 +687,7 @@ mod bench { /// Benchmark optimizing a [`Bvh`] with 120,000 [`Triangle`]'ss, where `percent` /// [`Triangle`]'s have been randomly moved. - fn optimize_bvh_120k(percent: f32, b: &mut ::test::Bencher) { + fn update_shapes_bvh_120k(percent: f32, b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); let mut bvh = TBvh3::build(&mut triangles); @@ -982,35 +697,35 @@ mod bench { b.iter(|| { let updated = randomly_transform_scene(&mut triangles, num_move, &bounds, Some(10.0), &mut seed); - bvh.optimize(&updated, &triangles); + bvh.update_shapes(&updated, &mut triangles); }); } #[bench] - fn bench_optimize_bvh_120k_00p(b: &mut ::test::Bencher) { - optimize_bvh_120k(0.0, b); + fn bench_update_shapes_bvh_120k_00p(b: &mut ::test::Bencher) { + update_shapes_bvh_120k(0.0, b); } #[bench] - fn bench_optimize_bvh_120k_01p(b: &mut ::test::Bencher) { - optimize_bvh_120k(0.01, b); + fn bench_update_shapes_bvh_120k_01p(b: &mut ::test::Bencher) { + update_shapes_bvh_120k(0.01, b); } #[bench] - fn bench_optimize_bvh_120k_10p(b: &mut ::test::Bencher) { - optimize_bvh_120k(0.1, b); + fn bench_update_shapes_bvh_120k_10p(b: &mut ::test::Bencher) { + update_shapes_bvh_120k(0.1, b); } #[bench] - fn bench_optimize_bvh_120k_50p(b: &mut ::test::Bencher) { - optimize_bvh_120k(0.5, b); + fn bench_update_shapes_bvh_120k_50p(b: &mut ::test::Bencher) { + update_shapes_bvh_120k(0.5, b); } /// Move `percent` [`Triangle`]`s in the scene given by `triangles` and optimize the /// [`Bvh`]. Iterate this procedure `iterations` times. Afterwards benchmark the performance /// of intersecting this scene/[`Bvh`]. - fn intersect_scene_after_optimize( - triangles: &mut Vec, + fn intersect_scene_after_update_shapes( + triangles: &mut [Triangle], bounds: &TAabb3, percent: f32, max_offset: Option, @@ -1024,38 +739,38 @@ mod bench { for _ in 0..iterations { let updated = randomly_transform_scene(triangles, num_move, bounds, max_offset, &mut seed); - bvh.optimize(&updated, triangles); + bvh.update_shapes(&updated, triangles); } intersect_bh(&bvh, triangles, bounds, b); } #[bench] - fn bench_intersect_120k_after_optimize_00p(b: &mut ::test::Bencher) { + fn bench_intersect_120k_after_update_shapes_00p(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); - intersect_scene_after_optimize(&mut triangles, &bounds, 0.0, None, 10, b); + intersect_scene_after_update_shapes(&mut triangles, &bounds, 0.0, None, 10, b); } #[bench] - fn bench_intersect_120k_after_optimize_01p(b: &mut ::test::Bencher) { + fn bench_intersect_120k_after_update_shapes_01p(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); - intersect_scene_after_optimize(&mut triangles, &bounds, 0.01, None, 10, b); + intersect_scene_after_update_shapes(&mut triangles, &bounds, 0.01, None, 10, b); } #[bench] - fn bench_intersect_120k_after_optimize_10p(b: &mut ::test::Bencher) { + fn bench_intersect_120k_after_update_shapes_10p(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); - intersect_scene_after_optimize(&mut triangles, &bounds, 0.1, None, 10, b); + intersect_scene_after_update_shapes(&mut triangles, &bounds, 0.1, None, 10, b); } #[bench] - fn bench_intersect_120k_after_optimize_50p(b: &mut ::test::Bencher) { + fn bench_intersect_120k_after_update_shapes_50p(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); - intersect_scene_after_optimize(&mut triangles, &bounds, 0.5, None, 10, b); + intersect_scene_after_update_shapes(&mut triangles, &bounds, 0.5, None, 10, b); } /// Move `percent` [`Triangle`]'s in the scene given by `triangles` `iterations` times. @@ -1063,7 +778,7 @@ mod bench { /// scene/[`Bvh`]. Used to compare optimizing with rebuilding. For reference see /// `intersect_scene_after_optimize`. fn intersect_scene_with_rebuild( - triangles: &mut Vec, + triangles: &mut [Triangle], bounds: &TAabb3, percent: f32, max_offset: Option, @@ -1110,29 +825,29 @@ mod bench { /// Benchmark intersecting a [`Bvh`] for Sponza after randomly moving one [`Triangle`] and /// optimizing. - fn intersect_sponza_after_optimize(percent: f32, b: &mut ::test::Bencher) { + fn intersect_sponza_after_update_shapes(percent: f32, b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); - intersect_scene_after_optimize(&mut triangles, &bounds, percent, Some(0.1), 10, b); + intersect_scene_after_update_shapes(&mut triangles, &bounds, percent, Some(0.1), 10, b); } #[bench] - fn bench_intersect_sponza_after_optimize_00p(b: &mut ::test::Bencher) { - intersect_sponza_after_optimize(0.0, b); + fn bench_intersect_sponza_after_update_shapes_00p(b: &mut ::test::Bencher) { + intersect_sponza_after_update_shapes(0.0, b); } #[bench] - fn bench_intersect_sponza_after_optimize_01p(b: &mut ::test::Bencher) { - intersect_sponza_after_optimize(0.01, b); + fn bench_intersect_sponza_after_update_shapes_01p(b: &mut ::test::Bencher) { + intersect_sponza_after_update_shapes(0.01, b); } #[bench] - fn bench_intersect_sponza_after_optimize_10p(b: &mut ::test::Bencher) { - intersect_sponza_after_optimize(0.1, b); + fn bench_intersect_sponza_after_update_shapes_10p(b: &mut ::test::Bencher) { + intersect_sponza_after_update_shapes(0.1, b); } #[bench] - fn bench_intersect_sponza_after_optimize_50p(b: &mut ::test::Bencher) { - intersect_sponza_after_optimize(0.5, b); + fn bench_intersect_sponza_after_update_shapes_50p(b: &mut ::test::Bencher) { + intersect_sponza_after_update_shapes(0.5, b); } /// Benchmark intersecting a [`Bvh`] for Sponza after rebuilding. Used to compare optimizing diff --git a/src/testbase.rs b/src/testbase.rs index df63fa0..7d1573d 100644 --- a/src/testbase.rs +++ b/src/testbase.rs @@ -407,7 +407,7 @@ pub fn load_sponza_scene() -> (Vec, TAabb3) { /// offset of a shape. This is used to simulate a realistic scene. /// Returns a [`HashSet`] of indices of modified triangles. pub fn randomly_transform_scene( - triangles: &mut Vec, + triangles: &mut [Triangle], amount: usize, bounds: &TAabb3, max_offset_option: Option,