parry3d/partitioning/bvh/
bvh_traverse.rs

1use super::BvhNode;
2use crate::math::Real;
3use crate::partitioning::Bvh;
4use smallvec::SmallVec;
5
6const TRAVERSAL_STACK_SIZE: usize = 32;
7
8pub struct Leaves<'a, Check: Fn(&BvhNode) -> bool> {
9    tree: &'a Bvh,
10    next: Option<&'a BvhNode>,
11    stack: SmallVec<[&'a BvhNode; TRAVERSAL_STACK_SIZE]>,
12    check: Check,
13}
14
15impl<'a, Check: Fn(&BvhNode) -> bool> Iterator for Leaves<'a, Check> {
16    type Item = u32;
17    fn next(&mut self) -> Option<Self::Item> {
18        loop {
19            if self.next.is_none() {
20                self.next = self.stack.pop();
21            }
22
23            let node = self.next.take()?;
24
25            if node.is_leaf() {
26                return Some(node.children);
27            }
28
29            let children = &self.tree.nodes[node.children as usize];
30            let left = &children.left;
31            let right = &children.right;
32
33            if (self.check)(left) {
34                self.next = Some(left);
35            }
36
37            if (self.check)(right) {
38                if self.next.is_none() {
39                    self.next = Some(right);
40                } else {
41                    self.stack.push(right);
42                }
43            }
44        }
45    }
46}
47
48/// Cost associated to a BVH leaf during best-first traversal.
49pub trait BvhLeafCost {
50    /// The cost value associated to the leaf.
51    ///
52    /// Best-first searches for the leaf with the lowest cost.
53    fn cost(&self) -> Real;
54}
55
56impl BvhLeafCost for Real {
57    #[inline(always)]
58    fn cost(&self) -> Real {
59        *self
60    }
61}
62
63impl<T> BvhLeafCost for (Real, T) {
64    #[inline(always)]
65    fn cost(&self) -> Real {
66        self.0
67    }
68}
69
70impl Bvh {
71    /// Iterates through the leaves, in depth-first order.
72    ///
73    /// The `check_node` closure is called on every traversed node. If it returns `false` then the
74    /// node and all its descendants won’t be iterated on. This is useful for pruning whole
75    /// sub-trees based on a geometric predicate on the node’s AABB.
76    ///
77    /// See also the [`Bvh::traverse`] function which is slightly less convenient since it doesn’t
78    /// rely on the iterator system, but takes a closure that implements [`FnMut`] instead of [`Fn`].
79    pub fn leaves<F: Fn(&BvhNode) -> bool>(&self, check_node: F) -> Leaves<'_, F> {
80        if let Some(root) = self.nodes.first() {
81            let mut stack = SmallVec::default();
82            if root.right.leaf_count() > 0 {
83                stack.push(&root.right);
84            }
85
86            Leaves {
87                tree: self,
88                next: Some(&root.left),
89                stack,
90                check: check_node,
91            }
92        } else {
93            Leaves {
94                tree: self,
95                next: None,
96                stack: Default::default(),
97                check: check_node,
98            }
99        }
100    }
101}
102
103/// Controls the execution flowo of [`Bvh::traverse`].
104pub enum TraversalAction {
105    /// The traversal will continue on the children of the tested node.
106    Continue,
107    /// The traversal will skip all descendants of the tested node.
108    Prune,
109    /// The traversal will exit immediately.
110    EarlyExit,
111}
112
113impl Bvh {
114    #[inline(always)]
115    pub(crate) fn traversal_stack() -> SmallVec<[u32; 32]> {
116        Default::default()
117    }
118
119    /// Traverse the tree in depth-first order.
120    ///
121    /// The `check_node` closure is called on every traversed node. The returned [`TraversalAction`]
122    /// controls whether a given node (and all its subtree) needs to be traversed, skipped, or if
123    /// the traversal needs to exit immediately (for example if you were looking for only one
124    /// particular node).
125    ///
126    /// See also the [`Bvh::leaves`] iterator which is a more convenient way of traversing the tree,
127    /// but is slightly limited in terms of node checking. In particular the closure
128    /// given to [`Bvh::traverse`] is mutable can hold any state so its check can depend on previous
129    /// execution of itself during the traversal.
130    pub fn traverse(&self, mut check_node: impl FnMut(&BvhNode) -> TraversalAction) {
131        let mut stack = Self::traversal_stack();
132        let mut curr_id = 0;
133
134        if self.nodes.is_empty() {
135            return;
136        } else if self.nodes[0].right.leaf_count() == 0 {
137            // Special case for partial root.
138            let _ = check_node(&self.nodes[0].left);
139            return;
140        }
141
142        loop {
143            let node = &self.nodes[curr_id as usize];
144            let left = &node.left;
145            let right = &node.right;
146            let go_left = match check_node(left) {
147                TraversalAction::Continue => !left.is_leaf(),
148                TraversalAction::Prune => false,
149                TraversalAction::EarlyExit => return,
150            };
151            let go_right = match check_node(right) {
152                TraversalAction::Continue => !right.is_leaf(),
153                TraversalAction::Prune => false,
154                TraversalAction::EarlyExit => return,
155            };
156
157            match (go_left, go_right) {
158                (true, true) => {
159                    curr_id = left.children;
160                    stack.push(right.children);
161                }
162                (true, false) => curr_id = left.children,
163                (false, true) => curr_id = right.children,
164                (false, false) => {
165                    let Some(next) = stack.pop() else {
166                        return;
167                    };
168                    curr_id = next;
169                }
170            }
171        }
172    }
173
174    /// Find the leaf that minimizes their associated cost.
175    pub fn find_best<L: BvhLeafCost>(
176        &self,
177        max_cost: Real,
178        aabb_cost: impl Fn(&BvhNode, Real) -> Real,
179        leaf_cost: impl Fn(u32, Real) -> Option<L>,
180    ) -> Option<(u32, L)> {
181        // A stack with 32 elements should be more than enough in most cases.
182        let mut stack = Self::traversal_stack();
183        let mut best_val = None;
184        let mut best_cost = max_cost;
185        let mut best_id = u32::MAX;
186        let mut curr_id = 0;
187
188        if self.nodes.is_empty() {
189            return None;
190        } else if self.nodes[0].right.leaf_count() == 0 {
191            // Special case for partial root.
192            let leaf = &self.nodes[0].left;
193            if aabb_cost(leaf, max_cost) < max_cost {
194                let cost = leaf_cost(leaf.children, best_cost)?;
195                return (cost.cost() < max_cost).then_some((leaf.children, cost));
196            } else {
197                return None;
198            }
199        }
200
201        loop {
202            let node = &self.nodes[curr_id as usize];
203            let mut left = &node.left;
204            let mut right = &node.right;
205
206            let mut left_score = aabb_cost(left, best_cost);
207            let mut right_score = aabb_cost(right, best_cost);
208
209            if left_score > right_score {
210                core::mem::swap(&mut left_score, &mut right_score);
211                core::mem::swap(&mut left, &mut right);
212            }
213
214            let mut found_next = false;
215            if left_score < best_cost && left_score != Real::MAX {
216                if left.is_leaf() {
217                    if let Some(primitive_val) = leaf_cost(left.children, best_cost) {
218                        let primitive_score = primitive_val.cost();
219                        if primitive_score < best_cost {
220                            best_val = Some(primitive_val);
221                            best_cost = primitive_score;
222                            best_id = left.children;
223                        }
224                    }
225                } else {
226                    curr_id = left.children;
227                    found_next = true;
228                }
229            }
230
231            if right_score < best_cost && right_score != Real::MAX {
232                if right.is_leaf() {
233                    if let Some(primitive_val) = leaf_cost(right.children, best_cost) {
234                        let primitive_score = primitive_val.cost();
235                        if primitive_score < best_cost {
236                            best_val = Some(primitive_val);
237                            best_cost = primitive_score;
238                            best_id = right.children;
239                        }
240                    }
241                } else if found_next {
242                    stack.push(right.children);
243                } else {
244                    curr_id = right.children;
245                    found_next = true;
246                }
247            }
248
249            if !found_next {
250                if let Some(next) = stack.pop() {
251                    curr_id = next;
252                } else {
253                    return best_val.map(|val| (best_id, val));
254                }
255            }
256        }
257    }
258}