parry3d/partitioning/bvh/
bvh_traverse_bvtt.rs

1use super::{Bvh, BvhNode, BvhWorkspace};
2use smallvec::SmallVec;
3
4const TRAVERSAL_STACK_SIZE: usize = 32;
5
6impl Bvh {
7    /*
8     * Traversal of a tree against itself.
9     */
10    // NOTE PERF: change detection doesn’t make a huge difference in 2D (it can
11    //            occasionally even make it slower!) Once we support a static/dynamic tree
12    //            instead of a single tree, we might want to fully disable change detection
13    //            in 2D.
14    /// Traverses the Bounding Volume Test Tree of a tree against itself.
15    ///
16    /// The closure `f` will be called on each pair of leaf that passed the AABB intersection checks.
17    /// If `CHANGE_DETECTION` is `true`, then only pairs of leaves where at least one was detected
18    /// as changed during [`Self::insert_or_update_partially`] will be traversed.
19    pub fn traverse_bvtt_single_tree<const CHANGE_DETECTION: bool>(
20        &self,
21        workspace: &mut BvhWorkspace,
22        f: &mut impl FnMut(u32, u32),
23    ) {
24        if self.nodes.is_empty() || self.nodes[0].right.leaf_count() == 0 {
25            // Not enough nodes for any overlap.
26            return;
27        }
28
29        workspace.traversal_stack.clear();
30        self.self_intersect_node::<CHANGE_DETECTION>(workspace, 0, f)
31    }
32
33    // Traverses overlaps of a single node with itself.
34    // This as special case to:
35    // - Ensure we don’t traverse the same branch twice.
36    // - Only check the left/right overlap. Left/left and right/right checks trivially pass.
37    // TODO: take change detection into account.
38    fn self_intersect_node<const CHANGE_DETECTION: bool>(
39        &self,
40        workspace: &mut BvhWorkspace,
41        id: u32,
42        f: &mut impl FnMut(u32, u32),
43    ) {
44        let node = &self.nodes[id as usize];
45
46        if CHANGE_DETECTION && !node.right.is_changed() && !node.left.is_changed() {
47            return;
48        }
49
50        let left_right_intersect = node.left.intersects(&node.right);
51        let left_child = node.left.children;
52        let right_child = node.right.children;
53        let left_is_leaf = node.left.is_leaf();
54        let right_is_leaf = node.right.is_leaf();
55
56        if (!CHANGE_DETECTION || node.left.is_changed()) && !left_is_leaf {
57            self.self_intersect_node::<CHANGE_DETECTION>(workspace, left_child, f);
58        }
59
60        if (!CHANGE_DETECTION || node.right.is_changed()) && !right_is_leaf {
61            self.self_intersect_node::<CHANGE_DETECTION>(workspace, right_child, f);
62        }
63
64        if left_right_intersect {
65            match (left_is_leaf, right_is_leaf) {
66                (true, true) => f(left_child, right_child),
67                (true, false) => self.traverse_single_subtree::<CHANGE_DETECTION>(
68                    workspace,
69                    &node.left,
70                    right_child,
71                    f,
72                ),
73                (false, true) => self.traverse_single_subtree::<CHANGE_DETECTION>(
74                    workspace,
75                    &node.right,
76                    left_child,
77                    f,
78                ),
79                (false, false) => self.traverse_two_branches::<CHANGE_DETECTION>(
80                    workspace,
81                    left_child,
82                    right_child,
83                    f,
84                ),
85            }
86        }
87    }
88
89    fn traverse_two_branches<const CHANGE_DETECTION: bool>(
90        &self,
91        workspace: &mut BvhWorkspace,
92        a: u32,
93        b: u32,
94        f: &mut impl FnMut(u32, u32),
95    ) {
96        let node1 = &self.nodes[a as usize];
97        let node2 = &self.nodes[b as usize];
98
99        let left1 = &node1.left;
100        let right1 = &node1.right;
101        let left2 = &node2.left;
102        let right2 = &node2.right;
103
104        let left_left = (!CHANGE_DETECTION || left1.is_changed() || left2.is_changed())
105            && left1.intersects(left2);
106        let left_right = (!CHANGE_DETECTION || left1.is_changed() || right2.is_changed())
107            && left1.intersects(right2);
108        let right_left = (!CHANGE_DETECTION || right1.is_changed() || left2.is_changed())
109            && right1.intersects(left2);
110        let right_right = (!CHANGE_DETECTION || right1.is_changed() || right2.is_changed())
111            && right1.intersects(right2);
112
113        macro_rules! dispatch(
114            ($check: ident, $child_a: ident, $child_b: ident) => {
115                if $check {
116                    match ($child_a.is_leaf(), $child_b.is_leaf()) {
117                        (true, true) => f($child_a.children, $child_b.children),
118                        (true, false) => {
119                            self.traverse_single_subtree::<CHANGE_DETECTION>(workspace, $child_a, $child_b.children, f)
120                        }
121                        (false, true) => self.traverse_single_subtree::<CHANGE_DETECTION>(
122                            workspace,
123                            $child_b,
124                            $child_a.children,
125                            f,
126                        ),
127                        (false, false) => self.traverse_two_branches::<CHANGE_DETECTION>(
128                            workspace,
129                            $child_a.children,
130                            $child_b.children,
131                            f,
132                        ),
133                    }
134                }
135            }
136        );
137
138        dispatch!(left_left, left1, left2);
139        dispatch!(left_right, left1, right2);
140        dispatch!(right_left, right1, left2);
141        dispatch!(right_right, right1, right2);
142    }
143
144    // Checks overlap between a single node and a subtree.
145    fn traverse_single_subtree<const CHANGE_DETECTION: bool>(
146        &self,
147        workspace: &mut BvhWorkspace,
148        node: &BvhNode,
149        subtree: u32,
150        f: &mut impl FnMut(u32, u32),
151    ) {
152        debug_assert!(workspace.traversal_stack.is_empty());
153
154        // Since this is traversing against a single node it is more efficient to keep the leaf reference
155        // around and traverse the branch using a manual stack. Left branches are traversed by the main
156        // loop whereas the right branches are pushed to the stack.
157        let mut curr_id = subtree;
158        let node_changed = node.is_changed();
159
160        loop {
161            let curr = &self.nodes[curr_id as usize];
162            let left = &curr.left;
163            let right = &curr.right;
164            let left_check =
165                (!CHANGE_DETECTION || node_changed || left.is_changed()) && node.intersects(left);
166            let right_check =
167                (!CHANGE_DETECTION || node_changed || right.is_changed()) && node.intersects(right);
168            let left_is_leaf = left.is_leaf();
169            let right_is_leaf = right.is_leaf();
170            let mut found_next = false;
171
172            if left_check {
173                if left_is_leaf {
174                    f(node.children, left.children)
175                } else {
176                    curr_id = left.children;
177                    found_next = true;
178                }
179            }
180
181            if right_check {
182                if right_is_leaf {
183                    f(node.children, right.children)
184                } else if !found_next {
185                    curr_id = right.children;
186                    found_next = true;
187                } else {
188                    // We already advanced in curr_id once, push the other
189                    // branch to the stack.
190                    workspace.traversal_stack.push(right.children);
191                }
192            }
193
194            if !found_next {
195                // Pop the stack to find the next candidate.
196                if let Some(next_id) = workspace.traversal_stack.pop() {
197                    curr_id = next_id;
198                } else {
199                    // Traversal is finished.
200                    return;
201                }
202            }
203        }
204    }
205
206    /// Performs a simultaneous traversal of the BVHs `self` and `other`, and yields the pairs
207    /// of leaves it reached.
208    ///
209    /// Any node pairs failing the given `check` will be excluded from the traversal.
210    pub fn leaf_pairs<'a, F: Fn(&BvhNode, &BvhNode) -> bool>(
211        &'a self,
212        other: &'a Self,
213        check: F,
214    ) -> LeafPairs<'a, F> {
215        if let (Some(root1), Some(root2)) = (self.nodes.first(), other.nodes.first()) {
216            let mut stack = SmallVec::default();
217
218            if root1.left.leaf_count() > 0 && root2.right.leaf_count() > 0 {
219                stack.push((&root1.left, &root2.right));
220                // NOTE: we don’t need to push (&root1.left, &root2.left), it is already given as
221                //       the initial value of `LeafPairs::next`.
222                // stack.push((&root1.left, &root2.left))
223            }
224
225            if root1.right.leaf_count() > 0 {
226                if root2.right.leaf_count() > 0 {
227                    stack.push((&root1.right, &root2.right));
228                }
229                stack.push((&root1.right, &root2.left))
230            }
231
232            LeafPairs {
233                tree1: self,
234                tree2: other,
235                next: Some((&root1.left, &root2.left)),
236                stack,
237                check,
238            }
239        } else {
240            LeafPairs {
241                tree1: self,
242                tree2: other,
243                next: None,
244                stack: Default::default(),
245                check,
246            }
247        }
248    }
249}
250
251pub struct LeafPairs<'a, Check: Fn(&BvhNode, &BvhNode) -> bool> {
252    tree1: &'a Bvh,
253    tree2: &'a Bvh,
254    next: Option<(&'a BvhNode, &'a BvhNode)>,
255    stack: SmallVec<[(&'a BvhNode, &'a BvhNode); TRAVERSAL_STACK_SIZE]>,
256    check: Check,
257}
258
259impl<'a, Check: Fn(&BvhNode, &BvhNode) -> bool> Iterator for LeafPairs<'a, Check> {
260    type Item = (u32, u32);
261    fn next(&mut self) -> Option<Self::Item> {
262        loop {
263            if self.next.is_none() {
264                self.next = self.stack.pop();
265            }
266
267            let (node1, node2) = self.next.take()?;
268
269            match (node1.is_leaf(), node2.is_leaf()) {
270                (true, true) => return Some((node1.children, node2.children)),
271                (true, false) => {
272                    let child2 = &self.tree2.nodes[node2.children as usize];
273                    if (self.check)(node1, &child2.left) {
274                        self.next = Some((node1, &child2.left));
275                    }
276                    if (self.check)(node1, &child2.right) {
277                        if self.next.is_none() {
278                            self.next = Some((node1, &child2.right));
279                        } else {
280                            self.stack.push((node1, &child2.right));
281                        }
282                    }
283                }
284                (false, true) => {
285                    let child1 = &self.tree1.nodes[node1.children as usize];
286                    if (self.check)(&child1.left, node2) {
287                        self.next = Some((&child1.left, node2));
288                    }
289                    if (self.check)(&child1.right, node2) {
290                        if self.next.is_none() {
291                            self.next = Some((&child1.right, node2));
292                        } else {
293                            self.stack.push((&child1.right, node2));
294                        }
295                    }
296                }
297                (false, false) => {
298                    let child1 = &self.tree1.nodes[node1.children as usize];
299                    let child2 = &self.tree2.nodes[node2.children as usize];
300                    if (self.check)(&child1.left, &child2.left) {
301                        self.stack.push((&child1.left, &child2.left));
302                    }
303                    if (self.check)(&child1.right, &child2.left) {
304                        self.stack.push((&child1.right, &child2.left));
305                    }
306                    if (self.check)(&child1.left, &child2.right) {
307                        self.stack.push((&child1.left, &child2.right));
308                    }
309                    if (self.check)(&child1.right, &child2.right) {
310                        self.stack.push((&child1.right, &child2.right));
311                    }
312                }
313            }
314        }
315    }
316}