Skip to main content

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    /// Searches the tree with the greedy descent in [`Bvh2::find_sibling_greedy()`] to find a sibling
22    /// for the node being inserted, then attaches it there and refits back up to the root,
23    /// performing rotations along the way to help prevent the BVH from degenerating.
24    ///
25    /// This is the faster counterpart of [`Bvh2::insert_leaf_precise()`]. It doesn't need a traversal stack
26    /// and it tries to keep [`Bvh2::max_depth`] up to date, at the cost of sometimes picking a slightly worse sibling.
27    ///
28    /// # Returns
29    /// The index of the newly added node. Note that this is generally not `self.nodes.len() - 1`,
30    /// since a rotation on the refit walk can move the node that was just attached.
31    ///
32    /// # Arguments
33    /// * `new_node` - This node must be a leaf and already have a valid `first_index` into `primitive_indices`.
34    pub fn insert_leaf(&mut self, new_node: Bvh2Node) -> usize {
35        assert!(new_node.is_leaf());
36
37        if self.nodes.is_empty() {
38            self.nodes.push(new_node);
39            self.parents.clear();
40            self.parents.push(0);
41            return 0;
42        }
43
44        self.init_parents_if_uninit();
45
46        let (sibling_id, sibling_depth) = self.find_sibling_greedy(new_node.aabb());
47
48        // Grow by an adjacent pair at the end for the displaced sibling and the new node.
49        // The node count is always odd, so the first of the two is always a left sibling.
50        let left_id = self.nodes.len();
51        self.nodes.push(Default::default());
52        self.nodes.push(Default::default());
53        self.parents.push(0);
54        self.parents.push(0);
55
56        let new_node_id = self.attach_leaf(new_node, sibling_id, left_id);
57        self.update_max_depth_for_greedy_insertion(sibling_depth);
58
59        // Need to work up the tree updating the aabbs since we just added a node.
60        // A rotation along the way can move the node we just attached, so we track it.
61        self.refit_and_rotate_from_tracking_fast(sibling_id, new_node_id)
62    }
63
64    /// Removes and returns the leaf specified by `node_id`.
65    /// Puts `node_id` sibling in its parents place then moves the last two nodes into the now empty slots at `node_id`
66    /// and its sibling.
67    ///
68    /// Doesn't update the primitive_indices mapping. If this node is just going to be re-inserted again, nothing needs
69    /// to be done with primitive_indices, the mapping will still be valid. If this primitive needs to be removed
70    /// permanently see Bvh2::remove_primitive().
71    ///
72    /// # Arguments
73    /// * `node_id` - The index into self.nodes of the node that is to be removed
74    pub fn remove_leaf(&mut self, node_id: usize) -> Bvh2Node {
75        assert!(
76            !self.uses_spatial_splits,
77            "Removing leaves while using spatial splits is currently unsupported as it would require a mapping \
78from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
79        );
80
81        let node_to_remove = self.nodes[node_id];
82        assert!(node_to_remove.is_leaf());
83
84        if self.nodes.len() == 1 {
85            // Special case if the BVH is just a leaf
86            self.nodes.clear();
87            self.parents.clear();
88            self.primitives_to_nodes.clear();
89            return node_to_remove;
90        }
91
92        // if primitives_to_nodes has already been initialized
93        if !self.primitives_to_nodes.is_empty() {
94            // Invalidate primitives_to_nodes instances
95            for node_prim_id in
96                node_to_remove.first_index..node_to_remove.first_index + node_to_remove.prim_count
97            {
98                let direct_prim_id = self.primitive_indices[node_prim_id as usize];
99                self.primitives_to_nodes[direct_prim_id as usize] = INVALID;
100            }
101        }
102
103        let sibling_id = Bvh2Node::get_sibling_id(node_id);
104        debug_assert_eq!(self.parents[node_id], self.parents[sibling_id]); // Both children should already have the same parent.
105        let mut parent_id = self.parents[node_id] as usize;
106
107        // Put sibling in parent's place (parent doesn't exist anymore)
108        let sibling = self.nodes[sibling_id];
109        self.nodes[parent_id] = sibling;
110        // Tell the children (or primitives) of the moved sibling where it went.
111        self.relink_node(&sibling, parent_id);
112        // Don't need to update other parents here since the parent that was for this `parent_id` slot is now the direct
113        // parent of the moved sibling, and the parents of `node_id` and `sibling_id` are updated below.
114
115        // Now slots at both node_id and sibling_id are empty.
116        // Take the last two nodes "src" and put them in those now empty "dst" slots.
117        let end_nodes = node_id >= self.nodes.len() - 2;
118        if end_nodes {
119            // If these were already the last 2 nodes in the list we can just discard both.
120            self.nodes.pop().unwrap();
121            self.nodes.pop().unwrap();
122            self.parents.pop().unwrap();
123            self.parents.pop().unwrap();
124        } else {
125            let dst_left_id = Bvh2Node::get_left_sibling_id(node_id);
126            let dst_right_id = Bvh2Node::get_right_sibling_id(node_id);
127
128            let src_left_id = self.nodes.len() as u32 - 2;
129            let src_right_id = Bvh2Node::get_sibling_id32(src_left_id);
130            let src_right_parent = self.parents.pop().unwrap();
131            let src_left_parent = self.parents.pop().unwrap();
132
133            self.parents[dst_left_id] = src_left_parent;
134            self.parents[dst_right_id] = src_right_parent;
135
136            debug_assert_eq!(src_left_parent, src_right_parent); // Both children should already have the same parent.
137            let parent_of_relocated = &mut self.nodes[src_left_parent as usize];
138            debug_assert!(!parent_of_relocated.is_leaf());
139            debug_assert_eq!(parent_of_relocated.first_index, src_left_id);
140            debug_assert_eq!(parent_of_relocated.first_index + 1, src_right_id);
141            // Tell the actual parent of the nodes that are moving where they're going to be now.
142            self.nodes[src_left_parent as usize].first_index = dst_left_id as u32;
143
144            let right_src_sibling = self.nodes.pop().unwrap(); // Last node is right src sibling
145            self.nodes[dst_right_id] = right_src_sibling;
146            // Tell the children (or primitives) of the moved right_src_sibling where it went.
147            self.relink_node(&right_src_sibling, dst_right_id);
148
149            let left_src_sibling = self.nodes.pop().unwrap(); // Last node is left src sibling
150            self.nodes[dst_left_id] = left_src_sibling;
151            // Tell the children (or primitives) of the moved left_src_sibling where it went.
152            self.relink_node(&left_src_sibling, dst_left_id);
153
154            // If the to be removed node's parent was at the end of the array and has now moved update parent_id:
155            if parent_id as u32 == src_left_id {
156                parent_id = dst_left_id;
157            }
158            if parent_id as u32 == src_right_id {
159                parent_id = dst_right_id;
160            }
161        }
162
163        // Need to work up the tree updating the aabbs since we just removed a node.
164        self.refit_from_fast(parent_id);
165
166        self.children_are_ordered_after_parents = false;
167        // Return the removed node.
168        node_to_remove
169    }
170
171    /// Searches the tree with the greedy descent in [`Bvh2::find_sibling_greedy()`] to find a sibling
172    /// for the primitive being inserted, then attaches it there and refits back up to the root,
173    /// performing rotations along the way to help prevent the BVH from degenerating.
174    /// Updates [`Bvh2::primitive_indices`] and [`Bvh2::primitive_indices_freelist`].
175    ///
176    /// This is the faster counterpart of [`Bvh2::insert_primitive_precise()`]. It doesn't need a traversal stack
177    /// and it tries to keep [`Bvh2::max_depth`] up to date, at the cost of sometimes picking a slightly worse sibling.
178    ///
179    /// # Returns
180    /// The index of the newly added node.
181    ///
182    /// # Arguments
183    /// * `aabb` - The aabb of the primitive being inserted.
184    /// * `primitive_id` - The index of the primitive being inserted.
185    pub fn insert_primitive(&mut self, aabb: Aabb, primitive_id: u32) -> usize {
186        self.init_primitives_to_nodes_if_uninit();
187        self.init_parents_if_uninit();
188        if self.primitives_to_nodes.len() <= primitive_id as usize {
189            self.primitives_to_nodes
190                .resize(primitive_id as usize + 1, INVALID);
191        }
192        let first_index = if let Some(free_slot) = self.primitive_indices_freelist.pop() {
193            self.primitive_indices[free_slot as usize] = primitive_id;
194            free_slot
195        } else {
196            self.primitive_indices.push(primitive_id);
197            self.primitive_indices.len() as u32 - 1
198        };
199        let new_node_id = self.insert_leaf(Bvh2Node::new(aabb, 1, first_index));
200        self.primitives_to_nodes[primitive_id as usize] = new_node_id as u32;
201        new_node_id
202    }
203
204    /// Removes the leaf that contains the given primitive. Should be correct for nodes with multiple primitives per
205    /// leaf but faster for nodes with only one primitive per leaf, and will leave node aabb oversized.
206    /// Updates Bvh2::primitive_indices and Bvh2::primitive_indices_freelist.
207    ///
208    /// # Arguments
209    /// * `primitive_id` - The index of the primitive being removed.
210    pub fn remove_primitive(&mut self, primitive_id: u32) {
211        assert!(
212            !self.uses_spatial_splits,
213            "Removing primitives while using spatial splits is currently unsupported as it would require a mapping \
214from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
215        );
216        let remove_primitive_id = primitive_id;
217        self.init_parents_if_uninit();
218        self.init_primitives_to_nodes_if_uninit();
219
220        let node_id = self.primitives_to_nodes[remove_primitive_id as usize];
221
222        let node = &self.nodes[node_id as usize];
223        assert!(node.is_leaf());
224        if node.prim_count == 1 {
225            let removed_leaf = self.remove_leaf(node_id as usize);
226            self.primitive_indices_freelist
227                .push(removed_leaf.first_index);
228            self.primitive_indices[removed_leaf.first_index as usize] = INVALID;
229        } else {
230            // Update leaf with the remaining primitives, use the existing leftover space in primitive_indices and
231            // only add the removed primitive to the freelist
232
233            let node = &mut self.nodes[node_id as usize];
234
235            let start = node.first_index as usize;
236            let end = (node.first_index + node.prim_count) as usize;
237            let last = end - 1;
238            let mut spare_spot_id = start;
239            // Condense primitive_indices for this node.
240            for node_prim_id in start..end {
241                let direct_prim_id = self.primitive_indices[node_prim_id];
242                if direct_prim_id == remove_primitive_id {
243                    break;
244                }
245                spare_spot_id += 1;
246            }
247            if spare_spot_id < last {
248                self.primitive_indices[spare_spot_id] = self.primitive_indices[last];
249            }
250            // Free now open last position.
251            self.primitive_indices_freelist.push(last as u32);
252            self.primitive_indices[last] = INVALID;
253
254            assert!(node.prim_count > 1);
255            node.prim_count -= 1;
256        }
257
258        if self.primitives_to_nodes.len() > remove_primitive_id as usize {
259            self.primitives_to_nodes[remove_primitive_id as usize] = INVALID;
260        }
261    }
262
263    /// Attaches `new_node` to the BVH as the new sibling of the node at `sibling_id`.
264    ///
265    /// The new parent takes the sibling's slot, so nothing above `sibling_id` moves.
266    /// The displaced sibling and `new_node` are put in the adjacent pair of slots
267    /// at `left_id` and `left_id + 1`. Both of those slots have to already exist
268    /// and must not be reachable from the root.
269    ///
270    /// # Returns
271    /// The index of the newly attached node (always `left_id + 1`).
272    fn attach_leaf(&mut self, new_node: Bvh2Node, sibling_id: usize, left_id: usize) -> usize {
273        debug_assert!(Bvh2Node::is_left_sibling(left_id));
274
275        let new_node_id = left_id + 1;
276        let sibling = self.nodes[sibling_id];
277
278        // New parent goes in the sibling's position.
279        self.nodes[sibling_id] =
280            Bvh2Node::new(new_node.aabb().union(sibling.aabb()), 0, left_id as u32);
281        self.nodes[left_id] = sibling;
282        self.nodes[new_node_id] = new_node;
283        self.parents[left_id] = sibling_id as u32;
284        self.parents[new_node_id] = sibling_id as u32;
285
286        // Tell the children (or primitives) of the moved sibling where it went.
287        self.relink(left_id);
288
289        // if primitives_to_nodes has already been initialized
290        if !self.primitives_to_nodes.is_empty() {
291            // Tell primitives where their node went.
292            let end = new_node.first_index + new_node.prim_count;
293            if self.primitives_to_nodes.len() < end as usize {
294                // Since we are adding a primitive it's possible that primitives_to_nodes is not large enough yet.
295                self.primitives_to_nodes.resize(end as usize, INVALID);
296            }
297            update_primitives_to_nodes_for_node(
298                &new_node,
299                new_node_id,
300                &self.primitive_indices,
301                &mut self.primitives_to_nodes,
302            )
303        }
304
305        if left_id < sibling_id || (!sibling.is_leaf() && sibling.first_index as usize <= left_id) {
306            // The sibling was moved, but its children stayed where they were,
307            // so they may now come before their parent. The new pair may also
308            // come before the new parent when reusing freed slots.
309            self.children_are_ordered_after_parents = false;
310        }
311
312        new_node_id
313    }
314
315    /// Detaches the leaf at `node_id` without compacting `Bvh2::nodes`.
316    ///
317    /// The sibling takes the parent's slot, and the slots at `node_id` and its sibling then fall free.
318    /// They are unreachable from the root, so nothing can select one of them as an insertion sibling.
319    ///
320    /// Doesn't update the `primitive_indices` mapping, and leaves the `primitives_to_nodes` entries of the detached leaf
321    /// pointing at `node_id`. Both are still valid if the leaf is immediately reattached, see [`Bvh2::move_leaf()`].
322    ///
323    /// # Returns
324    /// The index of the left slot of the freed pair.
325    fn detach_leaf(&mut self, node_id: usize) -> usize {
326        let sibling_id = Bvh2Node::get_sibling_id(node_id);
327        debug_assert_eq!(
328            self.parents[node_id], self.parents[sibling_id],
329            "Both children should already have the same parent."
330        );
331        let parent_id = self.parents[node_id] as usize;
332
333        // Put sibling in parent's place (parent doesn't exist anymore)
334        self.nodes[parent_id] = self.nodes[sibling_id];
335        self.relink(parent_id);
336
337        // Need to work up the tree updating the aabbs since we just removed a node.
338        self.refit_from_fast(parent_id);
339
340        Bvh2Node::get_left_sibling_id(node_id)
341    }
342
343    /// Moves the leaf specified by `node_id` to a new position in the BVH, resizing it to `aabb`.
344    /// Refits back up to the root, performing rotations along the way to help prevent the BVH from degenerating.
345    ///
346    /// This is the fused equivalent of [`Bvh2::remove_leaf()`] followed by [`Bvh2::insert_leaf()`].
347    ///
348    /// `Bvh2::nodes.len()` is unchanged across the call and `Bvh2::primitive_indices` is untouched.
349    ///
350    /// # Returns
351    /// The index of the moved node.
352    ///
353    /// # Arguments
354    /// * `node_id` - The index into `self.nodes` of the leaf being moved.
355    /// * `aabb` - The new aabb of the leaf.
356    pub fn move_leaf(&mut self, node_id: usize, aabb: Aabb) -> usize {
357        assert!(
358            !self.uses_spatial_splits,
359            "Moving leaves while using spatial splits is currently unsupported as it would require a mapping \
360from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
361        );
362
363        let mut node = self.nodes[node_id];
364        assert!(node.is_leaf());
365        node.set_aabb(aabb);
366
367        if self.nodes.len() == 1 {
368            self.nodes[0] = node;
369            return 0;
370        }
371        debug_assert_ne!(node_id, 0);
372
373        self.init_parents_if_uninit();
374
375        let left_id = self.detach_leaf(node_id);
376        let (sibling_id, sibling_depth) = self.find_sibling_greedy(node.aabb());
377        let new_node_id = self.attach_leaf(node, sibling_id, left_id);
378        self.update_max_depth_for_greedy_insertion(sibling_depth);
379
380        // Need to work up the tree updating the aabbs since we just attached a node.
381        // A rotation along the way can move the node we just attached, so we track it.
382        self.refit_and_rotate_from_tracking_fast(sibling_id, new_node_id)
383    }
384
385    /// Moves the leaf that contains the given primitive to a new position in the BVH, resizing it to `aabb`.
386    /// Refits back up to the root, performing rotations along the way to help prevent the BVH from degenerating.
387    ///
388    /// This is the fused equivalent of [`Bvh2::remove_primitive()`] followed by [`Bvh2::insert_primitive()`].
389    /// The whole leaf is moved, so this requires that the leaf contains only this primitive.
390    ///
391    /// # Returns
392    /// The new index of the node of this primitive.
393    ///
394    /// # Arguments
395    /// * `aabb` - The new aabb of the primitive.
396    /// * `primitive_id` - The index of the primitive being moved.
397    pub fn move_primitive(&mut self, aabb: Aabb, primitive_id: u32) -> usize {
398        self.init_primitives_to_nodes_if_uninit();
399        self.init_parents_if_uninit();
400        let node_id = self.primitives_to_nodes[primitive_id as usize] as usize;
401        assert_eq!(
402            self.nodes[node_id].prim_count, 1,
403            "Bvh2::move_primitive() would move the other primitives in this leaf along with it."
404        );
405        let new_node_id = self.move_leaf(node_id, aabb);
406        debug_assert_eq!(
407            self.primitives_to_nodes[primitive_id as usize],
408            new_node_id as u32
409        );
410        new_node_id
411    }
412
413    /// Searches the tree recursively to find the best sibling for the primitive being inserted
414    /// (see [`Bvh2::insert_leaf_precise()`]). Updates [`Bvh2::primitive_indices`] and
415    /// [`Bvh2::primitive_indices_freelist`]. Refits back up to the root, performing rotations
416    /// along the way to help prevent the BVH from degenerating.
417    ///
418    /// See [`Bvh2::insert_primitive()`] for a faster version that descends a single path when choosing the sibling.
419    /// Unlike this method, [`Bvh2::insert_primitive()`] also tries to keep [`Bvh2::max_depth`] up to date.
420    ///
421    /// # Returns
422    /// The index of the newly added node. Note that this is generally not `self.nodes.len() - 1`,
423    /// since a rotation on the refit walk can move the node that was just attached.
424    ///
425    /// # Arguments
426    /// * `aabb` - The aabb of the primitive being inserted.
427    /// * `primitive_id` - The index of the primitive being inserted.
428    /// * `stack` - Used for the traversal stack. Needs to be large enough to initially accommodate traversal to the
429    ///   deepest leaf of the BVH. `insert_leaf_precise()` will resize this stack after traversal to be at least 2x the
430    ///   required size. This ends up being quite a bit faster than using a Vec and works well when inserting multiple
431    ///   nodes. But does require the user to provide a good initial guess. SiblingInsertionCandidate is tiny so be
432    ///   generous. Something like: `stack.reserve(bvh.depth(0) * 2).max(1000);` If you are inserting a lot of leaves
433    ///   don't call `bvh.depth(0)` with each leaf; just let `insert_leaf_precise()` resize the stack as needed.
434    pub fn insert_primitive_precise(
435        &mut self,
436        aabb: Aabb,
437        primitive_id: u32,
438        stack: &mut HeapStack<SiblingInsertionCandidate>,
439    ) -> usize {
440        self.init_primitives_to_nodes_if_uninit();
441        self.init_parents_if_uninit();
442        if self.primitives_to_nodes.len() <= primitive_id as usize {
443            self.primitives_to_nodes
444                .resize(primitive_id as usize + 1, INVALID);
445        }
446        let first_index = if let Some(free_slot) = self.primitive_indices_freelist.pop() {
447            self.primitive_indices[free_slot as usize] = primitive_id;
448            free_slot
449        } else {
450            self.primitive_indices.push(primitive_id);
451            self.primitive_indices.len() as u32 - 1
452        };
453        let new_node_id = self.insert_leaf_precise(Bvh2Node::new(aabb, 1, first_index), stack);
454        self.primitives_to_nodes[primitive_id as usize] = new_node_id as u32;
455        new_node_id
456    }
457
458    /// Searches the tree recursively to find the best sibling for the node being inserted. The best sibling is
459    /// classified as the sibling that if chosen it would increase the surface area of the BVH the least.
460    /// When the best sibling is found, a parent of both the sibling and the new node is put in the location of
461    /// the sibling and both the sibling and new node are added to the end of `Bvh2::nodes`.
462    /// Refits back up to the root, performing rotations along the way to help prevent the BVH from degenerating.
463    ///
464    /// See "Branch and Bound" <https://box2d.org/files/ErinCatto_DynamicBVH_Full.pdf> and
465    /// Jiˇrí Bittner et al. 2012 Fast Insertion-Based Optimization of Bounding Volume Hierarchies
466    ///
467    /// See [`Bvh2::insert_leaf()`] for a faster version that descends a single path when choosing the sibling.
468    /// Unlike this method, [`Bvh2::insert_leaf()`] also tries to keep [`Bvh2::max_depth`] up to date.
469    ///
470    /// # Returns
471    /// The index of the newly added node. Note that this is generally not `self.nodes.len() - 1`,
472    /// since a rotation on the refit walk can move the node that was just attached.
473    ///
474    /// # Arguments
475    /// * `new_node` - This node must be a leaf and already have a valid `first_index` into `primitive_indices`.
476    /// * `stack` - Used for the traversal stack. Needs to be large enough to initially accommodate traversal to the
477    ///   deepest leaf of the BVH. `insert_leaf_precise()` will resize this stack after traversal to be at least 2x the
478    ///   required size. This ends up being quite a bit faster than using a Vec and works well when inserting multiple
479    ///   nodes. But does require the user to provide a good initial guess. SiblingInsertionCandidate is tiny so be
480    ///   generous. Something like: `stack.reserve(bvh.depth(0) * 2).max(1000);` If you are inserting a lot of leaves
481    ///   don't call `bvh.depth(0)` with each leaf; just let `insert_leaf_precise()` resize the stack as needed.
482    pub fn insert_leaf_precise(
483        &mut self,
484        new_node: Bvh2Node,
485        stack: &mut HeapStack<SiblingInsertionCandidate>,
486    ) -> usize {
487        assert!(new_node.is_leaf());
488
489        if self.nodes.is_empty() {
490            self.nodes.push(new_node);
491            self.parents.clear();
492            self.parents.push(0);
493            return 0;
494        }
495
496        self.init_parents_if_uninit();
497
498        let mut min_cost = f32::MAX;
499        let mut best_sibling_candidate_id = 0;
500        let mut max_stack_len = 1;
501        let new_node_cost = new_node.aabb().half_area();
502
503        stack.clear();
504        let root_aabb = self.nodes[0].aabb();
505
506        // Traverse the BVH to find the best sibling
507        stack.push(SiblingInsertionCandidate {
508            inherited_cost: root_aabb.union(new_node.aabb()).half_area() - root_aabb.half_area(),
509            index: 0,
510        });
511        while let Some(sibling_candidate) = stack.pop() {
512            let current_node_index = sibling_candidate.index as usize;
513
514            let candidate = &self.nodes[current_node_index];
515
516            let direct_cost = candidate.aabb().union(new_node.aabb()).half_area();
517            let total_cost = direct_cost + sibling_candidate.inherited_cost;
518
519            if total_cost < min_cost {
520                min_cost = total_cost;
521                best_sibling_candidate_id = current_node_index;
522            }
523
524            // If this is not a leaf, it's possible a better cost could be found further down.
525            if !candidate.is_leaf() {
526                let inherited_cost = total_cost - candidate.aabb().half_area();
527                let min_subtree_cost = new_node_cost + inherited_cost;
528                if min_subtree_cost < min_cost {
529                    stack.push(SiblingInsertionCandidate {
530                        inherited_cost,
531                        index: candidate.first_index,
532                    });
533                    stack.push(SiblingInsertionCandidate {
534                        inherited_cost,
535                        index: candidate.first_index + 1,
536                    });
537                    max_stack_len = stack.len().max(max_stack_len);
538                }
539            }
540        }
541
542        if max_stack_len * 2 > stack.cap() {
543            stack.reserve(max_stack_len * 2);
544        }
545
546        let best_sibling_candidate = self.nodes[best_sibling_candidate_id];
547
548        // To avoid having gaps or re-arranging the BVH:
549        // The new parent goes in the sibling's position.
550        // The sibling and new node go on the end.
551        let new_sibling_id = self.nodes.len() as u32;
552        let new_parent = Bvh2Node::new(
553            new_node.aabb().union(best_sibling_candidate.aabb()),
554            0,
555            new_sibling_id,
556        );
557
558        // New parent goes in the sibling's position.
559        let new_parent_id = best_sibling_candidate_id;
560        self.nodes[new_parent_id] = new_parent;
561
562        self.nodes.push(best_sibling_candidate);
563        let new_node_id = self.nodes.len();
564        self.nodes.push(new_node); // Put the new node at the very end.
565        self.parents.push(new_parent_id as u32);
566        self.parents.push(new_parent_id as u32);
567
568        // Tell the children (or primitives) of the moved sibling where it went.
569        self.relink_node(&best_sibling_candidate, new_sibling_id as usize);
570
571        if !best_sibling_candidate.is_leaf() {
572            // The sibling was moved to the end of the node list, but its children stayed where they were,
573            // so they now come before their parent.
574            self.children_are_ordered_after_parents = false;
575        }
576
577        // if primitives_to_nodes has already been initialized
578        if !self.primitives_to_nodes.is_empty() {
579            // Tell primitives where their node went.
580            let end = new_node.first_index + new_node.prim_count;
581            if self.primitives_to_nodes.len() < end as usize {
582                // Since we are adding a primitive it's possible that primitives_to_nodes is not large enough yet.
583                self.primitives_to_nodes.resize(end as usize, INVALID);
584            }
585            update_primitives_to_nodes_for_node(
586                &new_node,
587                new_node_id,
588                &self.primitive_indices,
589                &mut self.primitives_to_nodes,
590            )
591        }
592
593        // Need to work up the tree updating the aabbs since we just added a node.
594        // A rotation along the way can move the node we just attached, so we track it.
595        self.refit_and_rotate_from_tracking_fast(new_parent_id, new_node_id)
596    }
597
598    /// Grows [`Bvh2::max_depth`] to account for a leaf that was just inserted below a sibling
599    /// at `sibling_depth`, where the root has a depth of 0.
600    ///
601    /// Note this is an upper bound estimate, not a strict bound. If the sibling is an inner node,
602    /// its whole subtree is pushed one level deeper too, and the greedy descent never visits that subtree,
603    /// so its height isn't known here.
604    #[inline]
605    fn update_max_depth_for_greedy_insertion(&mut self, sibling_depth: usize) {
606        // + 1 for the new leaf below the sibling
607        // + 1 because `max_depth` is a traversal stack size rather than a node depth
608        // + 1 more for a rotation that may push the new leaf one level deeper
609        self.max_depth = self.max_depth.max(sibling_depth + 3);
610    }
611
612    /// Searches the tree for the best sibling for a leaf with the given `aabb` using a greedy top-down descent.
613    /// The best sibling is classified as the sibling that if chosen it would increase the surface area of the BVH the least.
614    ///
615    /// Unlike the branch and bound search in [`Bvh2::insert_leaf_precise()`], this only ever descends a single path from the root,
616    /// always taking the child with the lower bound on what inserting beneath it could cost, and stopping as soon as
617    /// neither child's bound can beat the best candidate already found. This is O(depth), needs no traversal stack,
618    /// and doesn't blow up the same way as the BVH gets deeper. However, it tends to find a slightly worse sibling
619    /// than the branch and bound search.
620    ///
621    /// # Returns
622    /// The index of the best sibling found, and the depth it was found at (the root has a depth of 0).
623    ///
624    /// # Arguments
625    /// * `aabb` - The aabb of the leaf that is going to be inserted.
626    pub fn find_sibling_greedy(&self, aabb: &Aabb) -> (usize, usize) {
627        // This is based on `b2FindBestSibling` in Box2D by Erin Catto.
628
629        let center = aabb.center();
630        let area = aabb.half_area();
631
632        let root_aabb = self.nodes[0].aabb();
633
634        // Area of the node currently being descended into.
635        let mut area_base = root_aabb.half_area();
636
637        // Area of that node once it has been inflated to also contain the new leaf.
638        let mut direct_cost = root_aabb.union(aabb).half_area();
639
640        // How much every node from the root down to the current node would have to grow.
641        let mut inherited_cost = 0.0;
642
643        let mut best_cost = direct_cost;
644        let mut best_sibling_candidate_id = 0;
645        let mut best_sibling_candidate_depth = 0;
646
647        let mut current_node_index = 0;
648        let mut depth = 0;
649
650        // Descend the tree from the root, following a single greedy path.
651        while !self.nodes[current_node_index].is_leaf() {
652            let left_id = self.nodes[current_node_index].first_index as usize;
653            let right_id = left_id + 1;
654
655            // Cost of creating a new parent for this node and the new leaf.
656            let total_cost = direct_cost + inherited_cost;
657            if total_cost < best_cost {
658                best_cost = total_cost;
659                best_sibling_candidate_id = current_node_index;
660                best_sibling_candidate_depth = depth;
661            }
662
663            inherited_cost += direct_cost - area_base;
664
665            let left = &self.nodes[left_id];
666            let right = &self.nodes[right_id];
667            let left_is_leaf = left.is_leaf();
668            let right_is_leaf = right.is_leaf();
669            let left_aabb = left.aabb();
670            let right_aabb = right.aabb();
671            let left_direct_cost = left_aabb.union(aabb).half_area();
672            let right_direct_cost = right_aabb.union(aabb).half_area();
673
674            // Lower bound on the cost of choosing a sibling from the subtree rooted at each child.
675            // Left at f32::MAX for leaves since there is nothing below them to descend into.
676            let mut left_lower_cost = f32::MAX;
677            let mut right_lower_cost = f32::MAX;
678            let mut left_area = 0.0;
679            let mut right_area = 0.0;
680
681            if left_is_leaf {
682                let cost = left_direct_cost + inherited_cost;
683                if cost < best_cost {
684                    best_cost = cost;
685                    best_sibling_candidate_id = left_id;
686                    best_sibling_candidate_depth = depth + 1;
687                }
688            } else {
689                left_area = left_aabb.half_area();
690
691                // Lower bound cost of choosing a sibling from the subtree rooted at the left child.
692                //
693                // This is the minimum of:
694                //
695                // 1. Choosing the child itself: inherited_cost + left_direct_cost
696                // 2. Choosing one of its descendants: inherited_cost + (left_direct_cost - left_area) + area
697                left_lower_cost = inherited_cost + left_direct_cost + (area - left_area).min(0.0);
698            }
699
700            if right_is_leaf {
701                let cost = right_direct_cost + inherited_cost;
702                if cost < best_cost {
703                    best_cost = cost;
704                    best_sibling_candidate_id = right_id;
705                    best_sibling_candidate_depth = depth + 1;
706                }
707            } else {
708                right_area = right_aabb.half_area();
709
710                // See above for left_lower_cost.
711                right_lower_cost =
712                    inherited_cost + right_direct_cost + (area - right_area).min(0.0);
713            }
714
715            // Note: Box2D also has a separate early out if both children are leaves,
716            //       but because the costs for leaves are left as f32::MAX, this check
717            //       handles that too.
718            if best_cost <= left_lower_cost && best_cost <= right_lower_cost {
719                // Neither subtree can beat the best candidate already found.
720                break;
721            }
722
723            if left_lower_cost == right_lower_cost && !left_is_leaf {
724                // The bounds give no clear choice, which happens when both children
725                // fully contain `aabb`. Fall back to the child whose center is closest.
726                left_lower_cost = (left_aabb.center() - center).length_squared();
727                right_lower_cost = (right_aabb.center() - center).length_squared();
728            }
729
730            // Descend into the more promising child.
731            if left_lower_cost < right_lower_cost && !left_is_leaf {
732                current_node_index = left_id;
733                area_base = left_area;
734                direct_cost = left_direct_cost;
735            } else {
736                current_node_index = right_id;
737                area_base = right_area;
738                direct_cost = right_direct_cost;
739            }
740
741            depth += 1;
742        }
743
744        (best_sibling_candidate_id, best_sibling_candidate_depth)
745    }
746
747    /// Computes the cost of a potential rotation around the node at `node_id`,
748    /// and applies it if it improves the total cost of the two inner nodes below `node_id`.
749    ///
750    /// This is a local restructuring of the tree that swaps one of the two children of `node_id`
751    /// with a child of the other (a grandchild of `node_id`). Given a tree of the form:
752    ///
753    /// ```text
754    ///     node
755    ///    /    \
756    ///   b      c
757    ///  / \    / \
758    /// d   e  f   g
759    ///
760    /// ```
761    ///
762    /// where `node_id` refers to `node`, four rotations are considered: swapping `b` with `f`,
763    /// `b` with `g`, `c` with `d`, and `c` with `e`. The minimum cost of the four is computed,
764    /// and if it is less than the current cost of `b` and `c`, the rotation is applied.
765    ///
766    /// This incrementally recovers the quality of the BVH as leaves are inserted or the tree is refit,
767    /// and helps avoid degenerating depth.
768    ///
769    /// # Returns
770    /// The two slots whose contents were swapped, if a rotation was applied. Any node id held by the caller
771    /// that is one of the two needs to be updated to the other.
772    ///
773    /// # Arguments
774    /// * `node_id` - The index into `self.nodes` of the node to rotate around. Does nothing for leaves,
775    ///   or for nodes whose children are both leaves.
776    pub fn rotate_node(&mut self, node_id: usize) -> Option<(usize, usize)> {
777        // This is based on `b2RotateNodes` in Box2D by Erin Catto.
778
779        let node = self.nodes[node_id];
780        if node.is_leaf() {
781            return None;
782        }
783
784        let b_id = node.first_index as usize;
785        let c_id = b_id + 1;
786        let b = self.nodes[b_id];
787        let c = self.nodes[c_id];
788
789        let b_area = b.aabb().half_area();
790        let c_area = c.aabb().half_area();
791        let mut best_cost = b_area + c_area;
792
793        /// The two slots to swap, the node that ends up with a different pair of children,
794        /// and the aabb it ends up with.
795        struct RotationCandidate {
796            from: usize,
797            to: usize,
798            refit_id: usize,
799            refit_aabb: Aabb,
800        }
801
802        let mut best_rotation: Option<RotationCandidate> = None;
803
804        if !c.is_leaf() {
805            let f_id = c.first_index as usize;
806            let g_id = f_id + 1;
807            let f_aabb = self.nodes[f_id].aabb();
808            let g_aabb = self.nodes[g_id].aabb();
809
810            // Cost of swapping b and f, which leaves c covering b and g.
811            //
812            //       node
813            //      /    \
814            //     f      c
815            //           / \
816            //          b   g
817            //         / \
818            //        d   e
819            let aabb_bg = b.aabb().union(g_aabb);
820            let cost_bf = b_area + aabb_bg.half_area();
821            if cost_bf < best_cost {
822                best_cost = cost_bf;
823                best_rotation = Some(RotationCandidate {
824                    from: b_id,
825                    to: f_id,
826                    refit_id: c_id,
827                    refit_aabb: aabb_bg,
828                });
829            }
830
831            // Cost of swapping b and g, which leaves c covering b and f.
832            //
833            //       node
834            //      /    \
835            //     g      c
836            //           / \
837            //          f   b
838            //             / \
839            //            d   e
840            let aabb_bf = b.aabb().union(f_aabb);
841            let cost_bg = b_area + aabb_bf.half_area();
842            if cost_bg < best_cost {
843                best_cost = cost_bg;
844                best_rotation = Some(RotationCandidate {
845                    from: b_id,
846                    to: g_id,
847                    refit_id: c_id,
848                    refit_aabb: aabb_bf,
849                });
850            }
851        }
852
853        if !b.is_leaf() {
854            let d_id = b.first_index as usize;
855            let e_id = d_id + 1;
856            let d_aabb = self.nodes[d_id].aabb();
857            let e_aabb = self.nodes[e_id].aabb();
858
859            // Cost of swapping c and d, which leaves b covering c and e.
860            //
861            //       node
862            //      /    \
863            //     b      d
864            //    / \
865            //   c   e
866            //  / \
867            // f   g
868            let aabb_ce = c.aabb().union(e_aabb);
869            let cost_cd = c_area + aabb_ce.half_area();
870            if cost_cd < best_cost {
871                best_cost = cost_cd;
872                best_rotation = Some(RotationCandidate {
873                    from: c_id,
874                    to: d_id,
875                    refit_id: b_id,
876                    refit_aabb: aabb_ce,
877                });
878            }
879
880            // Cost of swapping c and e, which leaves b covering c and d.
881            //
882            //       node
883            //      /    \
884            //     b      e
885            //    / \
886            //   d   c
887            //      / \
888            //     f   g
889            let aabb_cd = c.aabb().union(d_aabb);
890            let cost_ce = c_area + aabb_cd.half_area();
891            if cost_ce < best_cost {
892                // Last candidate, so best_cost doesn't need to be updated.
893                best_rotation = Some(RotationCandidate {
894                    from: c_id,
895                    to: e_id,
896                    refit_id: b_id,
897                    refit_aabb: aabb_cd,
898                });
899            }
900        }
901
902        let Some(RotationCandidate {
903            from,
904            to,
905            refit_id,
906            refit_aabb,
907        }) = best_rotation
908        else {
909            // No rotation improves the cost.
910            return None;
911        };
912
913        // Box2D reassigns child pointers, but obvhs stores children as an adjacent pair.
914        // Swapping the contents of the two slots has the same structural effect.
915        self.nodes.swap(from, to);
916        self.relink(from);
917        self.relink(to);
918
919        // Only the node that took on a different pair of children needs its aabb recomputed.
920        self.nodes[refit_id].set_aabb(refit_aabb);
921
922        self.children_are_ordered_after_parents = false;
923
924        Some((from, to))
925    }
926
927    /// Refit the BVH working up the tree from this node, ignoring leaves,
928    /// performing rotations at each node along the way where beneficial.
929    ///
930    /// Halts once two consecutive levels needed neither a resize nor a rotation.
931    /// Panics in debug if some parent still needed to be resized.
932    ///
933    /// This can only be used to refit when a single node has changed or moved.
934    ///
935    /// See [`Bvh2::rotate_node()`] and [`Bvh2::refit_from()`].
936    pub fn refit_and_rotate_from_fast(&mut self, index: usize) {
937        self.refit_and_rotate_from_tracking_fast(index, INVALID as usize);
938    }
939
940    /// Same as [`Bvh2::refit_and_rotate_from_fast()`], but follows the node at `tracked_node_id`
941    /// through any rotations applied along the way.
942    ///
943    /// # Returns
944    /// The index that the tracked node ended up at.
945    pub fn refit_and_rotate_from_tracking_fast(
946        &mut self,
947        mut index: usize,
948        mut tracked_node_id: usize,
949    ) -> usize {
950        self.init_parents_if_uninit();
951        let mut unchanged_count = 0;
952        #[cfg(debug_assertions)]
953        let mut stopped = false;
954        loop {
955            let node = self.nodes[index];
956            if !node.is_leaf() {
957                let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
958                let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
959                let new_aabb = first_child_bbox.union(&second_child_bbox);
960
961                let aabb_unchanged = self.nodes[index].aabb() == &new_aabb;
962
963                self.nodes[index].set_aabb(new_aabb);
964
965                #[cfg(debug_assertions)]
966                if stopped {
967                    // Debug builds keep going to check that nothing above actually needed refitting,
968                    // but it can't perform any more rotations so that `tracked_node_id` is stable.
969                    debug_assert!(
970                        aabb_unchanged,
971                        "Some parents still needed refitting. Unideal fitting is occurring somewhere."
972                    );
973                    if index == 0 {
974                        break;
975                    }
976                    index = self.parents[index] as usize;
977                    continue;
978                }
979
980                // A rotation only swaps nodes below `index`, so the computed aabb stays valid.
981                let rotation = self.rotate_node(index);
982                if let Some((from, to)) = rotation {
983                    if tracked_node_id == from {
984                        tracked_node_id = to;
985                    } else if tracked_node_id == to {
986                        tracked_node_id = from;
987                    }
988                }
989
990                // Two consecutive levels that didn't need a resize or a rotation mean that
991                // the ancestors above see no change in their children's or grandchildren's aabbs,
992                // which is what rotation candidates are based on, so we can stop early.
993                //
994                // Note that they could still have rotations that have not been evaluated,
995                // so this is a trade-off.
996                if aabb_unchanged && rotation.is_none() {
997                    unchanged_count += 1;
998                    if unchanged_count == 2 {
999                        #[cfg(not(debug_assertions))]
1000                        return tracked_node_id;
1001                        #[cfg(debug_assertions)]
1002                        {
1003                            stopped = true;
1004                        }
1005                    }
1006                } else {
1007                    unchanged_count = 0;
1008                }
1009            }
1010            if index == 0 {
1011                break;
1012            }
1013            index = self.parents[index] as usize;
1014        }
1015        tracked_node_id
1016    }
1017}
1018
1019/// Slow at building, makes a slow bvh, just for testing insertion.
1020/// Can result in very deep BVHs in some cases.
1021///
1022/// Dramatically slower at building than ploc. Rotations keep the tree from degenerating
1023/// as primitives are inserted.
1024#[doc(hidden)]
1025pub fn build_bvh2_by_precise_insertion<T: Boundable>(primitives: &[T]) -> Bvh2 {
1026    let mut bvh = Bvh2::default();
1027
1028    let mut stack = HeapStack::new_with_capacity(1000);
1029
1030    for prim_id in 0..primitives.len() {
1031        bvh.insert_primitive_precise(primitives[prim_id].aabb(), prim_id as u32, &mut stack);
1032    }
1033
1034    // Update max depth for validate
1035    if !bvh.nodes.is_empty() {
1036        bvh.max_depth = (bvh.depth(0) + 1).max(DEFAULT_MAX_STACK_DEPTH);
1037    }
1038
1039    #[cfg(debug_assertions)]
1040    {
1041        bvh.validate(primitives, false, true);
1042    }
1043
1044    bvh
1045}
1046
1047/// Same as [`build_bvh2_by_precise_insertion()`], but with the faster greedy sibling search,
1048/// just for testing insertion.
1049///
1050/// Still slow at building compared to ploc, and can result in lower quality trees
1051/// than [`build_bvh2_by_precise_insertion()`]. Rotations keep the tree from degenerating
1052/// as primitives are inserted.
1053#[doc(hidden)]
1054pub fn build_bvh2_by_insertion<T: Boundable>(primitives: &[T]) -> Bvh2 {
1055    let mut bvh = Bvh2::default();
1056
1057    for prim_id in 0..primitives.len() {
1058        bvh.insert_primitive(primitives[prim_id].aabb(), prim_id as u32);
1059    }
1060
1061    #[cfg(debug_assertions)]
1062    {
1063        bvh.validate(primitives, false, true);
1064    }
1065
1066    bvh
1067}
1068
1069/// Just here to for testing/benchmarking/validating leaf removal and insertion. See reinsertion.rs if you want to
1070/// optimize a BVH2. This currently tends to make a good BVH slower despite applying rotations during insertion.
1071#[doc(hidden)]
1072pub fn slow_leaf_reinsertion(bvh: &mut Bvh2) {
1073    let mut stack = HeapStack::new_with_capacity(1000);
1074    for node_id in 1..bvh.nodes.len() {
1075        if bvh.nodes.len() <= node_id {
1076            break;
1077        }
1078        if bvh.nodes[node_id].is_leaf() {
1079            // Assert that the parent of this node is not a leaf (a parent could never be a leaf)
1080            assert!(!bvh.nodes[bvh.parents[node_id] as usize].is_leaf());
1081            // If the node is a leaf, remove it
1082            let removed_leaf = bvh.remove_leaf(node_id);
1083            // Insert it again, maybe it will find a better spot
1084            bvh.insert_leaf_precise(removed_leaf, &mut stack);
1085        }
1086    }
1087    #[cfg(debug_assertions)]
1088    {
1089        bvh.validate_parents();
1090    }
1091}
1092
1093#[cfg(test)]
1094mod tests {
1095    use std::time::Duration;
1096
1097    use glam::vec3a;
1098
1099    use super::*;
1100    use crate::{BvhBuildParams, bvh2::builder::build_bvh2, test_util::geometry::demoscene};
1101
1102    /// `count` unit boxes laid out in sorted order along a line.
1103    /// This is the input that degenerates a rotation-free insertion build into a list.
1104    fn sorted_boxes(count: usize) -> Vec<Aabb> {
1105        (0..count)
1106            .map(|i| {
1107                let center = vec3a(i as f32, 0.0, 0.0);
1108                Aabb::new(center - 0.5, center + 0.5)
1109            })
1110            .collect()
1111    }
1112
1113    #[test]
1114    fn build_by_precise_insertion() {
1115        for res in 30..=32 {
1116            let tris = demoscene(res, 0);
1117            let bvh = build_bvh2_by_precise_insertion(&tris);
1118            assert_eq!(bvh.active_primitive_indices_count(), tris.len());
1119            assert_eq!(bvh.nodes.len(), tris.len() * 2 - 1);
1120            assert_eq!(bvh.primitives_to_nodes.len(), tris.len());
1121            bvh.validate(&tris, false, false);
1122        }
1123    }
1124
1125    #[test]
1126    fn build_by_insertion() {
1127        for res in 30..=32 {
1128            let tris = demoscene(res, 0);
1129            let bvh = build_bvh2_by_insertion(&tris);
1130            bvh.validate(&tris, false, true);
1131        }
1132    }
1133
1134    #[test]
1135    fn test_slow_leaf_reinsertion() {
1136        for res in 30..=32 {
1137            let tris = demoscene(res, 0);
1138
1139            let mut bvh = build_bvh2(
1140                &tris,
1141                BvhBuildParams::medium_build(),
1142                &mut Duration::default(),
1143            );
1144            bvh.init_primitives_to_nodes_if_uninit();
1145            bvh.init_parents_if_uninit();
1146            slow_leaf_reinsertion(&mut bvh);
1147            bvh.validate(&tris, false, false);
1148            bvh.reorder_in_stack_traversal_order();
1149            bvh.validate(&tris, false, false);
1150        }
1151    }
1152
1153    #[test]
1154    fn remove_all_primitives() {
1155        let tris = demoscene(16, 0);
1156
1157        // Test with both a bvh that only has one primitive per leaf
1158        // and also with one that has multiple primitives per leaf.
1159        let bvh1 = build_bvh2(
1160            &tris,
1161            BvhBuildParams::fastest_build(),
1162            &mut Duration::default(),
1163        );
1164        let bvh2 = build_bvh2(
1165            &tris,
1166            BvhBuildParams::medium_build(),
1167            &mut Duration::default(),
1168        );
1169
1170        let mut found_leaf_with_multiple_nodes = false;
1171        for node in &bvh2.nodes {
1172            if node.prim_count > 1 {
1173                found_leaf_with_multiple_nodes = true;
1174                break;
1175            }
1176        }
1177        if !found_leaf_with_multiple_nodes {
1178            panic!(
1179                "Test remove_all_primitives bvh2 should have some nodes that contain multiple primitives"
1180            );
1181        }
1182
1183        for bvh in &mut [bvh1, bvh2] {
1184            bvh.init_primitives_to_nodes_if_uninit();
1185            bvh.init_parents_if_uninit();
1186            bvh.validate(&tris, false, false);
1187
1188            for primitive_id in 0..tris.len() as u32 {
1189                bvh.remove_primitive(primitive_id);
1190                bvh.validate(&tris, false, false);
1191            }
1192
1193            assert_eq!(bvh.nodes.len(), 0);
1194            assert_eq!(bvh.parents.len(), 0);
1195            assert_eq!(bvh.primitives_to_nodes.len(), 0);
1196            bvh.validate(&tris, false, false);
1197        }
1198    }
1199
1200    #[test]
1201    fn remove_and_insert_all_primitives() {
1202        let tris = demoscene(16, 0);
1203
1204        let mut bvh = build_bvh2(
1205            &tris,
1206            BvhBuildParams::medium_build(),
1207            &mut Duration::default(),
1208        );
1209        bvh.init_primitives_to_nodes_if_uninit();
1210        bvh.init_parents_if_uninit();
1211        bvh.validate(&tris, false, false);
1212
1213        let mut stack = HeapStack::new_with_capacity(1000);
1214
1215        for primitive_id in 0..tris.len() as u32 {
1216            bvh.remove_primitive(primitive_id);
1217            bvh.validate(&tris, false, false);
1218        }
1219
1220        for primitive_id in 0..tris.len() as u32 {
1221            bvh.insert_primitive_precise(
1222                tris[primitive_id as usize].aabb(),
1223                primitive_id,
1224                &mut stack,
1225            );
1226            bvh.validate_primitives_to_nodes();
1227        }
1228
1229        bvh.validate(&tris, false, false);
1230    }
1231
1232    #[test]
1233    fn insert_leaf_precise_clears_children_are_ordered_after_parents() {
1234        // Regression test
1235        //
1236        // If `Bvh2::children_are_ordered_after_parents` isn't cleared when inserting primitives,
1237        // `Bvh2::refit_all()` takes the fast path, and computes some parents from children
1238        // it hasn't updated yet, leaving nodes that don't fit their children.
1239
1240        let tris = demoscene(16, 0);
1241
1242        let mut bvh = build_bvh2(
1243            &tris,
1244            BvhBuildParams::medium_build(),
1245            &mut Duration::default(),
1246        );
1247        bvh.init_primitives_to_nodes_if_uninit();
1248        bvh.init_parents_if_uninit();
1249
1250        // Remove and re-insert some primitives.
1251        let mut stack = HeapStack::new_with_capacity(1000);
1252        let primitive_ids = (0..tris.len() as u32).step_by(32).collect::<Vec<_>>();
1253
1254        for &primitive_id in &primitive_ids {
1255            bvh.remove_primitive(primitive_id);
1256        }
1257
1258        // Removal clears `children_are_ordered_after_parents`, so we restore the ordering
1259        // here so that we can test that insertion clears it again.
1260        bvh.reorder_in_stack_traversal_order();
1261        assert!(bvh.children_are_ordered_after_parents);
1262
1263        for &primitive_id in &primitive_ids {
1264            let mut aabb = tris[primitive_id as usize].aabb();
1265            aabb.min -= 0.05;
1266            aabb.max += 0.05;
1267            bvh.insert_primitive_precise(aabb, primitive_id, &mut stack);
1268        }
1269
1270        bvh.max_depth = (bvh.depth(0) + 1).max(DEFAULT_MAX_STACK_DEPTH);
1271
1272        bvh.validate(&tris, false, false);
1273
1274        // Grow every leaf, then refit the whole bvh.
1275        for node in &mut bvh.nodes {
1276            if node.is_leaf() {
1277                let mut aabb = *node.aabb();
1278                aabb.max += 0.1;
1279                node.set_aabb(aabb);
1280            }
1281        }
1282        bvh.refit_all();
1283
1284        // Every inner node should tightly fit its children.
1285        for (node_id, node) in bvh.nodes.iter().enumerate() {
1286            if !node.is_leaf() {
1287                let children_aabb = bvh.nodes[node.first_index as usize]
1288                    .aabb()
1289                    .union(bvh.nodes[node.first_index as usize + 1].aabb());
1290                assert_eq!(
1291                    *node.aabb(),
1292                    children_aabb,
1293                    "node {node_id} does not fit its children after refit_all()"
1294                );
1295            }
1296        }
1297    }
1298
1299    #[test]
1300    fn insertion_maintains_max_depth() {
1301        // The greedy descent knows the depth of the sibling it picked, so insertion can keep `max_depth` up to date.
1302        // Bvh2::validate() requires it to be a valid stack size, so a stale `max_depth` would fail the validation below.
1303        let tris = demoscene(32, 0);
1304        let bvh = build_bvh2_by_insertion(&tris);
1305
1306        assert!(
1307            bvh.depth(0) <= bvh.max_depth,
1308            "max_depth ({}) does not cover the actual bvh depth ({})",
1309            bvh.max_depth,
1310            bvh.depth(0)
1311        );
1312        bvh.validate(&tris, false, true);
1313    }
1314
1315    #[test]
1316    fn insertion_rotations_prevent_degenerate_bvhs() {
1317        // Without rotations, this sorted input degenerates either insertion method into a list.
1318        let boxes = sorted_boxes(1024);
1319
1320        let mut greedy_bvh = Bvh2::default();
1321        for (prim_id, aabb) in boxes.iter().enumerate() {
1322            greedy_bvh.insert_primitive(*aabb, prim_id as u32);
1323        }
1324        greedy_bvh.validate(&boxes, false, true);
1325
1326        let mut precise_bvh = Bvh2::default();
1327        let mut stack = HeapStack::new_with_capacity(1000);
1328        for (prim_id, aabb) in boxes.iter().enumerate() {
1329            let node_id = precise_bvh.insert_primitive_precise(*aabb, prim_id as u32, &mut stack);
1330            assert_eq!(precise_bvh.primitives_to_nodes[prim_id], node_id as u32);
1331        }
1332        precise_bvh.validate(&boxes, false, true);
1333
1334        for bvh in [&greedy_bvh, &precise_bvh] {
1335            let depth = bvh.depth(0);
1336            assert!(
1337                depth * 8 < boxes.len(),
1338                "rotations should keep the BVH far shallower than a list, got depth {depth}"
1339            );
1340        }
1341    }
1342
1343    #[test]
1344    fn rotate_node_preserves_aabbs() {
1345        let tris = demoscene(24, 0);
1346
1347        let mut bvh = build_bvh2(
1348            &tris,
1349            BvhBuildParams::fastest_build(),
1350            &mut Duration::default(),
1351        );
1352        bvh.init_primitives_to_nodes_if_uninit();
1353        bvh.init_parents_if_uninit();
1354
1355        for node_id in 0..bvh.nodes.len() {
1356            let aabb = *bvh.nodes[node_id].aabb();
1357            bvh.rotate_node(node_id);
1358            assert_eq!(
1359                *bvh.nodes[node_id].aabb(),
1360                aabb,
1361                "rotating node {node_id} changed its aabb"
1362            );
1363            bvh.validate(&tris, false, true);
1364        }
1365    }
1366
1367    #[test]
1368    fn move_all_primitives() {
1369        let tris = demoscene(16, 0);
1370
1371        let mut bvh = build_bvh2(
1372            &tris,
1373            BvhBuildParams::fastest_build(),
1374            &mut Duration::default(),
1375        );
1376        bvh.init_primitives_to_nodes_if_uninit();
1377        bvh.init_parents_if_uninit();
1378        bvh.validate(&tris, false, false);
1379
1380        let node_count = bvh.nodes.len();
1381        let primitive_indices = bvh.primitive_indices.clone();
1382
1383        // Move every primitive to a grown aabb, then back to its original one.
1384        for primitive_id in 0..tris.len() as u32 {
1385            let mut aabb = tris[primitive_id as usize].aabb();
1386            aabb.min -= 0.05;
1387            aabb.max += 0.05;
1388            bvh.move_primitive(aabb, primitive_id);
1389            assert_eq!(bvh.nodes.len(), node_count);
1390            bvh.validate_parents();
1391            bvh.validate_primitives_to_nodes();
1392        }
1393        for primitive_id in 0..tris.len() as u32 {
1394            bvh.move_primitive(tris[primitive_id as usize].aabb(), primitive_id);
1395            assert_eq!(bvh.nodes.len(), node_count);
1396        }
1397
1398        assert_eq!(bvh.primitive_indices, primitive_indices);
1399        bvh.validate(&tris, false, true);
1400    }
1401
1402    #[test]
1403    fn move_leaf_with_single_node_bvh() {
1404        let tris = demoscene(16, 0);
1405
1406        let mut bvh = Bvh2::default();
1407        bvh.insert_primitive(tris[0].aabb(), 0);
1408        assert_eq!(bvh.nodes.len(), 1);
1409
1410        let mut aabb = tris[0].aabb();
1411        aabb.max += 1.0;
1412        assert_eq!(bvh.move_leaf(0, aabb), 0);
1413        assert_eq!(bvh.nodes.len(), 1);
1414        assert_eq!(*bvh.nodes[0].aabb(), aabb);
1415
1416        bvh.insert_primitive(tris[1].aabb(), 1);
1417        bvh.move_primitive(tris[0].aabb(), 0);
1418        bvh.validate(&tris[0..2], false, true);
1419    }
1420}