obvhs/bvh2/
insertion_removal.rs

1use core::f32;
2
3use crate::{
4    Boundable, INVALID,
5    aabb::Aabb,
6    bvh2::{Bvh2, Bvh2Node, update_primitives_to_nodes_for_node},
7    faststack::{FastStack, HeapStack},
8};
9
10use super::DEFAULT_MAX_STACK_DEPTH;
11
12// The index and inherited_cost of a given candidate sibling used for insertion.
13#[doc(hidden)]
14#[derive(Debug, Default, Clone, Copy)]
15pub struct SiblingInsertionCandidate {
16    inherited_cost: f32,
17    index: u32,
18}
19
20impl Bvh2 {
21    /// Removes and returns the leaf specified by `node_id`.
22    /// Puts `node_id` sibling in its parents place then moves the last two nodes into the now empty slots at `node_id`
23    /// and its sibling.
24    ///
25    /// Doesn't update the primitive_indices mapping. If this node is just going to be re-inserted again, nothing needs
26    /// to be done with primitive_indices, the mapping will still be valid. If this primitive needs to be removed
27    /// permanently see Bvh2::remove_primitive().
28    ///
29    /// # Arguments
30    /// * `node_id` - The index into self.nodes of the node that is to be removed
31    pub fn remove_leaf(&mut self, node_id: usize) -> Bvh2Node {
32        assert!(
33            !self.uses_spatial_splits,
34            "Removing leaves while using spatial splits is currently unsupported as it would require a mapping \
35from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
36        );
37
38        let node_to_remove = self.nodes[node_id];
39        assert!(node_to_remove.is_leaf());
40
41        if self.nodes.len() == 1 {
42            // Special case if the BVH is just a leaf
43            self.nodes.clear();
44            self.parents.clear();
45            self.primitives_to_nodes.clear();
46            return node_to_remove;
47        }
48
49        // if primitives_to_nodes has already been initialized
50        if !self.primitives_to_nodes.is_empty() {
51            // Invalidate primitives_to_nodes instances
52            for node_prim_id in
53                node_to_remove.first_index..node_to_remove.first_index + node_to_remove.prim_count
54            {
55                let direct_prim_id = self.primitive_indices[node_prim_id as usize];
56                self.primitives_to_nodes[direct_prim_id as usize] = INVALID;
57            }
58        }
59
60        let sibling_id = Bvh2Node::get_sibling_id(node_id);
61        debug_assert_eq!(self.parents[node_id], self.parents[sibling_id]); // Both children should already have the same parent.
62        let mut parent_id = self.parents[node_id] as usize;
63
64        // Put sibling in parent's place (parent doesn't exist anymore)
65        self.nodes[parent_id] = self.nodes[sibling_id];
66        let sibling = &mut self.nodes[parent_id];
67        if sibling.is_leaf() {
68            // Tell primitives where their node went.
69            update_primitives_to_nodes_for_node(
70                sibling,
71                parent_id,
72                &self.primitive_indices,
73                &mut self.primitives_to_nodes,
74            )
75        } else {
76            // Tell children of sibling where their parent went.
77            let left_sibling_child = sibling.first_index as usize;
78            self.parents[left_sibling_child] = parent_id as u32;
79            self.parents[left_sibling_child + 1] = parent_id as u32;
80        }
81        // Don't need to update other parents here since the parent that was for this `parent_id` slot is now the direct
82        // parent of the moved sibling, and the parents of `node_id` and `sibling_id` are updated below.
83
84        // Now slots at both node_id and sibling_id are empty.
85        // Take the last two nodes "src" and put them in those now empty "dst" slots.
86        let end_nodes = node_id >= self.nodes.len() - 2;
87        if end_nodes {
88            // If these were already the last 2 nodes in the list we can just discard both.
89            self.nodes.pop().unwrap();
90            self.nodes.pop().unwrap();
91            self.parents.pop().unwrap();
92            self.parents.pop().unwrap();
93        } else {
94            let dst_left_id = Bvh2Node::get_left_sibling_id(node_id);
95            let dst_right_id = Bvh2Node::get_right_sibling_id(node_id);
96
97            let src_left_id = self.nodes.len() as u32 - 2;
98            let src_right_id = Bvh2Node::get_sibling_id32(src_left_id);
99            let src_right_parent = self.parents.pop().unwrap();
100            let src_left_parent = self.parents.pop().unwrap();
101
102            self.parents[dst_left_id] = src_left_parent;
103            self.parents[dst_right_id] = src_right_parent;
104
105            debug_assert_eq!(src_left_parent, src_right_parent); // Both children should already have the same parent.
106            let parent_of_relocated = &mut self.nodes[src_left_parent as usize];
107            debug_assert!(!parent_of_relocated.is_leaf());
108            debug_assert_eq!(parent_of_relocated.first_index, src_left_id);
109            debug_assert_eq!(parent_of_relocated.first_index + 1, src_right_id);
110            // Tell the actual parent of the nodes that are moving where they're going to be now.
111            self.nodes[src_left_parent as usize].first_index = dst_left_id as u32;
112
113            let right_src_sibling = self.nodes.pop().unwrap(); // Last node is right src sibling
114            if right_src_sibling.is_leaf() {
115                // Tell primitives where their node went.
116                update_primitives_to_nodes_for_node(
117                    &right_src_sibling,
118                    dst_right_id,
119                    &self.primitive_indices,
120                    &mut self.primitives_to_nodes,
121                );
122            } else {
123                // Go to children of right_src_sibling and tell them where their parent went
124                self.parents[right_src_sibling.first_index as usize] = dst_right_id as u32;
125                self.parents[right_src_sibling.first_index as usize + 1] = dst_right_id as u32;
126            }
127            self.nodes[dst_right_id] = right_src_sibling;
128
129            let left_src_sibling = self.nodes.pop().unwrap(); // Last node is left src sibling
130            if left_src_sibling.is_leaf() {
131                // Tell primitives where their node went.
132                update_primitives_to_nodes_for_node(
133                    &left_src_sibling,
134                    dst_left_id,
135                    &self.primitive_indices,
136                    &mut self.primitives_to_nodes,
137                );
138            } else {
139                // Go to children of left_src_sibling and tell them where their parent went
140                self.parents[left_src_sibling.first_index as usize] = dst_left_id as u32;
141                self.parents[left_src_sibling.first_index as usize + 1] = dst_left_id as u32;
142            }
143            self.nodes[dst_left_id] = left_src_sibling;
144
145            // If the to be removed node's parent was at the end of the array and has now moved update parent_id:
146            if parent_id as u32 == src_left_id {
147                parent_id = dst_left_id;
148            }
149            if parent_id as u32 == src_right_id {
150                parent_id = dst_right_id;
151            }
152        }
153
154        // Need to work up the tree updating the aabbs since we just removed a node.
155        self.refit_from_fast(parent_id);
156
157        self.children_are_ordered_after_parents = false;
158        // Return the removed node.
159        node_to_remove
160    }
161
162    /// Searches the tree recursively to find the best sibling for the node being inserted. The best sibling is
163    /// classified as the sibling that if chosen it would increase the surface area of the BVH the least.
164    /// When the best sibling is found, a parent of both the sibling and the new node is put in the location of
165    /// the sibling and both the sibling and new node are added to the end of the bvh.nodes.
166    /// See "Branch and Bound" <https://box2d.org/files/ErinCatto_DynamicBVH_Full.pdf>
167    /// Jiˇrí Bittner et al. 2012 Fast Insertion-Based Optimization of Bounding Volume Hierarchies
168    ///
169    /// # Returns
170    /// The index of the newly added node (always `bvh.nodes.len() - 1` since the node it put at the end).
171    ///
172    /// # Arguments
173    /// * `new_node` - This node must be a leaf and already have a valid first_index into primitive_indices
174    /// * `stack` - Used for the traversal stack. Needs to be large enough to initially accommodate traversal to the
175    ///   deepest leaf of the BVH. insert_leaf() will resize this stack after traversal to be at least 2x the
176    ///   required size. This ends up being quite a bit faster than using a Vec and works well when inserting multiple
177    ///   nodes. But does require the user to provide a good initial guess. SiblingInsertionCandidate is tiny so be
178    ///   generous. Something like: `stack.reserve(bvh.depth(0) * 2).max(1000);` If you are inserting a lot of leaves
179    ///   don't call bvh.depth(0) with each leaf just let insert_leaf() resize the stack as needed.
180    pub fn insert_leaf(
181        &mut self,
182        new_node: Bvh2Node,
183        stack: &mut HeapStack<SiblingInsertionCandidate>,
184    ) -> usize {
185        assert!(new_node.is_leaf());
186
187        if self.nodes.is_empty() {
188            self.nodes.push(new_node);
189            self.parents.clear();
190            self.parents.push(0);
191            return 0;
192        }
193
194        self.init_parents_if_uninit();
195
196        let mut min_cost = f32::MAX;
197        let mut best_sibling_candidate_id = 0;
198        let mut max_stack_len = 1;
199        let new_node_cost = new_node.aabb().half_area();
200
201        stack.clear();
202        let root_aabb = self.nodes[0].aabb();
203
204        // Traverse the BVH to find the best sibling
205        stack.push(SiblingInsertionCandidate {
206            inherited_cost: root_aabb.union(new_node.aabb()).half_area() - root_aabb.half_area(),
207            index: 0,
208        });
209        while let Some(sibling_candidate) = stack.pop() {
210            let current_node_index = sibling_candidate.index as usize;
211
212            let candidate = &self.nodes[current_node_index];
213
214            let direct_cost = candidate.aabb().union(new_node.aabb()).half_area();
215            let total_cost = direct_cost + sibling_candidate.inherited_cost;
216
217            if total_cost < min_cost {
218                min_cost = total_cost;
219                best_sibling_candidate_id = current_node_index;
220            }
221
222            // If this is not a leaf, it's possible a better cost could be found further down.
223            if !candidate.is_leaf() {
224                let inherited_cost = total_cost - candidate.aabb().half_area();
225                let min_subtree_cost = new_node_cost + inherited_cost;
226                if min_subtree_cost < min_cost {
227                    stack.push(SiblingInsertionCandidate {
228                        inherited_cost,
229                        index: candidate.first_index,
230                    });
231                    stack.push(SiblingInsertionCandidate {
232                        inherited_cost,
233                        index: candidate.first_index + 1,
234                    });
235                    max_stack_len = stack.len().max(max_stack_len);
236                }
237            }
238        }
239
240        if max_stack_len * 2 > stack.cap() {
241            stack.reserve(max_stack_len * 2);
242        }
243
244        let best_sibling_candidate = self.nodes[best_sibling_candidate_id];
245
246        // To avoid having gaps or re-arranging the BVH:
247        // The new parent goes in the sibling's position.
248        // The sibling and new node go on the end.
249        let new_sibling_id = self.nodes.len() as u32;
250        let new_parent = Bvh2Node::new(
251            new_node.aabb().union(best_sibling_candidate.aabb()),
252            0,
253            new_sibling_id,
254        );
255
256        // New parent goes in the sibling's position.
257        let new_parent_id = best_sibling_candidate_id;
258        self.nodes[new_parent_id] = new_parent;
259
260        if best_sibling_candidate.is_leaf() {
261            // Tell primitives where their node went.
262            update_primitives_to_nodes_for_node(
263                &best_sibling_candidate,
264                new_sibling_id as usize,
265                &self.primitive_indices,
266                &mut self.primitives_to_nodes,
267            )
268        } else {
269            // If the best selected sibling was an inner node, we need to update the parents mapping so that the children of
270            // that node point to the new location that it's being moved to.
271            self.parents[best_sibling_candidate.first_index as usize] = new_sibling_id;
272            self.parents[best_sibling_candidate.first_index as usize + 1] = new_sibling_id;
273        }
274        self.nodes.push(best_sibling_candidate);
275        let new_node_id = self.nodes.len();
276        self.nodes.push(new_node); // Put the new node at the very end.
277        self.parents.push(new_parent_id as u32);
278        self.parents.push(new_parent_id as u32);
279
280        // if primitives_to_nodes has already been initialized
281        if !self.primitives_to_nodes.is_empty() {
282            // Tell primitives where their node went.
283            let end = new_node.first_index + new_node.prim_count;
284            if self.primitives_to_nodes.len() < end as usize {
285                // Since we are adding a primitive it's possible that primitives_to_nodes is not large enough yet.
286                self.primitives_to_nodes.resize(end as usize, INVALID);
287            }
288            update_primitives_to_nodes_for_node(
289                &new_node,
290                new_node_id,
291                &self.primitive_indices,
292                &mut self.primitives_to_nodes,
293            )
294        }
295
296        // Need to work up the tree updating the aabbs since we just added a node.
297        self.refit_from_fast(new_parent_id);
298
299        new_node_id
300    }
301
302    /// Removes the leaf that contains the given primitive. Should be correct for nodes with multiple primitives per
303    /// leaf but faster for nodes with only one primitive per leaf, and will leave node aabb oversized.
304    /// Updates Bvh2::primitive_indices and Bvh2::primitive_indices_freelist.
305    ///
306    /// # Arguments
307    /// * `primitive_id` - The index of the primitive being removed.
308    pub fn remove_primitive(&mut self, primitive_id: u32) {
309        assert!(
310            !self.uses_spatial_splits,
311            "Removing primitives while using spatial splits is currently unsupported as it would require a mapping \
312from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
313        );
314        let remove_primitive_id = primitive_id;
315        self.init_parents_if_uninit();
316        self.init_primitives_to_nodes_if_uninit();
317
318        let node_id = self.primitives_to_nodes[remove_primitive_id as usize];
319
320        let node = &self.nodes[node_id as usize];
321        assert!(node.is_leaf());
322        if node.prim_count == 1 {
323            let removed_leaf = self.remove_leaf(node_id as usize);
324            self.primitive_indices_freelist
325                .push(removed_leaf.first_index);
326            self.primitive_indices[removed_leaf.first_index as usize] = INVALID;
327        } else {
328            // Update leaf with the remaining primitives, use the existing leftover space in primitive_indices and
329            // only add the removed primitive to the freelist
330
331            let node = &mut self.nodes[node_id as usize];
332
333            let start = node.first_index as usize;
334            let end = (node.first_index + node.prim_count) as usize;
335            let last = end - 1;
336            let mut spare_spot_id = start;
337            // Condense primitive_indices for this node.
338            for node_prim_id in start..end {
339                let direct_prim_id = self.primitive_indices[node_prim_id];
340                if direct_prim_id == remove_primitive_id {
341                    break;
342                }
343                spare_spot_id += 1;
344            }
345            if spare_spot_id < last {
346                self.primitive_indices[spare_spot_id] = self.primitive_indices[last];
347            }
348            // Free now open last position.
349            self.primitive_indices_freelist.push(last as u32);
350            self.primitive_indices[last] = INVALID;
351
352            assert!(node.prim_count > 1);
353            node.prim_count -= 1;
354        }
355
356        if self.primitives_to_nodes.len() > remove_primitive_id as usize {
357            self.primitives_to_nodes[remove_primitive_id as usize] = INVALID;
358        }
359    }
360
361    /// Searches the tree recursively to find the best sibling for the primitive being inserted
362    /// (see Bvh2::insert_leaf()). Updates Bvh2::primitive_indices and Bvh2::primitive_indices_freelist.
363    ///
364    /// # Returns
365    /// The index of the newly added node.
366    ///
367    /// # Arguments
368    /// * `bvh` - The Bvh2 the new node is being added to
369    /// * `primitive_id` - The index of the primitive being inserted.
370    /// * `stack` - Used for the traversal stack. Needs to be large enough to initially accommodate traversal to the
371    ///   deepest leaf of the BVH. insert_leaf() will resize this stack after traversal to be at least 2x the
372    ///   required size. This ends up being quite a bit faster than using a Vec and works well when inserting multiple
373    ///   nodes. But does require the user to provide a good initial guess. SiblingInsertionCandidate is tiny so be
374    ///   generous. Something like: `stack.reserve(bvh.depth(0) * 2).max(1000);` If you are inserting a lot of leaves
375    ///   don't call bvh.depth(0) with each leaf just let insert_leaf() resize the stack as needed.
376    pub fn insert_primitive(
377        &mut self,
378        aabb: Aabb,
379        primitive_id: u32,
380        stack: &mut HeapStack<SiblingInsertionCandidate>,
381    ) -> usize {
382        self.init_primitives_to_nodes_if_uninit();
383        self.init_parents_if_uninit();
384        if self.primitives_to_nodes.len() <= primitive_id as usize {
385            self.primitives_to_nodes
386                .resize(primitive_id as usize + 1, INVALID);
387        }
388        let first_index = if let Some(free_slot) = self.primitive_indices_freelist.pop() {
389            self.primitive_indices[free_slot as usize] = primitive_id;
390            free_slot
391        } else {
392            self.primitive_indices.push(primitive_id);
393            self.primitive_indices.len() as u32 - 1
394        };
395        let new_node_id = self.insert_leaf(Bvh2Node::new(aabb, 1, first_index), stack);
396        self.primitives_to_nodes[primitive_id as usize] = new_node_id as u32;
397        new_node_id
398    }
399}
400
401/// Slow at building, makes a slow bvh, just for testing insertion.
402/// Can result in very deep BVHs in some cases.
403///
404/// Dramatically slower than ploc at both building and traversal. Easily 10x or 100x slower at building.
405/// (goes up by something like n^3 after a certain threshold).
406/// (BVH quality still improved afterward lot by reinsertion/collapse).
407#[doc(hidden)]
408pub fn build_bvh2_by_insertion<T: Boundable>(primitives: &[T]) -> Bvh2 {
409    let mut bvh = Bvh2::default();
410
411    let mut stack = HeapStack::new_with_capacity(1000);
412
413    for prim_id in 1..primitives.len() {
414        bvh.insert_primitive(primitives[prim_id].aabb(), prim_id as u32, &mut stack);
415    }
416
417    // Update max depth for validate
418    bvh.max_depth = (bvh.depth(0) + 1).max(DEFAULT_MAX_STACK_DEPTH);
419
420    #[cfg(debug_assertions)]
421    {
422        bvh.validate(primitives, false, true);
423    }
424
425    bvh
426}
427
428/// Just here to for testing/benchmarking/validating leaf removed and inserting. See reinsertion.rs if you want to
429/// optimize a BVH2. This currently actually tends to make a good bvh slower since doing a lot of insert_leaf_node tends
430/// to result in very deep BVHs.
431#[doc(hidden)]
432pub fn slow_leaf_reinsertion(bvh: &mut Bvh2) {
433    let mut stack = HeapStack::new_with_capacity(1000);
434    for node_id in 1..bvh.nodes.len() {
435        if bvh.nodes.len() <= node_id {
436            break;
437        }
438        if bvh.nodes[node_id].is_leaf() {
439            // Assert that the parent of this node is not a leaf (a parent could never be a leaf)
440            assert!(!bvh.nodes[bvh.parents[node_id] as usize].is_leaf());
441            // If the node is a leaf, remove it
442            let removed_leaf = bvh.remove_leaf(node_id);
443            // Insert it again, maybe it will find a better spot
444            bvh.insert_leaf(removed_leaf, &mut stack);
445        }
446    }
447    #[cfg(debug_assertions)]
448    {
449        bvh.validate_parents();
450    }
451}
452
453#[cfg(test)]
454mod tests {
455    use std::time::Duration;
456
457    use super::*;
458    use crate::{BvhBuildParams, bvh2::builder::build_bvh2, test_util::geometry::demoscene};
459
460    #[test]
461    fn build_by_insertion() {
462        for res in 30..=32 {
463            let tris = demoscene(res, 0);
464            let bvh = build_bvh2_by_insertion(&tris);
465            bvh.validate(&tris, false, false);
466        }
467    }
468
469    #[test]
470    fn test_slow_leaf_reinsertion() {
471        for res in 30..=32 {
472            let tris = demoscene(res, 0);
473
474            let mut bvh = build_bvh2(
475                &tris,
476                BvhBuildParams::medium_build(),
477                &mut Duration::default(),
478            );
479            bvh.init_primitives_to_nodes_if_uninit();
480            bvh.init_parents_if_uninit();
481            slow_leaf_reinsertion(&mut bvh);
482            bvh.validate(&tris, false, false);
483            bvh.reorder_in_stack_traversal_order();
484            bvh.validate(&tris, false, false);
485        }
486    }
487
488    #[test]
489    fn remove_all_primitives() {
490        let tris = demoscene(16, 0);
491
492        // Test with both a bvh that only has one primitive per leaf
493        // and also with one that has multiple primitives per leaf.
494        let bvh1 = build_bvh2(
495            &tris,
496            BvhBuildParams::fastest_build(),
497            &mut Duration::default(),
498        );
499        let bvh2 = build_bvh2(
500            &tris,
501            BvhBuildParams::medium_build(),
502            &mut Duration::default(),
503        );
504
505        let mut found_leaf_with_multiple_nodes = false;
506        for node in &bvh2.nodes {
507            if node.prim_count > 1 {
508                found_leaf_with_multiple_nodes = true;
509                break;
510            }
511        }
512        if !found_leaf_with_multiple_nodes {
513            panic!(
514                "Test remove_all_primitives bvh2 should have some nodes that contain multiple primitives"
515            );
516        }
517
518        for bvh in &mut [bvh1, bvh2] {
519            bvh.init_primitives_to_nodes_if_uninit();
520            bvh.init_parents_if_uninit();
521            bvh.validate(&tris, false, false);
522
523            for primitive_id in 0..tris.len() as u32 {
524                bvh.remove_primitive(primitive_id);
525                bvh.validate(&tris, false, false);
526            }
527
528            assert_eq!(bvh.nodes.len(), 0);
529            assert_eq!(bvh.parents.len(), 0);
530            assert_eq!(bvh.primitives_to_nodes.len(), 0);
531            bvh.validate(&tris, false, false);
532        }
533    }
534
535    #[test]
536    fn remove_and_insert_all_primitives() {
537        let tris = demoscene(16, 0);
538
539        let mut bvh = build_bvh2(
540            &tris,
541            BvhBuildParams::medium_build(),
542            &mut Duration::default(),
543        );
544        bvh.init_primitives_to_nodes_if_uninit();
545        bvh.init_parents_if_uninit();
546        bvh.validate(&tris, false, false);
547
548        let mut stack = HeapStack::new_with_capacity(1000);
549
550        for primitive_id in 0..tris.len() as u32 {
551            bvh.remove_primitive(primitive_id);
552            bvh.validate(&tris, false, false);
553        }
554
555        for primitive_id in 0..tris.len() as u32 {
556            bvh.insert_primitive(tris[primitive_id as usize].aabb(), primitive_id, &mut stack);
557            bvh.validate_primitives_to_nodes();
558        }
559
560        bvh.validate(&tris, false, false);
561    }
562}