Skip to main content

obvhs/bvh2/
mod.rs

1//! A binary BVH
2
3pub mod builder;
4pub mod insertion_removal;
5pub mod leaf_collapser;
6
7pub mod node;
8pub mod reinsertion;
9
10use bytemuck::zeroed_vec;
11use glam::Vec3A;
12use node::Bvh2Node;
13
14use std::{
15    collections::{HashMap, HashSet},
16    fmt,
17};
18
19use reinsertion::find_reinsertion;
20
21use crate::{
22    Boundable, INVALID,
23    aabb::Aabb,
24    fast_stack,
25    faststack::FastStack,
26    ray::{Ray, RayHit},
27};
28
29/// A binary BVH
30#[derive(Clone)]
31pub struct Bvh2 {
32    /// List of nodes contained in this bvh. first_index in Bvh2Node for inner nodes indexes into this list. This list
33    /// fully represents the BVH tree. The other fields in this struct provide additional information that allow the BVH
34    /// to be manipulated more efficiently, but are not actually part of the BVH itself. The only other critical field is
35    /// `primitive_indices`, assuming the BVH is not using a direct mapping.
36    pub nodes: Vec<Bvh2Node>,
37
38    /// Mapping from bvh primitive indices to original input indices
39    /// The reason for this mapping is that if multiple primitives are contained in a node, they need to have their
40    /// indices laid out contiguously. To avoid this indirection we have two options:
41    /// 1. Layout the primitives in the order of the primitive_indices mapping so that this can index directly into the
42    ///    primitive list.
43    /// 2. Only allow one primitive per node and write back the original mapping to the bvh node list.
44    pub primitive_indices: Vec<u32>,
45
46    /// A freelist for use when removing primitives from the bvh. These represent slots in Bvh2::primitive_indices
47    /// that are available if a primitive is added to the bvh. Only currently used by Bvh2::remove_primitive(),
48    /// Bvh2::insert_primitive(), and Bvh2::insert_primitive_precise(), which are not part of the typical initial
49    /// BVH generation.
50    pub primitive_indices_freelist: Vec<u32>,
51
52    /// An optional mapping from primitives back to nodes.
53    /// Ex. `let node_id = primitives_to_nodes[primitive_id];`
54    /// Where primitive_id is the original index of the primitive used when making the BVH and node_id is the index
55    /// into Bvh2::nodes for the node of that primitive. Always use with the direct primitive id, not the one in the
56    /// bvh node.
57    /// See: Bvh2::init_primitives_to_nodes().
58    /// If `primitives_to_nodes` is empty it's expected that it has not been initialized yet or has been invalidated.
59    /// If `primitives_to_nodes` is not empty, it is expected that functions that modify the BVH will keep the mapping
60    /// valid.
61    pub primitives_to_nodes: Vec<u32>,
62
63    /// An optional mapping from a given node index to that node's parent for each node in the bvh.
64    /// See: Bvh2::init_parents_if_uninit().
65    /// If `parents` is empty it's expected that it has not been initialized yet or has been invalidated.
66    /// If `parents` is not empty it's expected that functions that modify the BVH will keep the mapping valid.
67    pub parents: Vec<u32>,
68
69    /// This is set by operations that ensure that parents have higher indices than children and unset by operations
70    /// that might disturb that order. Some operations require this ordering and will reorder if this is not true.
71    pub children_are_ordered_after_parents: bool,
72
73    /// Stack defaults to 96 or the max depth found during initial ploc building, whichever is larger. This may be
74    /// larger than needed depending on what post processing steps (like collapse, reinsertion, etc...), but the cost of
75    /// recalculating it may not be worth it so it is not done automatically.
76    pub max_depth: usize,
77
78    /// Indicates that this BVH is using spatial splits. Large triangles are split into multiple smaller Aabbs, so
79    /// primitives will extend outside the leaf in some cases.
80    /// If the bvh uses splits, a primitive can show up in multiple leaf nodes so there wont be a 1 to 1 correlation
81    /// between the total number of primitives in leaf nodes and in Bvh2::primitive_indices, vs the input triangles.
82    /// If spatial splits are used, some validation steps have to be skipped and some features are unavailable:
83    /// `Bvh2::add_leaf()`, `Bvh2::remove_leaf()`, `Bvh2::add_primitive()`, `Bvh2::remove_primitive()` as these would
84    /// require a mapping from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`
85    pub uses_spatial_splits: bool,
86}
87
88pub const DEFAULT_MAX_STACK_DEPTH: usize = 96;
89
90impl Default for Bvh2 {
91    fn default() -> Self {
92        Self {
93            nodes: Default::default(),
94            primitive_indices: Default::default(),
95            primitive_indices_freelist: Default::default(),
96            primitives_to_nodes: Default::default(),
97            parents: Default::default(),
98            children_are_ordered_after_parents: Default::default(),
99            max_depth: DEFAULT_MAX_STACK_DEPTH,
100            uses_spatial_splits: Default::default(),
101        }
102    }
103}
104
105impl Bvh2 {
106    /// Reset BVH while keeping allocations for rebuild. Note: results in an invalid bvh until rebuilt.
107    pub fn reset_for_reuse(&mut self, prim_count: usize, indices: Option<Vec<u32>>) {
108        let nodes_count = (2 * prim_count as i64 - 1).max(0) as usize;
109        self.nodes.resize(nodes_count, Default::default());
110        if let Some(indices) = indices {
111            self.primitive_indices = indices;
112        } else {
113            self.primitive_indices
114                .resize(prim_count, Default::default());
115        }
116        self.primitive_indices_freelist.clear();
117        self.primitives_to_nodes.clear();
118        self.parents.clear();
119        self.children_are_ordered_after_parents = Default::default();
120        self.max_depth = DEFAULT_MAX_STACK_DEPTH;
121        self.uses_spatial_splits = Default::default();
122    }
123
124    pub fn zeroed(prim_count: usize) -> Self {
125        let nodes_count = (2 * prim_count as i64 - 1).max(0) as usize;
126        Self {
127            nodes: zeroed_vec(nodes_count),
128            primitive_indices: zeroed_vec(prim_count),
129            primitive_indices_freelist: Default::default(),
130            primitives_to_nodes: Default::default(),
131            parents: Default::default(),
132            children_are_ordered_after_parents: Default::default(),
133            max_depth: DEFAULT_MAX_STACK_DEPTH,
134            uses_spatial_splits: Default::default(),
135        }
136    }
137
138    /// Traverse the bvh for a given `Ray`. Returns the closest intersected primitive.
139    ///
140    /// # Arguments
141    /// * `ray` - The ray to be tested for intersection.
142    /// * `hit` - As traverse_dynamic intersects primitives, it will update `hit` with the closest.
143    /// * `intersection_fn` - should take the given ray and primitive index and return the distance to the intersection, if any.
144    ///
145    /// Note the primitive index should index first into Bvh2::primitive_indices then that will be index of original primitive.
146    /// Various parts of the BVH building process might reorder the primitives. To avoid this indirection, reorder your
147    /// original primitives per primitive_indices.
148    #[inline(always)]
149    pub fn ray_traverse<F: FnMut(&Ray, usize) -> f32>(
150        &self,
151        ray: Ray,
152        hit: &mut RayHit,
153        mut intersection_fn: F,
154    ) -> bool {
155        let mut intersect_prims = |node: &Bvh2Node, ray: &mut Ray, hit: &mut RayHit| {
156            (node.first_index..node.first_index + node.prim_count).for_each(|primitive_id| {
157                let t = intersection_fn(ray, primitive_id as usize);
158                if t < ray.tmax {
159                    hit.primitive_id = primitive_id;
160                    hit.t = t;
161                    ray.tmax = t;
162                }
163            });
164            true
165        };
166
167        fast_stack!(u32, (96, 192), self.max_depth, stack, {
168            Bvh2::ray_traverse_dynamic(self, &mut stack, ray, hit, &mut intersect_prims)
169        });
170
171        hit.t < ray.tmax // Note this is valid since traverse_with_stack does not mutate the ray
172    }
173
174    /// Traverse the bvh for a given `Ray`. Returns true if the ray missed all primitives.
175    ///
176    /// # Arguments
177    /// * `ray` - The ray to be tested for intersection.
178    /// * `hit` - As traverse_dynamic intersects primitives, it will update `hit` with the closest.
179    /// * `intersection_fn` - should take the given ray and primitive index and return the distance to the intersection, if any.
180    ///
181    /// Note the primitive index should index first into Bvh2::primitive_indices then that will be index of original primitive.
182    /// Various parts of the BVH building process might reorder the primitives. To avoid this indirection, reorder your
183    /// original primitives per primitive_indices.
184    #[inline(always)]
185    pub fn ray_traverse_miss<F: FnMut(&Ray, usize) -> f32>(
186        &self,
187        ray: Ray,
188        mut intersection_fn: F,
189    ) -> bool {
190        let mut miss = true;
191        let mut intersect_prims = |node: &Bvh2Node, ray: &mut Ray, _hit: &mut RayHit| {
192            for primitive_id in node.first_index..node.first_index + node.prim_count {
193                let t = intersection_fn(ray, primitive_id as usize);
194                if t < ray.tmax {
195                    miss = false;
196                    return false;
197                }
198            }
199            true
200        };
201
202        fast_stack!(u32, (96, 192), self.max_depth, stack, {
203            Bvh2::ray_traverse_dynamic(
204                self,
205                &mut stack,
206                ray,
207                &mut RayHit::none(),
208                &mut intersect_prims,
209            )
210        });
211
212        miss
213    }
214
215    /// Traverse the bvh for a given `Ray`. Intersects all primitives along ray (for things like evaluating transparency)
216    ///   intersection_fn is called for all intersections. Ray is not updated to allow for evaluating at every hit.
217    ///
218    /// # Arguments
219    /// * `ray` - The ray to be tested for intersection.
220    /// * `intersection_fn` - takes the given ray and primitive index.
221    ///
222    /// Note the primitive index should index first into Bvh2::primitive_indices then that will be index of original primitive.
223    /// Various parts of the BVH building process might reorder the primitives. To avoid this indirection, reorder your
224    /// original primitives per primitive_indices.
225    #[inline(always)]
226    pub fn ray_traverse_anyhit<F: FnMut(&Ray, usize)>(&self, ray: Ray, mut intersection_fn: F) {
227        let mut intersect_prims = |node: &Bvh2Node, ray: &mut Ray, _hit: &mut RayHit| {
228            for primitive_id in node.first_index..node.first_index + node.prim_count {
229                intersection_fn(ray, primitive_id as usize);
230            }
231            true
232        };
233
234        let mut hit = RayHit::none();
235        fast_stack!(u32, (96, 192), self.max_depth, stack, {
236            self.ray_traverse_dynamic(&mut stack, ray, &mut hit, &mut intersect_prims)
237        });
238    }
239
240    /// Traverse the BVH
241    /// Returns false when no hit is found. Consider using or referencing: Bvh2::ray_traverse(),
242    /// Bvh2::ray_traverse_miss(), or Bvh2::ray_traverse_anyhit().
243    ///
244    /// # Arguments
245    /// * `state` - Holds the current traversal state. Allows traverse_dynamic to yield.
246    /// * `hit` - As traverse_dynamic intersects primitives, it will update `hit` with the closest.
247    /// * `intersection_fn` - should test the primitives in the given node, update the ray.tmax, and hit info. Return
248    ///   false to halt traversal.
249    ///   For basic miss test return false on first hit to halt traversal.
250    ///   For closest hit run until it returns false and check hit.t < ray.tmax to see if it hit something
251    ///   For transparency, you want to hit every primitive in the ray's path, keeping track of the closest opaque hit.
252    ///   and then manually setting ray.tmax to that closest opaque hit at each iteration.
253    ///
254    /// Note the primitive index should index first into Bvh2::primitive_indices then that will be index of original primitive.
255    /// Various parts of the BVH building process might reorder the primitives. To avoid this indirection, reorder your
256    /// original primitives per primitive_indices.
257    #[inline(always)]
258    pub fn ray_traverse_dynamic<
259        F: FnMut(&Bvh2Node, &mut Ray, &mut RayHit) -> bool,
260        Stack: FastStack<u32>,
261    >(
262        &self,
263        stack: &mut Stack,
264        mut ray: Ray,
265        hit: &mut RayHit,
266        mut intersection_fn: F,
267    ) {
268        if self.nodes.is_empty() {
269            return;
270        }
271
272        let root_node = &self.nodes[0];
273        let hit_root = root_node.aabb().intersect_ray(&ray) < ray.tmax;
274        if !hit_root {
275            return;
276        } else if root_node.is_leaf() {
277            intersection_fn(root_node, &mut ray, hit);
278            return;
279        };
280
281        let mut current_node_index = root_node.first_index;
282        loop {
283            let right_index = current_node_index as usize + 1;
284            assert!(right_index < self.nodes.len());
285            let mut left_node = unsafe { self.nodes.get_unchecked(current_node_index as usize) };
286            let mut right_node = unsafe { self.nodes.get_unchecked(right_index) };
287
288            // TODO perf: could it be faster to intersect these at the same time with avx?
289            let mut left_t = left_node.aabb().intersect_ray(&ray);
290            let mut right_t = right_node.aabb().intersect_ray(&ray);
291
292            if left_t > right_t {
293                core::mem::swap(&mut left_t, &mut right_t);
294                core::mem::swap(&mut left_node, &mut right_node);
295            }
296
297            let hit_left = left_t < ray.tmax;
298
299            let go_left = if hit_left && left_node.is_leaf() {
300                if !intersection_fn(left_node, &mut ray, hit) {
301                    return;
302                }
303                false
304            } else {
305                hit_left
306            };
307
308            let hit_right = right_t < ray.tmax;
309
310            let go_right = if hit_right && right_node.is_leaf() {
311                if !intersection_fn(right_node, &mut ray, hit) {
312                    return;
313                }
314                false
315            } else {
316                hit_right
317            };
318
319            match (go_left, go_right) {
320                (true, true) => {
321                    current_node_index = left_node.first_index;
322                    stack.push(right_node.first_index);
323                }
324                (true, false) => current_node_index = left_node.first_index,
325                (false, true) => current_node_index = right_node.first_index,
326                (false, false) => {
327                    let Some(next) = stack.pop() else {
328                        hit.t = ray.tmax;
329                        return;
330                    };
331                    current_node_index = next;
332                }
333            }
334        }
335    }
336
337    /// Recursively traverse the bvh for a given `Ray`.
338    /// On completion, `leaf_indices` will contain a list of the intersected leaf node indices.
339    /// This method is slower than stack traversal and only exists as a reference.
340    /// This method does not check if the primitive was intersected, only the leaf node.
341    pub fn ray_traverse_recursive(
342        &self,
343        ray: &Ray,
344        node_index: usize,
345        leaf_indices: &mut Vec<usize>,
346    ) {
347        if self.nodes.is_empty() {
348            return;
349        }
350        let node = &self.nodes[node_index];
351        if node.aabb().intersect_ray(ray) < f32::INFINITY {
352            if node.is_leaf() {
353                leaf_indices.push(node_index);
354            } else {
355                self.ray_traverse_recursive(ray, node.first_index as usize, leaf_indices);
356                self.ray_traverse_recursive(ray, node.first_index as usize + 1, leaf_indices);
357            }
358        }
359    }
360
361    /// Traverse the BVH with an Aabb. fn `eval` is called for nodes that intersect `aabb`
362    /// The bvh (self) and the current node index is passed into fn `eval`
363    /// Note each node may have multiple primitives. `node.first_index` is the index of the first primitive.
364    /// `node.prim_count` is the quantity of primitives contained in the given node.
365    /// Return false from eval to halt traversal
366    pub fn aabb_traverse<F: FnMut(&Self, u32) -> bool>(&self, aabb: Aabb, mut eval: F) {
367        if self.nodes.is_empty() {
368            return;
369        }
370
371        let root_node = &self.nodes[0];
372        if root_node.is_leaf() {
373            if root_node.aabb().intersect_aabb(&aabb) {
374                eval(self, 0);
375            }
376            return;
377        }
378
379        fast_stack!(u32, (96, 192), self.max_depth, stack, {
380            stack.push(root_node.first_index);
381            while let Some(node_index) = stack.pop() {
382                // Left
383                let node = &self.nodes[node_index as usize];
384                if node.aabb().intersect_aabb(&aabb) {
385                    if node.is_leaf() {
386                        if !eval(self, node_index) {
387                            return;
388                        }
389                    } else {
390                        stack.push(node.first_index);
391                    }
392                }
393
394                // Right
395                let node_index = node_index + 1;
396                let node = &self.nodes[node_index as usize];
397                if node.aabb().intersect_aabb(&aabb) {
398                    if node.is_leaf() {
399                        if !eval(self, node_index) {
400                            return;
401                        }
402                    } else {
403                        stack.push(node.first_index);
404                    }
405                }
406            }
407        });
408    }
409
410    /// Traverse the BVH with a point. fn `eval` is called for nodes that intersect `point`
411    /// The bvh (self) and the current node index is passed into fn `eval`
412    /// Note each node may have multiple primitives. `node.first_index` is the index of the first primitive.
413    /// `node.prim_count` is the quantity of primitives contained in the given node.
414    /// Return false from eval to halt traversal
415    pub fn point_traverse<F: FnMut(&Self, u32) -> bool>(&self, point: Vec3A, mut eval: F) {
416        if self.nodes.is_empty() {
417            return;
418        }
419
420        let root_node = &self.nodes[0];
421        if root_node.is_leaf() {
422            if root_node.aabb().contains_point(point) {
423                eval(self, 0);
424            }
425            return;
426        }
427
428        fast_stack!(u32, (96, 192), self.max_depth, stack, {
429            stack.push(root_node.first_index);
430            while let Some(node_index) = stack.pop() {
431                // Left
432                let node = &self.nodes[node_index as usize];
433                if node.aabb().contains_point(point) {
434                    if node.is_leaf() {
435                        if !eval(self, node_index) {
436                            return;
437                        }
438                    } else {
439                        stack.push(node.first_index);
440                    }
441                }
442
443                // Right
444                let node_index = node_index + 1;
445                let node = &self.nodes[node_index as usize];
446                if node.aabb().contains_point(point) {
447                    if node.is_leaf() {
448                        if !eval(self, node_index) {
449                            return;
450                        }
451                    } else {
452                        stack.push(node.first_index);
453                    }
454                }
455            }
456        });
457    }
458
459    /// Order node array in stack traversal order. Ensures parents are always at lower indices than children. Fairly
460    /// slow, can take around 1/3 of the time of building the same BVH from scratch from with the fastest_build preset.
461    /// Doesn't seem to speed up traversal much for a new BVH created from PLOC, but if it has had many
462    /// removals/insertions it can help.
463    pub fn reorder_in_stack_traversal_order(&mut self) {
464        if self.nodes.len() < 2 {
465            return;
466        }
467        let mut new_nodes: Vec<Bvh2Node> = Vec::with_capacity(self.nodes.len());
468        let mut mapping = vec![0; self.nodes.len()]; // Map from where n node used to be to where it is now
469        let mut stack = Vec::new();
470        stack.push(self.nodes[0].first_index);
471        new_nodes.push(self.nodes[0]);
472        mapping[0] = 0;
473        while let Some(current_node_index) = stack.pop() {
474            let node_a = &self.nodes[current_node_index as usize];
475            let node_b = &self.nodes[current_node_index as usize + 1];
476            if !node_a.is_leaf() {
477                stack.push(node_a.first_index);
478            }
479            if !node_b.is_leaf() {
480                stack.push(node_b.first_index);
481            }
482            let new_node_idx = new_nodes.len() as u32;
483            mapping[current_node_index as usize] = new_node_idx;
484            mapping[current_node_index as usize + 1] = new_node_idx + 1;
485            new_nodes.push(*node_a);
486            new_nodes.push(*node_b);
487        }
488        for n in &mut new_nodes {
489            if !n.is_leaf() {
490                n.first_index = mapping[n.first_index as usize];
491            }
492        }
493        self.nodes = new_nodes;
494        if !self.parents.is_empty() {
495            self.update_parents();
496        }
497        if !self.primitives_to_nodes.is_empty() {
498            self.update_primitives_to_nodes();
499        }
500        self.children_are_ordered_after_parents = true;
501    }
502
503    /// Refits the whole BVH from the leaves up. If the leaves have moved very much the BVH can quickly become
504    /// degenerate causing significantly higher traversal times. Consider rebuilding the BVH from scratch or running a
505    /// bit of reinsertion after refit.
506    /// Usage:
507    /// ```
508    ///    use glam::*;
509    ///    use obvhs::*;
510    ///    use obvhs::{ploc::*, test_util::geometry::demoscene, bvh2::builder::build_bvh2_from_tris};
511    ///    use std::time::Duration;
512    ///
513    ///    let mut tris = demoscene(32, 0);
514    ///    let mut bvh = build_bvh2_from_tris(&tris, BvhBuildParams::fastest_build(), &mut Duration::default());
515    ///
516    ///    bvh.init_primitives_to_nodes_if_uninit(); // Generate mapping from primitives to nodes
517    ///    tris.transform(&Mat4::from_scale_rotation_translation(
518    ///        Vec3::splat(1.3),
519    ///        Quat::from_rotation_y(0.1),
520    ///        vec3(0.33, 0.3, 0.37),
521    ///    ));
522    ///    for (prim_id, tri) in tris.iter().enumerate() {
523    ///        bvh.nodes[bvh.primitives_to_nodes[prim_id] as usize].set_aabb(tri.aabb()); // Update aabbs
524    ///    }
525    ///    bvh.refit_all(); // Refit aabbs
526    ///    bvh.validate(&tris, false, true); // Validate that aabbs are now fitting tightly
527    /// ```
528    pub fn refit_all(&mut self) {
529        if self.nodes.is_empty() {
530            return;
531        }
532        if self.children_are_ordered_after_parents {
533            // If children are already ordered after parents we can update in a single sweep.
534            // Around 3x faster than the fallback below.
535            for node_id in (0..self.nodes.len()).rev() {
536                let node = &self.nodes[node_id];
537                if !node.is_leaf() {
538                    let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
539                    let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
540                    self.nodes[node_id].set_aabb(first_child_bbox.union(&second_child_bbox));
541                }
542            }
543        } else {
544            // If not, we need to create a safe order in which we can make updates.
545            // This is much faster than reordering the whole bvh with Bvh2::reorder_in_stack_traversal_order()
546            fast_stack!(u32, (96, 192), self.max_depth, stack, {
547                let mut reverse_stack = Vec::with_capacity(self.nodes.len());
548                stack.push(0);
549                reverse_stack.push(0);
550                while let Some(current_node_index) = stack.pop() {
551                    let node = &self.nodes[current_node_index as usize];
552                    if !node.is_leaf() {
553                        reverse_stack.push(node.first_index);
554                        reverse_stack.push(node.first_index + 1);
555                        stack.push(node.first_index);
556                        stack.push(node.first_index + 1);
557                    }
558                }
559                for node_id in reverse_stack.iter().rev() {
560                    let node = &self.nodes[*node_id as usize];
561                    if !node.is_leaf() {
562                        let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
563                        let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
564                        self.nodes[*node_id as usize]
565                            .set_aabb(first_child_bbox.union(&second_child_bbox));
566                    }
567                }
568            });
569        }
570    }
571
572    /// Compute parents and update cache only if they have not already been computed
573    pub fn init_parents_if_uninit(&mut self) {
574        if self.parents.is_empty() {
575            self.update_parents();
576        }
577    }
578
579    /// Compute the mapping from a given node index to that node's parent for each node in the bvh and update local
580    /// cache.
581    pub fn update_parents(&mut self) {
582        Bvh2::compute_parents(&self.nodes, &mut self.parents);
583    }
584
585    /// Compute the mapping from a given node index to that node's parent for each node in the bvh, takes a Vec to allow
586    /// reusing the allocation.
587    pub fn compute_parents(nodes: &[Bvh2Node], parents: &mut Vec<u32>) {
588        parents.resize(nodes.len(), 0);
589
590        if nodes.is_empty() {
591            return;
592        }
593
594        parents[0] = 0;
595
596        #[cfg(not(feature = "parallel"))]
597        {
598            nodes.iter().enumerate().for_each(|(i, node)| {
599                if !node.is_leaf() {
600                    parents[node.first_index as usize] = i as u32;
601                    parents[node.first_index as usize + 1] = i as u32;
602                }
603            });
604        }
605        // Seems around 80% faster than compute_parents.
606        // TODO is there a better way to parallelize?
607        #[cfg(feature = "parallel")]
608        {
609            use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
610            use std::sync::atomic::Ordering;
611
612            let parents = crate::as_slice_of_atomic_u32(parents);
613            nodes.par_iter().enumerate().for_each(|(i, node)| {
614                if !node.is_leaf() {
615                    parents[node.first_index as usize].store(i as u32, Ordering::Relaxed);
616                    parents[node.first_index as usize + 1].store(i as u32, Ordering::Relaxed);
617                }
618            });
619        }
620    }
621
622    /// Compute compute_primitives_to_nodes and update cache only if they have not already been computed. Not supported
623    /// if using spatial splits as it would require a mapping from one primitive to multiple nodes.
624    pub fn init_primitives_to_nodes_if_uninit(&mut self) {
625        if self.primitives_to_nodes.is_empty() {
626            self.update_primitives_to_nodes();
627        }
628    }
629
630    /// Compute the mapping from primitive index to node index and update local cache. Not supported if using spatial
631    /// splits as it would require a mapping from one primitive to multiple nodes.
632    pub fn update_primitives_to_nodes(&mut self) {
633        if self.uses_spatial_splits {
634            log::warn!(
635                "Calculating primitives_to_nodes while using spatial splits is currently unsupported as it would \
636                require a mapping from one primitive to multiple nodes in `Bvh2::primitives_to_nodes`."
637            );
638        }
639
640        Bvh2::compute_primitives_to_nodes(
641            &self.nodes,
642            &self.primitive_indices,
643            &mut self.primitives_to_nodes,
644        );
645    }
646
647    /// Compute the mapping from primitive index to node index. Takes a Vec to allow reusing the allocation.
648    pub fn compute_primitives_to_nodes(
649        nodes: &[Bvh2Node],
650        primitive_indices: &[u32],
651        primitives_to_nodes: &mut Vec<u32>,
652    ) {
653        primitives_to_nodes.clear();
654        primitives_to_nodes.resize(primitive_indices.len(), INVALID);
655        for (node_id, node) in nodes.iter().enumerate() {
656            if node.is_leaf() {
657                let start = node.first_index;
658                let end = node.first_index + node.prim_count;
659                for node_prim_id in start..end {
660                    // TODO perf avoid this indirection by making self.primitive_indices optional?
661                    let prim_id = primitive_indices[node_prim_id as usize];
662                    primitives_to_nodes[prim_id as usize] = node_id as u32;
663                }
664            }
665        }
666    }
667
668    pub fn validate_parents(&self) {
669        self.nodes.iter().enumerate().for_each(|(i, node)| {
670            if !node.is_leaf() {
671                assert_eq!(self.parents[node.first_index as usize], i as u32);
672                assert_eq!(self.parents[node.first_index as usize + 1], i as u32);
673            }
674        });
675    }
676
677    pub fn validate_primitives_to_nodes(&self) {
678        self.primitives_to_nodes
679            .iter()
680            .enumerate()
681            .for_each(|(prim_id, node_id)| {
682                if *node_id != INVALID {
683                    let prim_id = prim_id as u32;
684                    let node = &self.nodes[*node_id as usize];
685                    assert!(node.is_leaf());
686                    let start = node.first_index;
687                    let end = node.first_index + node.prim_count;
688                    let mut found = false;
689                    for node_prim_id in start..end {
690                        if prim_id == self.primitive_indices[node_prim_id as usize] {
691                            found = true;
692                            break;
693                        }
694                    }
695                    assert!(found, "prim_id {prim_id} not found")
696                }
697            });
698    }
699
700    /// Refit the BVH working up the tree from this node, ignoring leaves. (TODO add a version that checks leaves)
701    /// This recomputes the Aabbs for all the parents of the given node index.
702    /// This can only be used to refit when a single node has changed or moved.
703    pub fn refit_from(&mut self, mut index: usize) {
704        self.init_parents_if_uninit();
705        loop {
706            let node = &self.nodes[index];
707            if !node.is_leaf() {
708                let first_child_bbox = *self.nodes[node.first_index as usize].aabb();
709                let second_child_bbox = *self.nodes[node.first_index as usize + 1].aabb();
710                self.nodes[index].set_aabb(first_child_bbox.union(&second_child_bbox));
711            }
712            if index == 0 {
713                break;
714            }
715            index = self.parents[index] as usize;
716        }
717    }
718
719    /// Refit the BVH working up the tree from this node, ignoring leaves.
720    /// This recomputes the Aabbs for the parents of the given node index.
721    /// Halts if the parents are the same size. Panics in debug if some parents still needed to be resized.
722    /// This can only be used to refit when a single node has changed or moved.
723    pub fn refit_from_fast(&mut self, mut index: usize) {
724        self.init_parents_if_uninit();
725        let mut same_count = 0;
726        loop {
727            let node = &self.nodes[index];
728            if !node.is_leaf() {
729                let first_child_bbox = self.nodes[node.first_index as usize].aabb();
730                let second_child_bbox = self.nodes[node.first_index as usize + 1].aabb();
731                let new_aabb = first_child_bbox.union(second_child_bbox);
732                let node = &mut self.nodes[index];
733                if node.aabb() == &new_aabb {
734                    same_count += 1;
735                    #[cfg(not(debug_assertions))]
736                    if same_count == 2 {
737                        return;
738                    }
739                } else {
740                    debug_assert!(
741                        same_count < 2,
742                        "Some parents still needed refitting. Unideal fitting is occurring somewhere."
743                    );
744                }
745                node.set_aabb(new_aabb);
746            }
747            if index == 0 {
748                break;
749            }
750            index = self.parents[index] as usize;
751        }
752    }
753
754    /// Tell the children of the node at `node_id`, or the primitives it contains if it is a leaf,
755    /// that it is now at `node_id`. Needed after moving a node into a different slot in `Bvh2::nodes`.
756    ///
757    /// If the node is already available, prefer [`Bvh2::relink_node()`].
758    #[inline]
759    pub fn relink(&mut self, node_id: usize) {
760        let node = self.nodes[node_id];
761        self.relink_node(&node, node_id);
762    }
763
764    /// Tell the children of `node`, or the primitives it contains if it is a leaf,
765    /// that it is now at `node_id`. Needed after moving a node into a different slot in `Bvh2::nodes`.
766    ///
767    /// `node` must be the node that now occupies the `node_id` slot of `Bvh2::nodes`.
768    #[inline]
769    pub fn relink_node(&mut self, node: &Bvh2Node, node_id: usize) {
770        debug_assert_eq!(node.first_index, self.nodes[node_id].first_index);
771        debug_assert_eq!(node.prim_count, self.nodes[node_id].prim_count);
772        if node.is_leaf() {
773            // Tell primitives where their node went.
774            update_primitives_to_nodes_for_node(
775                node,
776                node_id,
777                &self.primitive_indices,
778                &mut self.primitives_to_nodes,
779            )
780        } else {
781            // Tell children where their parent went.
782            self.parents[node.first_index as usize] = node_id as u32;
783            self.parents[node.first_index as usize + 1] = node_id as u32;
784        }
785    }
786
787    /// Update node aabb and refit the BVH working up the tree from this node.
788    #[inline]
789    pub fn resize_node(&mut self, node_id: usize, aabb: Aabb) {
790        self.nodes[node_id].set_aabb(aabb);
791        self.refit_from_fast(node_id);
792    }
793
794    /// Find if there might be a better spot in the BVH for this node and move it there. The id of the reinserted node
795    /// does not changed.
796    #[inline]
797    pub fn reinsert_node(&mut self, node_id: usize) {
798        if node_id == 0 {
799            return;
800        }
801        let reinsertion = find_reinsertion(self, node_id);
802        if reinsertion.area_diff > 0.0 {
803            reinsertion::reinsert_node(self, reinsertion.from as usize, reinsertion.to as usize);
804            self.children_are_ordered_after_parents = false;
805        }
806    }
807
808    /// Get the count of active primitive indices.
809    /// when primitives are removed they are added to the `primitive_indices_freelist` so the
810    /// self.primitive_indices.len() may not represent the actual number of valid, active primitive_indices.
811    #[inline(always)]
812    pub fn active_primitive_indices_count(&self) -> usize {
813        self.primitive_indices.len() - self.primitive_indices_freelist.len()
814    }
815
816    /// direct_layout: The primitives are already laid out in bvh.primitive_indices order.
817    /// tight_fit: Requires that children nodes and primitives fit tightly in parents. This is ignored for primitives
818    ///     if the bvh uses spatial splits (tight_fit can still be set to `true`). This was added for validating
819    ///     refit_all().
820    pub fn validate<T: Boundable>(
821        &self,
822        primitives: &[T],
823        direct_layout: bool,
824        tight_fit: bool,
825    ) -> Bvh2ValidationResult {
826        let mut result = Bvh2ValidationResult {
827            direct_layout,
828            require_tight_fit: tight_fit,
829            ..Default::default()
830        };
831
832        if self.nodes.is_empty() {
833            assert!(self.parents.is_empty());
834            assert!(self.primitives_to_nodes.is_empty());
835            return result;
836        }
837
838        if !self.primitives_to_nodes.is_empty() {
839            self.validate_primitives_to_nodes();
840        }
841
842        if !self.parents.is_empty() {
843            self.validate_parents();
844        }
845
846        if !self.nodes.is_empty() {
847            self.validate_impl::<T>(primitives, &mut result, 0, 0, 0);
848        }
849        assert_eq!(result.discovered_nodes.len(), self.nodes.len());
850        assert_eq!(result.node_count, self.nodes.len());
851
852        // Ignore primitive_indices if this is a direct layout
853        if !direct_layout {
854            if result.discovered_primitives.is_empty() {
855                assert!(self.active_primitive_indices_count() == 0)
856            } else {
857                if !self.uses_spatial_splits {
858                    // If the bvh uses splits, a primitive can show up in multiple leaf nodes so there wont be a 1 to 1
859                    // correlation between the number of discovered primitives and the quantity in bvh.primitive_indices.
860                    let active_indices_count = self.active_primitive_indices_count();
861                    assert_eq!(result.discovered_primitives.len(), active_indices_count);
862                    assert_eq!(result.prim_count, active_indices_count);
863                }
864                // Check that the set of discovered_primitives is the same as the set in primitive_indices while
865                // ignoring empty slots in primitive_indices.
866                let primitive_indices_freeset: HashSet<&u32> =
867                    HashSet::from_iter(&self.primitive_indices_freelist);
868                for (slot, index) in self.primitive_indices.iter().enumerate() {
869                    let slot = slot as u32;
870                    if !primitive_indices_freeset.contains(&slot) {
871                        assert!(result.discovered_primitives.contains(index));
872                    }
873                }
874                let primitive_indices_set: HashSet<&u32> =
875                    HashSet::from_iter(self.primitive_indices.iter().filter(|i| **i != INVALID));
876                for discovered_prim_id in &result.discovered_primitives {
877                    assert!(primitive_indices_set.contains(discovered_prim_id))
878                }
879            }
880        }
881        assert!(
882            result.max_depth < self.max_depth as u32,
883            "result.max_depth ({}) must be less than self.max_depth ({})",
884            result.max_depth,
885            self.max_depth as u32
886        );
887        if result.max_depth > DEFAULT_MAX_STACK_DEPTH as u32 {
888            log::warn!(
889                "bvh depth is: {}, a depth beyond {} may be indicative of something pathological in the scene (like thousands of instances perfectly overlapping geometry) that will result in a BVH that is very slow to traverse.",
890                result.max_depth,
891                DEFAULT_MAX_STACK_DEPTH
892            );
893        }
894
895        if self.children_are_ordered_after_parents {
896            // Assert that children are always ordered after parents in self.nodes
897            let mut temp_parents = vec![];
898            let parents = if self.parents.is_empty() {
899                Bvh2::compute_parents(&self.nodes, &mut temp_parents);
900                &temp_parents
901            } else {
902                &self.parents
903            };
904
905            for node_id in (1..self.nodes.len()).rev() {
906                assert!(parents[node_id] < node_id as u32);
907            }
908        }
909
910        result
911    }
912
913    pub fn validate_impl<T: Boundable>(
914        &self,
915        primitives: &[T],
916        result: &mut Bvh2ValidationResult,
917        node_index: u32,
918        parent_index: u32,
919        current_depth: u32,
920    ) {
921        result.max_depth = result.max_depth.max(current_depth);
922        let parent_aabb = self.nodes[parent_index as usize].aabb();
923        result.discovered_nodes.insert(node_index);
924        let node = &self.nodes[node_index as usize];
925        result.node_count += 1;
926
927        if let Some(count) = result.nodes_at_depth.get(&current_depth) {
928            result.nodes_at_depth.insert(current_depth, count + 1);
929        } else {
930            result.nodes_at_depth.insert(current_depth, 1);
931        }
932
933        assert!(
934            node.aabb().min.cmpge(parent_aabb.min).all()
935                && node.aabb().max.cmple(parent_aabb.max).all(),
936            "Child {} does not fit in parent {}:\nchild:  {:?}\nparent: {:?}",
937            node_index,
938            parent_index,
939            node.aabb(),
940            parent_aabb
941        );
942
943        if node.is_leaf() {
944            result.leaf_count += 1;
945            if let Some(count) = result.leaves_at_depth.get(&current_depth) {
946                result.leaves_at_depth.insert(current_depth, count + 1);
947            } else {
948                result.leaves_at_depth.insert(current_depth, 1);
949            }
950            let mut temp_aabb = Aabb::empty();
951            for i in 0..node.prim_count {
952                result.prim_count += 1;
953                let mut prim_index = (node.first_index + i) as usize;
954                if result.direct_layout {
955                    result.discovered_primitives.insert(prim_index as u32);
956                } else {
957                    result
958                        .discovered_primitives
959                        .insert(self.primitive_indices[prim_index]);
960                }
961                // If using splits, primitives will extend outside the leaf in some cases.
962                if !self.uses_spatial_splits {
963                    if !result.direct_layout {
964                        prim_index = self.primitive_indices[prim_index] as usize
965                    }
966                    let prim_aabb = primitives[prim_index].aabb();
967                    temp_aabb = temp_aabb.union(&prim_aabb);
968                    assert!(
969                        prim_aabb.min.cmpge(node.aabb().min).all()
970                            && prim_aabb.max.cmple(node.aabb().max).all(),
971                        "Primitive {} does not fit in parent {}:\nprimitive: {:?}\nparent:    {:?}",
972                        prim_index,
973                        parent_index,
974                        prim_aabb,
975                        node.aabb()
976                    );
977                }
978            }
979            if result.require_tight_fit && !self.uses_spatial_splits {
980                assert_eq!(
981                    temp_aabb,
982                    *node.aabb(),
983                    "Primitive do not fit in tightly in parent {node_index}",
984                );
985            }
986        } else {
987            if result.require_tight_fit {
988                let left_id = node.first_index as usize;
989                let right_id = node.first_index as usize + 1;
990                let left_child_aabb = &self.nodes[left_id];
991                let right_child_aabb = &self.nodes[right_id];
992
993                assert_eq!(
994                    left_child_aabb.aabb().union(right_child_aabb.aabb()),
995                    *node.aabb(),
996                    "Children {left_id} & {right_id} do not fit in tightly in parent {node_index}",
997                );
998            }
999
1000            self.validate_impl::<T>(
1001                primitives,
1002                result,
1003                node.first_index,
1004                parent_index,
1005                current_depth + 1,
1006            );
1007            self.validate_impl::<T>(
1008                primitives,
1009                result,
1010                node.first_index + 1,
1011                parent_index,
1012                current_depth + 1,
1013            );
1014        }
1015    }
1016
1017    /// Basic debug print illustrating the bvh layout
1018    pub fn print_bvh(&self, node_index: usize, depth: usize) {
1019        let node = &self.nodes[node_index];
1020        if node.is_leaf() {
1021            println!(
1022                "{}{} leaf > {}",
1023                " ".repeat(depth),
1024                node_index,
1025                node.first_index
1026            )
1027        } else {
1028            println!(
1029                "{}{} inner > {}, {}",
1030                " ".repeat(depth),
1031                node_index,
1032                node.first_index,
1033                node.first_index + 1
1034            );
1035            self.print_bvh(node.first_index as usize, depth + 1);
1036            self.print_bvh(node.first_index as usize + 1, depth + 1);
1037        }
1038    }
1039
1040    /// Get the maximum depth of the BVH from the given node
1041    pub fn depth(&self, node_index: usize) -> usize {
1042        let node = &self.nodes[node_index];
1043        if node.is_leaf() {
1044            1
1045        } else {
1046            1 + self
1047                .depth(node.first_index as usize)
1048                .max(self.depth((node.first_index + 1) as usize))
1049        }
1050    }
1051}
1052
1053/// Update the `primitives_to_nodes` mappings for primitives contained in `node_id`. Does nothing if primitives_to_nodes
1054/// is not already init.
1055// Not a member of Bvh2 because of borrow issues when a reference to other things like parents is also taken.
1056// Maybe could be cleaner as a macro?
1057#[inline]
1058fn update_primitives_to_nodes_for_node(
1059    node: &Bvh2Node,
1060    node_id: usize,
1061    primitive_indices: &[u32],
1062    primitives_to_nodes: &mut [u32],
1063) {
1064    if !primitives_to_nodes.is_empty() {
1065        let start = node.first_index;
1066        let end = start + node.prim_count;
1067        for node_prim_id in start..end {
1068            let direct_prim_id = primitive_indices[node_prim_id as usize];
1069            primitives_to_nodes[direct_prim_id as usize] = node_id as u32;
1070        }
1071    }
1072}
1073
1074/// Result of Bvh2 validation. Contains various bvh stats.
1075#[derive(Default)]
1076pub struct Bvh2ValidationResult {
1077    /// The primitives are already laid out in bvh.primitive_indices order.
1078    pub direct_layout: bool,
1079    /// Require validation to ensure aabbs tightly fit children and primitives.
1080    pub require_tight_fit: bool,
1081    /// Set of primitives discovered though validation traversal.
1082    pub discovered_primitives: HashSet<u32>,
1083    /// Set of nodes discovered though validation traversal.
1084    pub discovered_nodes: HashSet<u32>,
1085    /// Total number of nodes discovered though validation traversal.
1086    pub node_count: usize,
1087    /// Total number of leaves discovered though validation traversal.
1088    pub leaf_count: usize,
1089    /// Total number of primitives discovered though validation traversal.
1090    pub prim_count: usize,
1091    /// Maximum hierarchical BVH depth discovered though validation traversal.
1092    pub max_depth: u32,
1093    /// Quantity of nodes found at each depth though validation traversal.
1094    pub nodes_at_depth: HashMap<u32, u32>,
1095    /// Quantity of leaves found at each depth though validation traversal.
1096    pub leaves_at_depth: HashMap<u32, u32>,
1097}
1098
1099impl fmt::Display for Bvh2ValidationResult {
1100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1101        writeln!(
1102            f,
1103            "GPU BVH Avg primitives/leaf: {:.3}",
1104            self.prim_count as f64 / self.leaf_count as f64
1105        )?;
1106
1107        writeln!(
1108            f,
1109            "\
1110node_count: {}
1111prim_count: {}
1112leaf_count: {}",
1113            self.node_count, self.prim_count, self.leaf_count
1114        )?;
1115
1116        writeln!(f, "Node & Leaf counts for each depth")?;
1117        for i in 0..=self.max_depth {
1118            writeln!(
1119                f,
1120                "{:<3} {:<10} {:<10}",
1121                i,
1122                self.nodes_at_depth.get(&i).unwrap_or(&0),
1123                self.leaves_at_depth.get(&i).unwrap_or(&0)
1124            )?;
1125        }
1126
1127        Ok(())
1128    }
1129}
1130
1131#[cfg(test)]
1132mod tests {
1133
1134    use glam::*;
1135
1136    use crate::{
1137        BvhBuildParams, Transformable,
1138        ploc::{PlocBuilder, PlocSearchDistance, SortPrecision},
1139        test_util::geometry::demoscene,
1140    };
1141
1142    use super::builder::build_bvh2_from_tris;
1143
1144    #[test]
1145    fn test_refit_all() {
1146        let mut tris = demoscene(32, 0);
1147        let mut aabbs = Vec::with_capacity(tris.len());
1148        let mut indices = Vec::with_capacity(tris.len());
1149        for (i, primitive) in tris.iter().enumerate() {
1150            indices.push(i as u32);
1151            aabbs.push(primitive.aabb());
1152        }
1153
1154        // Test without init_primitives_to_nodes & init_parents
1155        let mut bvh = PlocBuilder::new().build(
1156            PlocSearchDistance::VeryLow,
1157            &aabbs,
1158            indices.clone(),
1159            SortPrecision::U64,
1160            1,
1161        );
1162
1163        bvh.init_primitives_to_nodes_if_uninit();
1164        tris.transform(&Mat4::from_scale_rotation_translation(
1165            Vec3::splat(1.3),
1166            Quat::from_rotation_y(0.1),
1167            vec3(0.33, 0.3, 0.37),
1168        ));
1169        for (prim_id, tri) in tris.iter().enumerate() {
1170            bvh.nodes[bvh.primitives_to_nodes[prim_id] as usize].set_aabb(tri.aabb());
1171        }
1172
1173        bvh.refit_all();
1174
1175        bvh.validate(&tris, false, true);
1176    }
1177
1178    #[test]
1179    fn test_reinsert_node() {
1180        let tris = demoscene(32, 0);
1181
1182        let mut bvh = build_bvh2_from_tris(
1183            &tris,
1184            BvhBuildParams::fastest_build(),
1185            &mut Default::default(),
1186        );
1187
1188        bvh.init_primitives_to_nodes_if_uninit();
1189        bvh.init_parents_if_uninit();
1190
1191        for node_id in 1..bvh.nodes.len() {
1192            bvh.reinsert_node(node_id);
1193        }
1194
1195        bvh.validate(&tris, false, false);
1196    }
1197}