parry3d/partitioning/bvh/
bvh_insert.rs

1use super::bvh_tree::{BvhNodeIndex, BvhNodeWide};
2use super::BvhNode;
3use crate::bounding_volume::{Aabb, BoundingVolume};
4use crate::math::{Real, Vector};
5use crate::partitioning::Bvh;
6use alloc::vec;
7
8impl Bvh {
9    /// Inserts a leaf into this BVH, or updates it if already exists.
10    pub fn insert(&mut self, aabb: Aabb, leaf_index: u32) {
11        self.insert_with_change_detection(aabb, leaf_index, 0.0)
12    }
13
14    /// Inserts a leaf into this BVH, or updates it if already exists.
15    ///
16    /// If the `aabb` is already contained by the existing leaf node AABB, nothing is modified.
17    /// Otherwise, the aabb being effectively inserted is equal to `aabb` enlarged by the
18    /// `change_detection_margin`.
19    pub fn insert_with_change_detection(
20        &mut self,
21        aabb: Aabb,
22        leaf_index: u32,
23        change_detection_margin: Real,
24    ) {
25        if let Some(leaf) = self.leaf_node_indices.get(leaf_index as usize) {
26            let node = &mut self.nodes[*leaf];
27
28            if change_detection_margin > 0.0 {
29                if !node.contains_aabb(&aabb) {
30                    node.mins = aabb.mins - Vector::repeat(change_detection_margin);
31                    node.maxs = aabb.maxs + Vector::repeat(change_detection_margin);
32                    node.data.set_change_pending();
33                } else {
34                    // No change detected, no propagation needed.
35                    return;
36                }
37            } else {
38                node.mins = aabb.mins;
39                node.maxs = aabb.maxs;
40            }
41
42            // Propagate up.
43            // TODO: maybe we should offer multiple propagation strategy.
44            //       The one we currently implement simply stops as soon as a
45            //       parent node contains the given `aabb`, but it won’t try
46            //       to make the parent AABBs smaller even if we could.
47            //       There could be two additional strategies that are slower but would leave the
48            //       tree in a tighter state:
49            //       - Make the parent smaller if possible by merging the aabb with
50            //         the sibling.
51            //       - In addition to merging with the sibling, we could apply bottom-up
52            //         tree rotations to optimize part of the tree on our way up to the
53            //         root.
54            let wide_node_id = leaf.decompose().0;
55            if wide_node_id == 0 {
56                // Already at the root, no propagation possible.
57                return;
58            }
59
60            let mut parent = self.parents[wide_node_id];
61            loop {
62                let node = &mut self.nodes[parent];
63                if node.contains_aabb(&aabb) {
64                    // No more propagation needed, the parent is big enough.
65                    break;
66                }
67
68                node.mins = node.mins.inf(&aabb.mins);
69                node.maxs = node.maxs.sup(&aabb.maxs);
70
71                let wide_node_id = parent.decompose().0;
72                if wide_node_id == 0 {
73                    break;
74                }
75
76                parent = self.parents[wide_node_id];
77            }
78        } else {
79            self.insert_new_unchecked(aabb, leaf_index);
80        }
81    }
82
83    /// Either inserts a node on this tree, or, if it already exists, updates its associated bounding
84    /// but doesn’t update its ascendant nodes.
85    ///
86    /// This method is primarily designed to be called for inserting new nodes or updating existing
87    /// ones, and then running a [`Bvh::refit`]. Until [`Bvh::refit`] or [`Bvh::refit_without_opt`]
88    /// is called, the BVH will effectively be left in an invalid state where some internal nodes
89    /// might no longer enclose their children.
90    ///
91    /// For an alternative that inserts a node while also making sure all its ascendants are
92    /// up to date, see [`Bvh::insert`].
93    pub fn insert_or_update_partially(
94        &mut self,
95        aabb: Aabb,
96        leaf_index: u32,
97        change_detection_margin: Real,
98    ) {
99        if let Some(leaf) = self.leaf_node_indices.get(leaf_index as usize) {
100            let node = &mut self.nodes[*leaf];
101
102            if change_detection_margin > 0.0 {
103                if !node.contains_aabb(&aabb) {
104                    node.mins = aabb.mins - Vector::repeat(change_detection_margin);
105                    node.maxs = aabb.maxs + Vector::repeat(change_detection_margin);
106                    node.data.set_change_pending();
107                }
108            } else {
109                node.mins = aabb.mins;
110                node.maxs = aabb.maxs;
111            }
112        } else {
113            self.insert_new_unchecked(aabb, leaf_index);
114        }
115    }
116
117    /// Inserts a new leaf into this BVH without checking if it already exists.
118    fn insert_new_unchecked(&mut self, aabb: Aabb, leaf_index: u32) {
119        let _ = self
120            .leaf_node_indices
121            .insert(leaf_index as usize, BvhNodeIndex::default());
122        let leaf_index_mut = &mut self.leaf_node_indices[leaf_index as usize];
123
124        // If the tree is empty, create the root.
125        if self.nodes.is_empty() {
126            self.nodes.push(BvhNodeWide {
127                left: BvhNode::leaf(aabb, leaf_index),
128                right: BvhNode::zeros(),
129            });
130            self.parents.push(BvhNodeIndex::default());
131            *leaf_index_mut = BvhNodeIndex::left(0);
132            return;
133        }
134
135        // If we have a root, but it is partial, just complete it.
136        if self.nodes[0].right.leaf_count() == 0 {
137            self.nodes[0].right = BvhNode::leaf(aabb, leaf_index);
138            *leaf_index_mut = BvhNodeIndex::right(0);
139            return;
140        }
141
142        // General case: traverse the tree to find room for the new leaf.
143        let mut curr_id = 0u32;
144        let mut path_taken = vec![];
145
146        const APPLY_ROTATIONS_DOWN: bool = true;
147        const APPLY_ROTATIONS_UP: bool = false;
148
149        loop {
150            if APPLY_ROTATIONS_UP {
151                path_taken.push(curr_id);
152            }
153
154            if APPLY_ROTATIONS_DOWN {
155                self.maybe_apply_rotation(curr_id);
156            }
157
158            let curr_node = &self.nodes[curr_id as usize];
159
160            // Need to determine the best side to insert our node.
161            let left = &curr_node.left;
162            let right = &curr_node.right;
163
164            let left_merged_aabb = left.aabb().merged(&aabb);
165            let right_merged_aabb = right.aabb().merged(&aabb);
166
167            let left_merged_vol = left_merged_aabb.volume();
168            let right_merged_vol = right_merged_aabb.volume();
169            let left_vol = left.aabb().volume();
170            let right_vol = right.aabb().volume();
171            let left_count = left.leaf_count();
172            let right_count = right.leaf_count();
173
174            // NOTE: when calculating the SAH cost, we don’t care about dividing by the
175            //       parent’s volume since both compared costs use the same factor so
176            //       ignoring it doesn’t affect the comparison.
177            let left_cost =
178                left_merged_vol * (left_count + 1) as Real + right_vol * right_count as Real;
179            let right_cost =
180                right_merged_vol * (right_count + 1) as Real + left_vol * left_count as Real;
181
182            // Insert into the branch with lowest post-insertion SAH cost.
183            // If the costs are equal, just pick the branch with the smallest leaf count.
184            if left_cost < right_cost || (left_cost == right_cost && left_count < right_count) {
185                // Insert left. The `left` node will become an internal node.
186                // We create a new wide leaf containing the current and new leaves and
187                // attach it to `left`.
188                if left.is_leaf() {
189                    let new_leaf_id = self.nodes.len();
190                    let wide_node = BvhNodeWide {
191                        left: *left,
192                        right: BvhNode::leaf(aabb, leaf_index),
193                    };
194                    self.nodes.push(wide_node);
195                    self.parents.push(BvhNodeIndex::left(curr_id));
196
197                    let left = &mut self.nodes[curr_id as usize].left;
198                    self.leaf_node_indices[left.children as usize] =
199                        BvhNodeIndex::left(new_leaf_id as u32);
200                    self.leaf_node_indices[leaf_index as usize] =
201                        BvhNodeIndex::right(new_leaf_id as u32);
202
203                    left.children = new_leaf_id as u32;
204                    left.data.add_leaf_count(1);
205                    left.mins = left.mins.inf(&aabb.mins);
206                    left.maxs = left.maxs.sup(&aabb.maxs);
207                    break;
208                } else {
209                    let left = &mut self.nodes[curr_id as usize].left;
210                    curr_id = left.children;
211                    left.data.add_leaf_count(1);
212                    left.mins = left.mins.inf(&aabb.mins);
213                    left.maxs = left.maxs.sup(&aabb.maxs);
214                }
215            } else {
216                // Insert right. The `right` node will become an internal node.
217                // We create a new wide leaf containing the current and new leaves and
218                // attach it to `right`.
219                if right.is_leaf() {
220                    let new_leaf_id = self.nodes.len();
221                    let new_node = BvhNodeWide {
222                        left: BvhNode::leaf(aabb, leaf_index),
223                        right: *right,
224                    };
225                    self.nodes.push(new_node);
226                    self.parents.push(BvhNodeIndex::right(curr_id));
227
228                    let right = &mut self.nodes[curr_id as usize].right;
229                    self.leaf_node_indices[leaf_index as usize] =
230                        BvhNodeIndex::left(new_leaf_id as u32);
231                    self.leaf_node_indices[right.children as usize] =
232                        BvhNodeIndex::right(new_leaf_id as u32);
233
234                    right.children = new_leaf_id as u32;
235                    right.data.add_leaf_count(1);
236                    right.mins = right.mins.inf(&aabb.mins);
237                    right.maxs = right.maxs.sup(&aabb.maxs);
238                    break;
239                } else {
240                    let right = &mut self.nodes[curr_id as usize].right;
241                    curr_id = right.children;
242                    right.data.add_leaf_count(1);
243                    right.mins = right.mins.inf(&aabb.mins);
244                    right.maxs = right.maxs.sup(&aabb.maxs);
245                }
246            }
247        }
248
249        if APPLY_ROTATIONS_UP {
250            while let Some(node) = path_taken.pop() {
251                self.maybe_apply_rotation(node);
252            }
253        }
254    }
255
256    // Applies a tree rotation at the given `node` if this improves the SAH metric at that node.
257    fn maybe_apply_rotation(&mut self, node_id: u32) {
258        let node = self.nodes[node_id as usize];
259        let left = &node.left;
260        let right = &node.right;
261
262        let curr_score =
263            left.volume() * left.leaf_count() as Real + right.volume() * right.leaf_count() as Real;
264
265        macro_rules! eval_costs {
266            ($left: ident, $right: ident) => {
267                if !$left.is_leaf() {
268                    let children = self.nodes[$left.children as usize];
269                    let left_child = &children.left;
270                    let right_child = &children.right;
271
272                    // New SAH score after transforming [{left_child, right_child}, right]
273                    // into [left_child, {right_child, right}].
274                    let new_score1 = left_child.volume() * left_child.leaf_count() as Real
275                        + right_child.merged_volume($right)
276                            * (right_child.leaf_count() + $right.leaf_count()) as Real;
277
278                    // New SAH score after transforming [{left_child, right_child}, right]
279                    // into [right_child, {left_child, right}].
280                    let new_score2 = right_child.volume() * right_child.leaf_count() as Real
281                        + left_child.merged_volume($right)
282                            * (left_child.leaf_count() + $right.leaf_count()) as Real;
283
284                    if new_score1 < new_score2 {
285                        (new_score1 - curr_score, true)
286                    } else {
287                        (new_score2 - curr_score, false)
288                    }
289                } else {
290                    (Real::MAX, false)
291                }
292            };
293        }
294
295        // Because of the rotation some leaves might have changed location.
296        // This a helper to update the `leaf_data` map accordingly.
297        macro_rules! set_leaf_data {
298            ($leaf_data_id: ident, $node_id: ident, $left_or_right: expr) => {
299                self.leaf_node_indices[$leaf_data_id as usize] =
300                    BvhNodeIndex::new($node_id, $left_or_right);
301            };
302        }
303
304        // For right rotation.
305        let (rotation_score0, left_child_moves_up0) = eval_costs!(left, right);
306        // For left rotation.
307        let (rotation_score1, left_child_moves_up1) = eval_costs!(right, left);
308
309        if rotation_score0 < 0.0 || rotation_score1 < 0.0 {
310            // At least one of the rotations is worth it, apply the one with
311            // the best impact on SAH scoring.
312            if rotation_score0 < rotation_score1 {
313                // Apply RIGHT rotation.
314                let children_id = left.children;
315                let children = self.nodes[children_id as usize];
316                let left_child = &children.left;
317                let right_child = &children.right;
318
319                let right_is_leaf = right.is_leaf();
320                let left_child_is_leaf = left_child.is_leaf();
321                let right_child_is_leaf = right_child.is_leaf();
322
323                let right_leaf_data = right.children;
324                let left_child_leaf_data = left_child.children;
325                let right_child_leaf_data = right_child.children;
326
327                self.parents[children_id as usize] = BvhNodeIndex::right(node_id);
328
329                if left_child_moves_up0 {
330                    // The left child moves into `left`, and `right` takes it place.
331                    self.nodes[node_id as usize].left = *left_child;
332                    self.nodes[children_id as usize].left = *right;
333                    self.nodes[node_id as usize].right =
334                        self.nodes[children_id as usize].merged(children_id);
335
336                    if left_child_is_leaf {
337                        self.leaf_node_indices[left_child_leaf_data as usize] =
338                            BvhNodeIndex::left(node_id);
339                    } else {
340                        self.parents[left_child_leaf_data as usize] = BvhNodeIndex::left(node_id);
341                    }
342                    if right_is_leaf {
343                        self.leaf_node_indices[right_leaf_data as usize] =
344                            BvhNodeIndex::left(children_id);
345                    } else {
346                        self.parents[right_leaf_data as usize] = BvhNodeIndex::left(children_id);
347                    }
348                } else {
349                    // The right child moves into `left`, and `right` takes it place.
350                    self.nodes[node_id as usize].left = *right_child;
351                    self.nodes[children_id as usize].right = *right;
352                    self.nodes[node_id as usize].right =
353                        self.nodes[children_id as usize].merged(children_id);
354                    if right_child_is_leaf {
355                        self.leaf_node_indices[right_child_leaf_data as usize] =
356                            BvhNodeIndex::left(node_id);
357                    } else {
358                        self.parents[right_child_leaf_data as usize] = BvhNodeIndex::left(node_id);
359                    }
360                    if right_is_leaf {
361                        self.leaf_node_indices[right_leaf_data as usize] =
362                            BvhNodeIndex::right(children_id);
363                    } else {
364                        self.parents[right_leaf_data as usize] = BvhNodeIndex::right(children_id);
365                    }
366                }
367            } else {
368                // Apply LEFT rotation.
369                let children_id = right.children;
370                let children = self.nodes[children_id as usize];
371                let left_child = &children.left;
372                let right_child = &children.right;
373
374                let left_is_leaf = left.is_leaf();
375                let left_child_is_leaf = left_child.is_leaf();
376                let right_child_is_leaf = right_child.is_leaf();
377
378                let left_leaf_data = left.children;
379                let left_child_leaf_data = left_child.children;
380                let right_child_leaf_data = right_child.children;
381
382                self.parents[children_id as usize] = BvhNodeIndex::left(node_id);
383
384                if left_child_moves_up1 {
385                    // The left child moves into `right`, and `left` takes it place.
386                    self.nodes[node_id as usize].right = *left_child;
387                    self.nodes[children_id as usize].left = *left;
388                    self.nodes[node_id as usize].left =
389                        self.nodes[children_id as usize].merged(children_id);
390                    if left_child_is_leaf {
391                        self.leaf_node_indices[left_child_leaf_data as usize] =
392                            BvhNodeIndex::right(node_id);
393                    } else {
394                        self.parents[left_child_leaf_data as usize] = BvhNodeIndex::right(node_id);
395                    }
396                    if left_is_leaf {
397                        self.leaf_node_indices[left_leaf_data as usize] =
398                            BvhNodeIndex::left(children_id);
399                    } else {
400                        self.parents[left_leaf_data as usize] = BvhNodeIndex::left(children_id);
401                    }
402                } else {
403                    // The right child moves into `right`, and `left` takes it place.
404                    self.nodes[node_id as usize].right = *right_child;
405                    self.nodes[children_id as usize].right = *left;
406                    self.nodes[node_id as usize].left =
407                        self.nodes[children_id as usize].merged(children_id);
408                    if right_child_is_leaf {
409                        set_leaf_data!(right_child_leaf_data, node_id, BvhNodeIndex::RIGHT);
410                    } else {
411                        self.parents[right_child_leaf_data as usize] = BvhNodeIndex::right(node_id);
412                    }
413                    if left_is_leaf {
414                        set_leaf_data!(left_leaf_data, children_id, BvhNodeIndex::RIGHT);
415                    } else {
416                        self.parents[left_leaf_data as usize] = BvhNodeIndex::right(children_id);
417                    }
418                }
419            }
420        }
421    }
422}