parry3d/partitioning/bvh/
bvh_tree.rs

1use super::BvhOptimizationHeapEntry;
2use crate::bounding_volume::{Aabb, BoundingVolume};
3use crate::math::{Point, Real, Vector};
4use crate::query::{Ray, RayCast};
5use crate::utils::VecMap;
6use alloc::collections::{BinaryHeap, VecDeque};
7use alloc::vec::Vec;
8use core::ops::{Deref, DerefMut, Index, IndexMut};
9
10/// The strategy for one-time build of the tree.
11///
12/// For general-purpose usage [`BvhBuildStrategy::Binned`] is recommended. If the focus is
13/// ray-casting, [`BvhBuildStrategy::Ploc`] is recommended.
14#[derive(Default, Clone, Debug, Copy, PartialEq, Eq)]
15pub enum BvhBuildStrategy {
16    /// The tree is built using the binned strategy.
17    ///
18    /// This implements the strategy from "On fast Construction of SAH-based Bounding Volume Hierarchies", Ingo Ward.
19    /// This is **not** parallelized yet.
20    #[default]
21    Binned,
22    /// The tree is built using the Locally-Ordered Clustering technique.
23    ///
24    /// This implements the strategy from "Parallel Locally-Ordered Clustering for Bounding Volume Hierarchy Construction", Meister, Bittner.
25    /// This is **not** parallelized yet.
26    Ploc,
27}
28
29/// Workspace data for various operations on the tree.
30///
31/// This is all temporary data that can be freed at any time without affecting results.
32/// The main reason to reuse the same instance of this over time is to lower costs of internal
33/// allocations.
34#[derive(Clone, Default)]
35pub struct BvhWorkspace {
36    pub(super) refit_tmp: BvhNodeVec,
37    pub(super) rebuild_leaves: Vec<BvhNode>,
38    pub(super) rebuild_frame_index: u32,
39    pub(super) rebuild_start_index: u32,
40    pub(super) optimization_roots: Vec<u32>,
41    pub(super) queue: BinaryHeap<BvhOptimizationHeapEntry>,
42    pub(super) dequeue: VecDeque<u32>,
43    pub(super) traversal_stack: Vec<u32>,
44}
45
46/// A piece of data packing state flags as well as leaf counts for a BVH tree node.
47#[derive(Default, Copy, Clone, Debug)]
48#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
49#[cfg_attr(
50    feature = "rkyv",
51    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
52    archive(check_bytes)
53)]
54#[repr(transparent)]
55pub struct BvhNodeData(u32);
56const CHANGED: u32 = 0b01;
57const CHANGE_PENDING: u32 = 0b11;
58
59impl BvhNodeData {
60    #[inline(always)]
61    pub(super) fn with_leaf_count_and_pending_change(leaf_count: u32) -> Self {
62        Self(leaf_count | (CHANGE_PENDING << 30))
63    }
64
65    #[inline(always)]
66    pub(super) fn leaf_count(self) -> u32 {
67        self.0 & 0x3fff_ffff
68    }
69
70    #[inline(always)]
71    pub(super) fn is_changed(self) -> bool {
72        self.0 >> 30 == CHANGED
73    }
74
75    #[inline(always)]
76    pub(super) fn is_change_pending(self) -> bool {
77        self.0 >> 30 == CHANGE_PENDING
78    }
79
80    #[inline(always)]
81    pub(super) fn add_leaf_count(&mut self, added: u32) {
82        self.0 += added;
83    }
84
85    #[inline(always)]
86    pub(super) fn set_change_pending(&mut self) {
87        self.0 |= CHANGE_PENDING << 30;
88    }
89
90    #[inline(always)]
91    pub(super) fn resolve_pending_change(&mut self) {
92        if self.is_change_pending() {
93            *self = Self((self.0 & 0x3fff_ffff) | (CHANGED << 30));
94        } else {
95            *self = Self(self.0 & 0x3fff_ffff);
96        }
97    }
98
99    pub(super) fn merged(self, other: Self) -> Self {
100        let leaf_count = self.leaf_count() + other.leaf_count();
101        let changed = (self.0 >> 30) | (other.0 >> 30);
102        Self(leaf_count | changed << 30)
103    }
104}
105
106/// A pair of tree nodes.
107///
108/// Both `left` and `right` are guaranteed to be valid except for the only special-case where the
109/// tree contains only a single leaf, in which case only `left` is valid. But in every other
110/// cases where the tree contains at least 2 leaves, booth `left` and `right` are guaranteed
111/// to be valid.
112#[derive(Copy, Clone, Debug)]
113#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
114#[cfg_attr(
115    feature = "rkyv",
116    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
117    archive(check_bytes)
118)]
119#[repr(C)]
120// PERF: the size of this struct is 64 bytes but has a default alignment of 16 (in f32 + 3d + simd mode).
121//       Forcing an alignment of 64 won’t add padding, and makes aligns it with most cache lines.
122#[cfg_attr(all(feature = "dim3", feature = "f32"), repr(align(64)))]
123pub struct BvhNodeWide {
124    pub(super) left: BvhNode,
125    pub(super) right: BvhNode,
126}
127
128// NOTE: if this assertion fails with a weird "0 - 1 would overflow" error, it means the equality doesn’t hold.
129#[cfg(all(feature = "dim3", feature = "f32"))]
130static_assertions::const_assert_eq!(align_of::<BvhNodeWide>(), 64);
131#[cfg(all(feature = "dim3", feature = "f32"))]
132static_assertions::assert_eq_size!(BvhNodeWide, [u8; 64]);
133
134impl BvhNodeWide {
135    #[inline(always)]
136    pub fn zeros() -> Self {
137        Self {
138            left: BvhNode::zeros(),
139            right: BvhNode::zeros(),
140        }
141    }
142
143    /// The two nodes in `self` seen as an array.
144    ///
145    /// Useful for accessing the nodes by index.
146    #[inline(always)]
147    pub fn as_array(&self) -> [&BvhNode; 2] {
148        [&self.left, &self.right]
149    }
150
151    /// The two nodes in `self` seen as an array of mutable references.
152    ///
153    /// Useful for accessing the nodes mutable by index.
154    #[inline(always)]
155    pub fn as_array_mut(&mut self) -> [&mut BvhNode; 2] {
156        [&mut self.left, &mut self.right]
157    }
158
159    /// Merges both nodes contained by `self` to form its parent.
160    pub fn merged(&self, my_id: u32) -> BvhNode {
161        self.left.merged(&self.right, my_id)
162    }
163
164    /// The sum of leaves contained by both nodes in `self`.
165    pub fn leaf_count(&self) -> u32 {
166        self.left.leaf_count() + self.right.leaf_count()
167    }
168}
169
170#[repr(C)] // SAFETY: needed to ensure SIMD aabb checks rely on the layout.
171#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
172pub(super) struct BvhNodeSimd {
173    mins: glam::Vec3A,
174    maxs: glam::Vec3A,
175}
176
177// SAFETY: compile-time assertions to ensure we can transmute between `BvhNode` and `BvhNodeSimd`.
178#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
179static_assertions::assert_eq_align!(BvhNode, BvhNodeSimd);
180#[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
181static_assertions::assert_eq_size!(BvhNode, BvhNodeSimd);
182
183/// The note (internal or leaf) of a BVH.
184#[derive(Copy, Clone, Debug)]
185#[repr(C)] // SAFETY: needed to ensure SIMD aabb checks rely on the layout.
186#[cfg_attr(all(feature = "f32", feature = "dim3"), repr(align(16)))]
187#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
188#[cfg_attr(
189    feature = "rkyv",
190    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
191    archive(check_bytes)
192)]
193pub struct BvhNode {
194    /// Mins coordinates of the node’s bounding volume.
195    pub(super) mins: Point<Real>,
196    /// Children of this node. A node has either 0 (i.e. it’s a leaf) or 2 children.
197    ///
198    /// If [`Self::leaf_count`] is 1, then the node has 0 children and is a leaf.
199    pub(super) children: u32,
200    /// Maxs coordinates of this node’s bounding volume.
201    pub(super) maxs: Point<Real>,
202    /// Packed data associated to this node (leaf count and flags).
203    pub(super) data: BvhNodeData,
204}
205
206impl BvhNode {
207    #[inline(always)]
208    pub(super) fn zeros() -> Self {
209        Self {
210            mins: Point::origin(),
211            children: 0,
212            maxs: Point::origin(),
213            data: BvhNodeData(0),
214        }
215    }
216
217    /// Initialized a leaf.
218    #[inline(always)]
219    pub fn leaf(aabb: Aabb, leaf_data: u32) -> BvhNode {
220        Self {
221            mins: aabb.mins,
222            maxs: aabb.maxs,
223            children: leaf_data,
224            data: BvhNodeData::with_leaf_count_and_pending_change(1),
225        }
226    }
227
228    /// If this node is a leaf, returns its associated index provided at construction time.
229    #[inline(always)]
230    pub fn leaf_data(&self) -> Option<u32> {
231        self.is_leaf().then_some(self.children)
232    }
233
234    /// Is this node a leaf?
235    #[inline(always)]
236    pub fn is_leaf(&self) -> bool {
237        self.leaf_count() == 1
238    }
239
240    #[inline(always)]
241    pub(super) fn leaf_count(&self) -> u32 {
242        self.data.leaf_count()
243    }
244
245    #[inline(always)]
246    #[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
247    pub(super) fn as_simd(&self) -> &BvhNodeSimd {
248        // SAFETY: BvhNode is declared with the alignment
249        //         and size of two SimdReal.
250        unsafe { core::mem::transmute(self) }
251    }
252
253    #[inline(always)]
254    pub(super) fn merged(&self, other: &Self, children: u32) -> Self {
255        // TODO PERF: simd optimizations?
256        Self {
257            mins: self.mins.inf(&other.mins),
258            children,
259            maxs: self.maxs.sup(&other.maxs),
260            data: self.data.merged(other.data),
261        }
262    }
263
264    /// The min corner of this node’s AABB.
265    #[inline]
266    pub fn mins(&self) -> Point<Real> {
267        self.mins
268    }
269
270    /// The max corner of this node’s AABB.
271    #[inline]
272    pub fn maxs(&self) -> Point<Real> {
273        self.maxs
274    }
275
276    /// This node’s AABB.
277    #[inline]
278    pub fn aabb(&self) -> Aabb {
279        Aabb {
280            mins: self.mins,
281            maxs: self.maxs,
282        }
283    }
284
285    /// The center of this node’s AABB.
286    #[inline]
287    pub fn center(&self) -> Point<Real> {
288        na::center(&self.mins, &self.maxs)
289    }
290
291    /// Return `true` if this node has been marked as changed.
292    #[inline(always)]
293    pub fn is_changed(&self) -> bool {
294        self.data.is_changed()
295    }
296
297    /// Applies a scale factor to this node’s AABB.
298    #[inline]
299    pub fn scale(&mut self, scale: &Vector<Real>) {
300        self.mins.coords.component_mul_assign(scale);
301        self.maxs.coords.component_mul_assign(scale);
302    }
303
304    /// Calculates the volume of the AABB of `self`.
305    #[inline]
306    pub fn volume(&self) -> Real {
307        // TODO PERF: simd optimizations?
308        let extents = self.maxs - self.mins;
309        #[cfg(feature = "dim2")]
310        return extents.x * extents.y;
311        #[cfg(feature = "dim3")]
312        return extents.x * extents.y * extents.z;
313    }
314
315    /// Calculates the volume of the AABB resulting from the merge of `self` and `other`.
316    pub fn merged_volume(&self, other: &Self) -> Real {
317        // TODO PERF: simd optimizations?
318        let mins = self.mins.inf(&other.mins);
319        let maxs = self.maxs.sup(&other.maxs);
320        let extents = maxs - mins;
321
322        #[cfg(feature = "dim2")]
323        return extents.x * extents.y;
324        #[cfg(feature = "dim3")]
325        return extents.x * extents.y * extents.z;
326    }
327
328    /// Checks if the AABB of `self` intersects the `other` node’s AABB.
329    #[cfg(not(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32")))]
330    pub fn intersects(&self, other: &Self) -> bool {
331        na::partial_le(&self.mins, &other.maxs) && na::partial_ge(&self.maxs, &other.mins)
332    }
333
334    /// Checks if the AABB of `self` intersects the `other` node’s AABB.
335    #[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
336    pub fn intersects(&self, other: &Self) -> bool {
337        let simd_self = self.as_simd();
338        let simd_other = other.as_simd();
339        (simd_self.mins.cmple(simd_other.maxs) & simd_self.maxs.cmpge(simd_other.mins)).all()
340    }
341
342    /// Checks if the AABB of `self` fully encloses the `other` node’s AABB.
343    #[cfg(not(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32")))]
344    pub fn contains(&self, other: &Self) -> bool {
345        na::partial_le(&self.mins, &other.mins) && na::partial_ge(&self.maxs, &other.maxs)
346    }
347
348    /// Checks if the AABB of `self` fully encloses the `other` node’s AABB.
349    #[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
350    pub fn contains(&self, other: &Self) -> bool {
351        let simd_self = self.as_simd();
352        let simd_other = other.as_simd();
353        (simd_self.mins.cmple(simd_other.mins) & simd_self.maxs.cmpge(simd_other.maxs)).all()
354    }
355
356    /// Checks if the AABB of `self` fully encloses the `other` AABB.
357    pub fn contains_aabb(&self, other: &Aabb) -> bool {
358        // TODO PERF: simd optimizations?
359        na::partial_le(&self.mins, &other.mins) && na::partial_ge(&self.maxs, &other.maxs)
360    }
361
362    /// Casts a ray on this AABB.
363    ///
364    /// Returns `Real::MAX` if there is no hit.
365    pub fn cast_ray(&self, ray: &Ray, max_toi: Real) -> Real {
366        self.aabb()
367            .cast_local_ray(ray, max_toi, true)
368            .unwrap_or(Real::MAX)
369    }
370
371    /// Casts a ray on this AABB, with SIMD optimizations.
372    ///
373    /// Returns `Real::MAX` if there is no hit.
374    #[cfg(all(feature = "simd-is-enabled", feature = "dim3", feature = "f32"))]
375    pub(super) fn cast_inv_ray_simd(&self, ray: &super::bvh_queries::SimdInvRay) -> f32 {
376        let simd_self = self.as_simd();
377        let t1 = (simd_self.mins - ray.origin) * ray.inv_dir;
378        let t2 = (simd_self.maxs - ray.origin) * ray.inv_dir;
379
380        let tmin = t1.min(t2);
381        let tmax = t1.max(t2);
382        // let tmin = tmin.as_array_ref();
383        // let tmax = tmax.as_array_ref();
384        let tmin_n = tmin.max_element(); // tmin[0].max(tmin[1].max(tmin[2]));
385        let tmax_n = tmax.min_element(); // tmax[0].min(tmax[1].min(tmax[2]));
386
387        if tmax_n >= tmin_n && tmax_n >= 0.0 {
388            tmin_n
389        } else {
390            f32::MAX
391        }
392    }
393}
394
395/// An index identifying a single BVH tree node.
396#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
397#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
398#[cfg_attr(
399    feature = "rkyv",
400    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
401    archive(check_bytes)
402)]
403pub struct BvhNodeIndex(pub usize);
404
405impl BvhNodeIndex {
406    pub(super) const LEFT: bool = false;
407    pub(super) const RIGHT: bool = true;
408
409    /// Decomposes the index into the `BvhNodeWide` and the boolean indicating if
410    /// it is `BvhNodeWide::left` (false) or `BvhNodeWide::right` (true).
411    #[inline]
412    pub fn decompose(self) -> (usize, bool) {
413        (self.0 >> 1, (self.0 & 0b01) != 0)
414    }
415
416    /// The BVH index for the sibling of `self`.
417    ///
418    /// For example if `self` identifies the left child of a node, then its sibling is the right
419    /// child of the same node.
420    #[inline]
421    pub fn sibling(self) -> Self {
422        // Just flip the first bit to switch between left and right child.
423        Self(self.0 ^ 0b01)
424    }
425
426    /// The BVH node index stored as the left child of the `id`-th 2-wide node of this tree.
427    #[inline]
428    pub fn left(id: u32) -> Self {
429        Self::new(id, Self::LEFT)
430    }
431
432    /// The BVH node index stored as the right child of the `id`-th 2-wide node of this tree.
433    #[inline]
434    pub fn right(id: u32) -> Self {
435        Self::new(id, Self::RIGHT)
436    }
437
438    /// The BVH node index stored as the left (`is_right = false`) or right (`is_right = true`)
439    /// child of the `id`-th 2-wide node of this tree.
440    #[inline]
441    pub fn new(id: u32, is_right: bool) -> Self {
442        Self(((id as usize) << 1) | (is_right as usize))
443    }
444}
445
446#[derive(Clone, Debug, Default)]
447#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
448#[cfg_attr(
449    feature = "rkyv",
450    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
451    archive(check_bytes)
452)]
453pub(crate) struct BvhNodeVec(pub(crate) Vec<BvhNodeWide>);
454
455impl Deref for BvhNodeVec {
456    type Target = Vec<BvhNodeWide>;
457    fn deref(&self) -> &Self::Target {
458        &self.0
459    }
460}
461
462impl DerefMut for BvhNodeVec {
463    fn deref_mut(&mut self) -> &mut Self::Target {
464        &mut self.0
465    }
466}
467
468impl Index<usize> for BvhNodeVec {
469    type Output = BvhNodeWide;
470
471    #[inline(always)]
472    fn index(&self, index: usize) -> &Self::Output {
473        &self.0[index]
474    }
475}
476
477impl IndexMut<usize> for BvhNodeVec {
478    #[inline(always)]
479    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
480        &mut self.0[index]
481    }
482}
483
484impl Index<BvhNodeIndex> for BvhNodeVec {
485    type Output = BvhNode;
486
487    #[inline(always)]
488    fn index(&self, index: BvhNodeIndex) -> &Self::Output {
489        self.0[index.0 >> 1].as_array()[index.0 & 1]
490    }
491}
492
493impl IndexMut<BvhNodeIndex> for BvhNodeVec {
494    #[inline(always)]
495    fn index_mut(&mut self, index: BvhNodeIndex) -> &mut Self::Output {
496        self.0[index.0 >> 1].as_array_mut()[index.0 & 1]
497    }
498}
499
500/// A Bounding Volume Hierarchy designed for spatial queries and physics engine broad-phases.
501#[derive(Clone, Debug, Default)]
502#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
503#[cfg_attr(
504    feature = "rkyv",
505    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize),
506    archive(check_bytes)
507)]
508pub struct Bvh {
509    pub(super) nodes: BvhNodeVec,
510    // Parent indices for elements in `nodes`.
511    // We don’t store this in `Self::nodes` since it’s only useful for node removal.
512    pub(super) parents: Vec<BvhNodeIndex>,
513    pub(super) leaf_node_indices: VecMap<BvhNodeIndex>,
514}
515
516impl Bvh {
517    /// An empty BVH.
518    pub fn new() -> Self {
519        Self::default()
520    }
521
522    /// Creates a new BVH with a slice of AABBs.
523    ///
524    /// Each leaf will be associated an index equal to its position into the slice. For example,
525    /// the AABB `leaves[42]` is associated to the leaf with index 42.
526    pub fn from_leaves(strategy: BvhBuildStrategy, leaves: &[Aabb]) -> Self {
527        Self::from_iter(strategy, leaves.iter().copied().enumerate())
528    }
529
530    /// Creates a new BVH with leaves given by an iterator.
531    ///
532    /// The iterator yields leaf index and aabbs. The leaf indices will then be read back
533    /// by various methods like tree traversals or leaf iterations.
534    ///
535    /// Note that the indices are stored internally as `u32`. The iterator expects `usize`
536    /// for convenience (so that iterators built with `.enumerate()` can be used directly
537    /// without an additional cast of the `usize` index to `u32`).
538    pub fn from_iter<It>(strategy: BvhBuildStrategy, leaves: It) -> Self
539    where
540        It: IntoIterator<Item = (usize, Aabb)>,
541    {
542        let leaves = leaves.into_iter();
543        let (capacity_lo, capacity_up) = leaves.size_hint();
544        let capacity = capacity_up.unwrap_or(capacity_lo);
545
546        let mut result = Self::new();
547        let mut workspace = BvhWorkspace::default();
548        workspace.rebuild_leaves.reserve(capacity);
549        result.leaf_node_indices.reserve_len(capacity);
550
551        for (leaf_id, leaf_aabb) in leaves {
552            workspace
553                .rebuild_leaves
554                .push(BvhNode::leaf(leaf_aabb, leaf_id as u32));
555            let _ = result
556                .leaf_node_indices
557                .insert(leaf_id, BvhNodeIndex::default());
558        }
559
560        // Handle special cases that don’t play well with the rebuilds.
561        match workspace.rebuild_leaves.len() {
562            0 => {}
563            1 => {
564                result.nodes.push(BvhNodeWide {
565                    left: workspace.rebuild_leaves[0],
566                    right: BvhNode::zeros(),
567                });
568                result.parents.push(BvhNodeIndex::default());
569                result.leaf_node_indices[0] = BvhNodeIndex::left(0);
570            }
571            2 => {
572                result.nodes.push(BvhNodeWide {
573                    left: workspace.rebuild_leaves[0],
574                    right: workspace.rebuild_leaves[1],
575                });
576                result.parents.push(BvhNodeIndex::default());
577                result.leaf_node_indices[0] = BvhNodeIndex::left(0);
578                result.leaf_node_indices[1] = BvhNodeIndex::right(0);
579            }
580            _ => {
581                result.nodes.reserve(capacity);
582                result.parents.reserve(capacity);
583                result.parents.clear();
584                result.nodes.push(BvhNodeWide::zeros());
585                result.parents.push(BvhNodeIndex::default());
586
587                match strategy {
588                    BvhBuildStrategy::Ploc => {
589                        result.rebuild_range_ploc(0, &mut workspace.rebuild_leaves)
590                    }
591                    BvhBuildStrategy::Binned => {
592                        result.rebuild_range_binned(0, &mut workspace.rebuild_leaves)
593                    }
594                }
595
596                // Layout in depth-first order.
597                result.refit(&mut workspace);
598            }
599        }
600
601        result
602    }
603
604    /// The AABB bounding everything contained by this BVH.
605    pub fn root_aabb(&self) -> Aabb {
606        match self.leaf_count() {
607            0 => Aabb::new_invalid(),
608            1 => self.nodes[0].left.aabb(),
609            _ => self.nodes[0]
610                .left
611                .aabb()
612                .merged(&self.nodes[0].right.aabb()),
613        }
614    }
615
616    /// Scales this tree’s geometry by the given factor.
617    ///
618    /// This is only valid if all elements of `scale` are positive.
619    pub fn scale(&mut self, scale: &Vector<Real>) {
620        for node in self.nodes.0.iter_mut() {
621            node.left.scale(scale);
622            node.right.scale(scale);
623        }
624    }
625
626    /// Does this tree not contain any leaf?
627    pub fn is_empty(&self) -> bool {
628        self.nodes.is_empty()
629    }
630
631    ///  Reference to the leaf associated to the given index at construction time.
632    pub fn leaf_node(&self, leaf_key: u32) -> Option<&BvhNode> {
633        let idx = self.leaf_node_indices.get(leaf_key as usize)?;
634        Some(&self.nodes[*idx])
635    }
636
637    /// An approximation of the memory usage (in bytes) for this struct plus
638    /// the memory it allocates dynamically.
639    pub fn total_memory_size(&self) -> usize {
640        size_of::<Self>() + self.heap_memory_size()
641    }
642
643    /// An approximation of the memory dynamically-allocated by this struct.
644    pub fn heap_memory_size(&self) -> usize {
645        let Self {
646            nodes,
647            parents,
648            leaf_node_indices,
649        } = self;
650        nodes.capacity() * size_of::<BvhNodeWide>()
651            + parents.capacity() * size_of::<BvhNodeIndex>()
652            + leaf_node_indices.capacity() * size_of::<BvhNodeIndex>()
653    }
654
655    /// The depth of the sub-tree rooted at the node with index `node_id`.
656    ///
657    /// Set `node_id` to 0 to get the depth of the whole tree.
658    pub fn subtree_depth(&self, node_id: u32) -> u32 {
659        if node_id == 0 && self.nodes.is_empty() {
660            return 0;
661        } else if node_id == 0 && self.nodes.len() == 1 {
662            return 1 + (self.nodes[0].right.leaf_count() != 0) as u32;
663        }
664
665        let node = &self.nodes[node_id as usize];
666
667        let left_depth = if node.left.is_leaf() {
668            1
669        } else {
670            self.subtree_depth(node.left.children)
671        };
672
673        let right_depth = if node.right.is_leaf() {
674            1
675        } else {
676            self.subtree_depth(node.right.children)
677        };
678
679        left_depth.max(right_depth) + 1
680    }
681
682    /// The number of leaves of this tree.
683    pub fn leaf_count(&self) -> u32 {
684        if self.nodes.is_empty() {
685            0
686        } else {
687            self.nodes[0].leaf_count()
688        }
689    }
690
691    /// Deletes the leaf at the given index.
692    ///
693    /// The operation is `O(h)` where `h` is the tree height (which, if
694    /// optimized should be close to `n log(n)` where `n` is the leaf count). It will update the
695    /// leaf counts and AABBs of all ancestors of the removed leaf.
696    // TODO: should we make a version that doesn’t traverse the parents?
697    //       If we do, we must be very careful that the leaf counts that become
698    //       invalid don’t break other algorithm… (and, in particular, the root
699    //       special case that checks if its right element has 0 leaf count).
700    pub fn remove(&mut self, leaf_index: u32) {
701        if let Some(node_index) = self.leaf_node_indices.remove(leaf_index as usize) {
702            if self.leaf_node_indices.is_empty() {
703                // We deleted the last leaf! Remove the root.
704                self.nodes.clear();
705                self.parents.clear();
706                return;
707            }
708
709            let sibling = node_index.sibling();
710            let (wide_node_index, is_right) = node_index.decompose();
711
712            if wide_node_index == 0 {
713                if self.nodes[sibling].is_leaf() {
714                    // If the sibling is a leaf, we end up with a partial root.
715                    // There is no parent pointer to update.
716                    if !is_right {
717                        // We remove the left leaf. Move the right leaf in its place.
718                        let moved_index = self.nodes[0].right.children;
719                        self.nodes[0].left = self.nodes[0].right;
720                        self.leaf_node_indices[moved_index as usize] = BvhNodeIndex::left(0);
721                    }
722
723                    // Now we can just clear the right leaf.
724                    self.nodes[0].right = BvhNode::zeros();
725                } else {
726                    // The sibling isn’t a leaf. It becomes the new root at index 0.
727                    self.nodes[0] = self.nodes[self.nodes[sibling].children as usize];
728                    // Both parent pointers need to be updated since both nodes moved to the root.
729                    let new_root = &mut self.nodes[0];
730                    if new_root.left.is_leaf() {
731                        self.leaf_node_indices[new_root.left.children as usize] =
732                            BvhNodeIndex::left(0);
733                    } else {
734                        self.parents[new_root.left.children as usize] = BvhNodeIndex::left(0);
735                    }
736                    if new_root.right.is_leaf() {
737                        self.leaf_node_indices[new_root.right.children as usize] =
738                            BvhNodeIndex::right(0);
739                    } else {
740                        self.parents[new_root.right.children as usize] = BvhNodeIndex::right(0);
741                    }
742                }
743            } else {
744                // The sibling moves to the parent. The affected wide node is no longer accessible,
745                // but we can just leave it there, it will get cleaned up at the next refit.
746                let parent = self.parents[wide_node_index];
747                let sibling = &self.nodes[sibling];
748
749                if sibling.is_leaf() {
750                    self.leaf_node_indices[sibling.children as usize] = parent;
751                } else {
752                    self.parents[sibling.children as usize] = parent;
753                }
754
755                self.nodes[parent] = *sibling;
756
757                // TODO: we could use that propagation as an opportunity to
758                //       apply some rotations?
759                let mut curr = parent.decompose().0;
760                while curr != 0 {
761                    let parent = self.parents[curr];
762                    self.nodes[parent] = self.nodes[curr].merged(curr as u32);
763                    curr = parent.decompose().0;
764                }
765            }
766        }
767    }
768
769    // pub fn quality_metric(&self) -> Real {
770    //     let mut metric = 0.0;
771    //     for i in 0..self.nodes.len() {
772    //         if !self.nodes[i].is_leaf() {
773    //             metric += self.sah_cost(i);
774    //         }
775    //     }
776    //     metric
777    // }
778}