parry2d/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#[cfg(feature = "parallel")]
8use alloc::vec::Vec;
9
10/// Result of a leaf update through [`Bvh::insert_or_update_partially`] or
11/// [`Bvh::insert_with_change_detection`].
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub enum BvhLeafUpdateStatus {
14 /// The leaf already existed and its stored (fattened) AABB still contains the new
15 /// AABB: the tree was left completely untouched.
16 Unchanged,
17 /// The leaf already existed and its stored AABB was rewritten in place (with
18 /// [`Bvh::insert_or_update_partially`], its ancestors might no longer enclose it
19 /// until the next refit).
20 UpdatedInPlace,
21 /// The leaf didn't exist yet and was inserted (the tree topology changed).
22 Inserted,
23}
24
25impl Bvh {
26 /// Inserts a new leaf into the BVH or updates an existing one.
27 ///
28 /// If a leaf with the given `leaf_index` already exists in the tree, its AABB is updated
29 /// to the new value and the tree's internal nodes are adjusted to maintain correctness.
30 /// If the leaf doesn't exist, it's inserted into an optimal position based on the
31 /// Surface Area Heuristic (SAH).
32 ///
33 /// This operation automatically propagates AABB changes up the tree to maintain the
34 /// invariant that each internal node's AABB encloses all its descendants. For better
35 /// performance when updating many leaves, consider using [`insert_or_update_partially`]
36 /// followed by a single [`refit`] call.
37 ///
38 /// # Arguments
39 ///
40 /// * `aabb` - The Axis-Aligned Bounding Box for this leaf
41 /// * `leaf_index` - A unique identifier for this leaf (typically an object ID)
42 ///
43 /// # Performance
44 ///
45 /// - **Insert new leaf**: O(log n) average, O(n) worst case
46 /// - **Update existing leaf**: O(log n) for propagation up the tree
47 ///
48 /// # Examples
49 ///
50 /// ## Adding new objects
51 ///
52 /// ```
53 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
54 /// use parry3d::partitioning::Bvh;
55 /// use parry3d::bounding_volume::Aabb;
56 /// use parry3d::math::Vector;
57 ///
58 /// let mut bvh = Bvh::new();
59 ///
60 /// // Insert objects with custom IDs
61 /// bvh.insert(Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)), 100);
62 /// bvh.insert(Aabb::new(Vector::new(5.0, 0.0, 0.0), Vector::new(6.0, 1.0, 1.0)), 200);
63 /// bvh.insert(Aabb::new(Vector::new(10.0, 0.0, 0.0), Vector::new(11.0, 1.0, 1.0)), 300);
64 ///
65 /// assert_eq!(bvh.leaf_count(), 3);
66 /// # }
67 /// ```
68 ///
69 /// ## Updating object positions
70 ///
71 /// ```
72 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
73 /// use parry3d::partitioning::Bvh;
74 /// use parry3d::bounding_volume::Aabb;
75 /// use parry3d::math::Vector;
76 ///
77 /// let mut bvh = Bvh::new();
78 ///
79 /// // Insert an object
80 /// bvh.insert(Aabb::new(Vector::ZERO, Vector::new(1.0, 1.0, 1.0)), 42);
81 ///
82 /// // Simulate the object moving - just insert with the same ID
83 /// bvh.insert(Aabb::new(Vector::new(5.0, 0.0, 0.0), Vector::new(6.0, 1.0, 1.0)), 42);
84 ///
85 /// // The BVH still has only 1 leaf, but at the new position
86 /// assert_eq!(bvh.leaf_count(), 1);
87 /// # }
88 /// ```
89 ///
90 /// ## Bulk updates with better performance
91 ///
92 /// ```
93 /// # #[cfg(all(feature = "dim3", feature = "f32"))] {
94 /// use parry3d::partitioning::{Bvh, BvhWorkspace};
95 /// use parry3d::bounding_volume::Aabb;
96 /// use parry3d::math::Vector;
97 ///
98 /// let mut bvh = Bvh::new();
99 /// let mut workspace = BvhWorkspace::default();
100 ///
101 /// // Add initial objects
102 /// for i in 0..100 {
103 /// let aabb = Aabb::new(
104 /// Vector::new(i as f32, 0.0, 0.0),
105 /// Vector::new(i as f32 + 1.0, 1.0, 1.0)
106 /// );
107 /// bvh.insert(aabb, i);
108 /// }
109 ///
110 /// // For better performance on bulk updates, use insert_or_update_partially
111 /// // then refit once at the end
112 /// for i in 0..100 {
113 /// let aabb = Aabb::new(
114 /// Vector::new(i as f32 + 0.1, 0.0, 0.0),
115 /// Vector::new(i as f32 + 1.1, 1.0, 1.0)
116 /// );
117 /// bvh.insert_or_update_partially(aabb, i, 0.0);
118 /// }
119 /// bvh.refit(&mut workspace); // Update tree in one pass
120 /// # }
121 /// ```
122 ///
123 /// # Notes
124 ///
125 /// - Leaf indices can be any `u32` value - they don't need to be contiguous
126 /// - The same leaf index can only exist once in the tree
127 /// - For dynamic scenes, call this every frame for moving objects
128 /// - Consider calling [`refit`] or [`optimize_incremental`] periodically for best
129 /// query performance
130 ///
131 /// # See Also
132 ///
133 /// - [`insert_with_change_detection`](Self::insert_with_change_detection) - Insert with
134 /// margin for motion prediction
135 /// - [`insert_or_update_partially`](Self::insert_or_update_partially) - Insert without
136 /// propagation (faster for bulk updates)
137 /// - [`remove`](Self::remove) - Remove a leaf from the tree
138 /// - [`refit`](Self::refit) - Update tree after bulk modifications
139 ///
140 /// [`insert_or_update_partially`]: Self::insert_or_update_partially
141 /// [`refit`]: Self::refit
142 /// [`optimize_incremental`]: Self::optimize_incremental
143 pub fn insert(&mut self, aabb: Aabb, leaf_index: u32) {
144 let _ = self.insert_with_change_detection(aabb, leaf_index, 0.0);
145 }
146
147 /// Inserts a leaf into this BVH, or updates it if already exists.
148 ///
149 /// If the `aabb` is already contained by the existing leaf node AABB, nothing is modified.
150 /// Otherwise, the aabb being effectively inserted is equal to `aabb` enlarged by the
151 /// `change_detection_margin`.
152 pub fn insert_with_change_detection(
153 &mut self,
154 aabb: Aabb,
155 leaf_index: u32,
156 change_detection_margin: Real,
157 ) -> BvhLeafUpdateStatus {
158 if let Some(leaf) = self.leaf_node_indices.get(leaf_index as usize) {
159 let node = &mut self.nodes[*leaf];
160
161 if change_detection_margin > 0.0 {
162 if !node.contains_aabb(&aabb) {
163 node.mins = aabb.mins - Vector::splat(change_detection_margin);
164 node.maxs = aabb.maxs + Vector::splat(change_detection_margin);
165 node.data.set_change_pending();
166 } else {
167 // No change detected, no propagation needed.
168 return BvhLeafUpdateStatus::Unchanged;
169 }
170 } else {
171 node.mins = aabb.mins;
172 node.maxs = aabb.maxs;
173 }
174
175 // Propagate up.
176 // TODO: maybe we should offer multiple propagation strategy.
177 // The one we currently implement simply stops as soon as a
178 // parent node contains the given `aabb`, but it won’t try
179 // to make the parent AABBs smaller even if we could.
180 // There could be two additional strategies that are slower but would leave the
181 // tree in a tighter state:
182 // - Make the parent smaller if possible by merging the aabb with
183 // the sibling.
184 // - In addition to merging with the sibling, we could apply bottom-up
185 // tree rotations to optimize part of the tree on our way up to the
186 // root.
187 let wide_node_id = leaf.decompose().0;
188 if wide_node_id == 0 {
189 // Already at the root, no propagation possible.
190 return BvhLeafUpdateStatus::UpdatedInPlace;
191 }
192
193 let mut parent = self.parents[wide_node_id];
194 loop {
195 let node = &mut self.nodes[parent];
196 if node.contains_aabb(&aabb) {
197 // No more propagation needed, the parent is big enough.
198 break;
199 }
200
201 node.mins = node.mins.min(aabb.mins);
202 node.maxs = node.maxs.max(aabb.maxs);
203
204 let wide_node_id = parent.decompose().0;
205 if wide_node_id == 0 {
206 break;
207 }
208
209 parent = self.parents[wide_node_id];
210 }
211
212 BvhLeafUpdateStatus::UpdatedInPlace
213 } else {
214 self.insert_new_unchecked(aabb, leaf_index);
215 BvhLeafUpdateStatus::Inserted
216 }
217 }
218
219 /// Either inserts a node on this tree, or, if it already exists, updates its associated bounding
220 /// but doesn’t update its ascendant nodes.
221 ///
222 /// This method is primarily designed to be called for inserting new nodes or updating existing
223 /// ones, and then running a [`Bvh::refit`]. Until [`Bvh::refit`] or [`Bvh::refit_without_opt`]
224 /// is called, the BVH will effectively be left in an invalid state where some internal nodes
225 /// might no longer enclose their children.
226 ///
227 /// For an alternative that inserts a node while also making sure all its ascendants are
228 /// up to date, see [`Bvh::insert`].
229 pub fn insert_or_update_partially(
230 &mut self,
231 aabb: Aabb,
232 leaf_index: u32,
233 change_detection_margin: Real,
234 ) -> BvhLeafUpdateStatus {
235 match self.update_partially_if_present(aabb, leaf_index, change_detection_margin) {
236 Some(status) => status,
237 None => {
238 self.insert_new_unchecked(aabb, leaf_index);
239 BvhLeafUpdateStatus::Inserted
240 }
241 }
242 }
243
244 /// [`Self::insert_or_update_partially`] restricted to leaves already in the tree:
245 /// returns `None`, leaving the tree untouched, when `leaf_index` has no leaf yet.
246 ///
247 /// Lets a caller apply every in-place update before any structural insertion — the
248 /// order `insert_or_update_batch_partially_parallel` imposes, since its updates
249 /// run concurrently — without paying a second lookup to find out which updates are
250 /// insertions.
251 pub fn update_partially_if_present(
252 &mut self,
253 aabb: Aabb,
254 leaf_index: u32,
255 change_detection_margin: Real,
256 ) -> Option<BvhLeafUpdateStatus> {
257 let leaf = *self.leaf_node_indices.get(leaf_index as usize)?;
258 let node = &mut self.nodes[leaf];
259
260 if change_detection_margin > 0.0 {
261 if !node.contains_aabb(&aabb) {
262 node.mins = aabb.mins - Vector::splat(change_detection_margin);
263 node.maxs = aabb.maxs + Vector::splat(change_detection_margin);
264 node.data.set_change_pending();
265 Some(BvhLeafUpdateStatus::UpdatedInPlace)
266 } else {
267 // The new AABB is still inside the leaf's fat AABB: the tree
268 // is left untouched.
269 Some(BvhLeafUpdateStatus::Unchanged)
270 }
271 } else {
272 node.mins = aabb.mins;
273 node.maxs = aabb.maxs;
274 Some(BvhLeafUpdateStatus::UpdatedInPlace)
275 }
276 }
277
278 /// Batch, parallel version of [`Self::insert_or_update_partially`].
279 ///
280 /// Applies every update whose leaf already exists in parallel (existing
281 /// leaves are updated in place: distinct leaf indices map to distinct
282 /// [`BvhNode`]s, so the writes are disjoint — two sibling leaves share a
283 /// wide node but occupy its two disjoint halves), then inserts the new
284 /// leaves sequentially. `statuses` is filled with one entry per update, in
285 /// order.
286 ///
287 /// Like [`Self::insert_or_update_partially`], the ascendants of updated
288 /// leaves are **not** updated: the tree must be refitted (e.g.
289 /// [`Bvh::refit`]) before the next query for its results to be exact.
290 ///
291 /// # Panics
292 ///
293 /// May panic (or leave arbitrary leaf AABBs, but no memory unsafety beyond
294 /// a torn AABB) if the same leaf index appears twice in `updates`.
295 #[cfg(feature = "parallel")]
296 pub fn insert_or_update_batch_partially_parallel(
297 &mut self,
298 updates: &[(Aabb, u32, Real)],
299 statuses: &mut Vec<BvhLeafUpdateStatus>,
300 ) {
301 use rayon::prelude::*;
302
303 statuses.clear();
304 statuses.resize(updates.len(), BvhLeafUpdateStatus::Unchanged);
305
306 // Phase 1 (parallel): in-place updates of the existing leaves.
307 struct NodesPtr(*mut BvhNodeWide);
308 unsafe impl Sync for NodesPtr {}
309 let nodes = &NodesPtr(self.nodes.0.as_mut_ptr());
310 let leaf_node_indices = &self.leaf_node_indices;
311
312 updates.par_iter().zip(statuses.par_iter_mut()).for_each(
313 move |((aabb, leaf_index, change_detection_margin), status)| {
314 let Some(leaf) = leaf_node_indices.get(*leaf_index as usize) else {
315 // Structural change: deferred to the sequential phase below.
316 *status = BvhLeafUpdateStatus::Inserted;
317 return;
318 };
319 let (wide_id, is_right) = leaf.decompose();
320 // SAFETY: distinct leaf indices map to distinct (wide, side)
321 // slots, i.e. disjoint `BvhNode`s; `leaf_node_indices`
322 // is only read during this phase.
323 let wide = unsafe { &mut *nodes.0.add(wide_id) };
324 let node = if is_right {
325 &mut wide.right
326 } else {
327 &mut wide.left
328 };
329
330 if *change_detection_margin > 0.0 {
331 if !node.contains_aabb(aabb) {
332 node.mins = aabb.mins - Vector::splat(*change_detection_margin);
333 node.maxs = aabb.maxs + Vector::splat(*change_detection_margin);
334 node.data.set_change_pending();
335 *status = BvhLeafUpdateStatus::UpdatedInPlace;
336 } else {
337 // The new AABB is still inside the leaf's fat AABB: the
338 // tree is left untouched.
339 *status = BvhLeafUpdateStatus::Unchanged;
340 }
341 } else {
342 node.mins = aabb.mins;
343 node.maxs = aabb.maxs;
344 *status = BvhLeafUpdateStatus::UpdatedInPlace;
345 }
346 },
347 );
348
349 // Phase 2 (sequential): structural insertions.
350 for (update, status) in updates.iter().zip(statuses.iter()) {
351 if *status == BvhLeafUpdateStatus::Inserted {
352 self.insert_new_unchecked(update.0, update.1);
353 }
354 }
355 }
356
357 /// Inserts a new leaf into this BVH without checking if it already exists.
358 fn insert_new_unchecked(&mut self, aabb: Aabb, leaf_index: u32) {
359 let _ = self
360 .leaf_node_indices
361 .insert(leaf_index as usize, BvhNodeIndex::default());
362 let leaf_index_mut = &mut self.leaf_node_indices[leaf_index as usize];
363
364 // If the tree is empty, create the root.
365 if self.nodes.is_empty() {
366 self.nodes.push(BvhNodeWide {
367 left: BvhNode::leaf(aabb, leaf_index),
368 right: BvhNode::zeros(),
369 });
370 self.parents.push(BvhNodeIndex::default());
371 *leaf_index_mut = BvhNodeIndex::left(0);
372 return;
373 }
374
375 // If we have a root, but it is partial, just complete it.
376 if self.nodes[0].right.leaf_count() == 0 {
377 self.nodes[0].right = BvhNode::leaf(aabb, leaf_index);
378 *leaf_index_mut = BvhNodeIndex::right(0);
379 return;
380 }
381
382 // General case: traverse the tree to find room for the new leaf.
383 let mut curr_id = 0u32;
384 let mut path_taken = vec![];
385
386 const APPLY_ROTATIONS_DOWN: bool = true;
387 const APPLY_ROTATIONS_UP: bool = false;
388
389 loop {
390 if APPLY_ROTATIONS_UP {
391 path_taken.push(curr_id);
392 }
393
394 if APPLY_ROTATIONS_DOWN {
395 self.maybe_apply_rotation(curr_id);
396 }
397
398 let curr_node = &self.nodes[curr_id as usize];
399
400 // Need to determine the best side to insert our node.
401 let left = &curr_node.left;
402 let right = &curr_node.right;
403
404 let left_merged_aabb = left.aabb().merged(&aabb);
405 let right_merged_aabb = right.aabb().merged(&aabb);
406
407 let left_merged_vol = left_merged_aabb.volume();
408 let right_merged_vol = right_merged_aabb.volume();
409 let left_vol = left.aabb().volume();
410 let right_vol = right.aabb().volume();
411 let left_count = left.leaf_count();
412 let right_count = right.leaf_count();
413
414 // NOTE: when calculating the SAH cost, we don’t care about dividing by the
415 // parent’s volume since both compared costs use the same factor so
416 // ignoring it doesn’t affect the comparison.
417 let left_cost =
418 left_merged_vol * (left_count + 1) as Real + right_vol * right_count as Real;
419 let right_cost =
420 right_merged_vol * (right_count + 1) as Real + left_vol * left_count as Real;
421
422 // Insert into the branch with lowest post-insertion SAH cost.
423 // If the costs are equal, just pick the branch with the smallest leaf count.
424 if left_cost < right_cost || (left_cost == right_cost && left_count < right_count) {
425 // Insert left. The `left` node will become an internal node.
426 // We create a new wide leaf containing the current and new leaves and
427 // attach it to `left`.
428 if left.is_leaf() {
429 let wide_node = BvhNodeWide {
430 left: *left,
431 right: BvhNode::leaf(aabb, leaf_index),
432 };
433 let new_leaf_id = self.alloc_wide_node(wide_node, BvhNodeIndex::left(curr_id));
434
435 let left = &mut self.nodes[curr_id as usize].left;
436 self.leaf_node_indices[left.children as usize] =
437 BvhNodeIndex::left(new_leaf_id as u32);
438 self.leaf_node_indices[leaf_index as usize] =
439 BvhNodeIndex::right(new_leaf_id as u32);
440
441 left.children = new_leaf_id as u32;
442 left.data.add_leaf_count(1);
443 left.mins = left.mins.min(aabb.mins);
444 left.maxs = left.maxs.max(aabb.maxs);
445 break;
446 } else {
447 let left = &mut self.nodes[curr_id as usize].left;
448 curr_id = left.children;
449 left.data.add_leaf_count(1);
450 left.mins = left.mins.min(aabb.mins);
451 left.maxs = left.maxs.max(aabb.maxs);
452 }
453 } else {
454 // Insert right. The `right` node will become an internal node.
455 // We create a new wide leaf containing the current and new leaves and
456 // attach it to `right`.
457 if right.is_leaf() {
458 let new_node = BvhNodeWide {
459 left: BvhNode::leaf(aabb, leaf_index),
460 right: *right,
461 };
462 let new_leaf_id = self.alloc_wide_node(new_node, BvhNodeIndex::right(curr_id));
463
464 let right = &mut self.nodes[curr_id as usize].right;
465 self.leaf_node_indices[leaf_index as usize] =
466 BvhNodeIndex::left(new_leaf_id as u32);
467 self.leaf_node_indices[right.children as usize] =
468 BvhNodeIndex::right(new_leaf_id as u32);
469
470 right.children = new_leaf_id as u32;
471 right.data.add_leaf_count(1);
472 right.mins = right.mins.min(aabb.mins);
473 right.maxs = right.maxs.max(aabb.maxs);
474 break;
475 } else {
476 let right = &mut self.nodes[curr_id as usize].right;
477 curr_id = right.children;
478 right.data.add_leaf_count(1);
479 right.mins = right.mins.min(aabb.mins);
480 right.maxs = right.maxs.max(aabb.maxs);
481 }
482 }
483 }
484
485 if APPLY_ROTATIONS_UP {
486 while let Some(node) = path_taken.pop() {
487 self.maybe_apply_rotation(node);
488 }
489 }
490 }
491
492 /// Allocates a slot for a new wide node, preferring slots orphaned by earlier
493 /// leaf removals over growing the node array.
494 fn alloc_wide_node(&mut self, node: BvhNodeWide, parent: BvhNodeIndex) -> usize {
495 if let Some(free) = self.free_wide_nodes.pop() {
496 self.nodes[free as usize] = node;
497 self.parents[free as usize] = parent;
498 free as usize
499 } else {
500 let id = self.nodes.len();
501 self.nodes.push(node);
502 self.parents.push(parent);
503 id
504 }
505 }
506
507 /// Updates the leaf AABB like [`Self::insert_with_change_detection`], but
508 /// relocates the leaf through a removal + SAH re-insertion whenever its fattened
509 /// AABB must actually change.
510 ///
511 /// Unlike the in-place update of [`Self::insert_with_change_detection`] (which
512 /// keeps the leaf's tree position and only enlarges its ancestors), the
513 /// re-insertion picks a fresh position by SAH descent — including rotations —
514 /// so a stream of such updates keeps the tree quality high on its own, without
515 /// requiring periodic [`Self::optimize_incremental`] passes. The freed wide-node
516 /// slot is recycled by the re-insertion itself, so the node array doesn't grow.
517 ///
518 /// This is the preferred update path when only a small fraction of the leaves
519 /// move (the per-leaf cost is an O(log n) descent instead of O(1)); for bulk
520 /// updates, prefer in-place updates followed by a refit and periodic
521 /// optimization.
522 ///
523 /// Compatible with [`Self::refit_partial`] under the same rules as insertions.
524 pub fn reinsert_or_update_with_change_detection(
525 &mut self,
526 aabb: Aabb,
527 leaf_index: u32,
528 change_detection_margin: Real,
529 ) -> BvhLeafUpdateStatus {
530 match self.reinsert_or_update_if_present(aabb, leaf_index, change_detection_margin) {
531 Some(status) => status,
532 None => {
533 self.insert_new_unchecked(aabb, leaf_index);
534 BvhLeafUpdateStatus::Inserted
535 }
536 }
537 }
538
539 /// [`Self::reinsert_or_update_with_change_detection`] restricted to leaves already in
540 /// the tree: returns `None`, leaving the tree untouched, when `leaf_index` has no leaf
541 /// yet. See [`Self::update_partially_if_present`].
542 pub fn reinsert_or_update_if_present(
543 &mut self,
544 aabb: Aabb,
545 leaf_index: u32,
546 change_detection_margin: Real,
547 ) -> Option<BvhLeafUpdateStatus> {
548 let leaf = *self.leaf_node_indices.get(leaf_index as usize)?;
549 if self.nodes[leaf].contains_aabb(&aabb) {
550 return Some(BvhLeafUpdateStatus::Unchanged);
551 }
552
553 self.remove(leaf_index);
554 let fat_aabb = Aabb {
555 mins: aabb.mins - Vector::splat(change_detection_margin),
556 maxs: aabb.maxs + Vector::splat(change_detection_margin),
557 };
558 // The new leaf is created with a pending change flag, exactly like an
559 // in-place update that escaped its previous fattened AABB.
560 self.insert_new_unchecked(fat_aabb, leaf_index);
561 Some(BvhLeafUpdateStatus::UpdatedInPlace)
562 }
563
564 // Applies a tree rotation at the given `node` if this improves the SAH metric at that node.
565 fn maybe_apply_rotation(&mut self, node_id: u32) {
566 let node = self.nodes[node_id as usize];
567 let left = &node.left;
568 let right = &node.right;
569
570 let curr_score =
571 left.volume() * left.leaf_count() as Real + right.volume() * right.leaf_count() as Real;
572
573 macro_rules! eval_costs {
574 ($left: ident, $right: ident) => {
575 if !$left.is_leaf() {
576 let children = self.nodes[$left.children as usize];
577 let left_child = &children.left;
578 let right_child = &children.right;
579
580 // New SAH score after transforming [{left_child, right_child}, right]
581 // into [left_child, {right_child, right}].
582 let new_score1 = left_child.volume() * left_child.leaf_count() as Real
583 + right_child.merged_volume($right)
584 * (right_child.leaf_count() + $right.leaf_count()) as Real;
585
586 // New SAH score after transforming [{left_child, right_child}, right]
587 // into [right_child, {left_child, right}].
588 let new_score2 = right_child.volume() * right_child.leaf_count() as Real
589 + left_child.merged_volume($right)
590 * (left_child.leaf_count() + $right.leaf_count()) as Real;
591
592 if new_score1 < new_score2 {
593 (new_score1 - curr_score, true)
594 } else {
595 (new_score2 - curr_score, false)
596 }
597 } else {
598 (Real::MAX, false)
599 }
600 };
601 }
602
603 // Because of the rotation some leaves might have changed location.
604 // This a helper to update the `leaf_data` map accordingly.
605 macro_rules! set_leaf_data {
606 ($leaf_data_id: ident, $node_id: ident, $left_or_right: expr) => {
607 self.leaf_node_indices[$leaf_data_id as usize] =
608 BvhNodeIndex::new($node_id, $left_or_right);
609 };
610 }
611
612 // For right rotation.
613 let (rotation_score0, left_child_moves_up0) = eval_costs!(left, right);
614 // For left rotation.
615 let (rotation_score1, left_child_moves_up1) = eval_costs!(right, left);
616
617 if rotation_score0 < 0.0 || rotation_score1 < 0.0 {
618 // At least one of the rotations is worth it, apply the one with
619 // the best impact on SAH scoring.
620 if rotation_score0 < rotation_score1 {
621 // Apply RIGHT rotation.
622 let children_id = left.children;
623 let children = self.nodes[children_id as usize];
624 let left_child = &children.left;
625 let right_child = &children.right;
626
627 let right_is_leaf = right.is_leaf();
628 let left_child_is_leaf = left_child.is_leaf();
629 let right_child_is_leaf = right_child.is_leaf();
630
631 let right_leaf_data = right.children;
632 let left_child_leaf_data = left_child.children;
633 let right_child_leaf_data = right_child.children;
634
635 self.parents[children_id as usize] = BvhNodeIndex::right(node_id);
636
637 if left_child_moves_up0 {
638 // The left child moves into `left`, and `right` takes it place.
639 self.nodes[node_id as usize].left = *left_child;
640 self.nodes[children_id as usize].left = *right;
641 self.nodes[node_id as usize].right =
642 self.nodes[children_id as usize].merged(children_id);
643
644 if left_child_is_leaf {
645 self.leaf_node_indices[left_child_leaf_data as usize] =
646 BvhNodeIndex::left(node_id);
647 } else {
648 self.parents[left_child_leaf_data as usize] = BvhNodeIndex::left(node_id);
649 }
650 if right_is_leaf {
651 self.leaf_node_indices[right_leaf_data as usize] =
652 BvhNodeIndex::left(children_id);
653 } else {
654 self.parents[right_leaf_data as usize] = BvhNodeIndex::left(children_id);
655 }
656 } else {
657 // The right child moves into `left`, and `right` takes it place.
658 self.nodes[node_id as usize].left = *right_child;
659 self.nodes[children_id as usize].right = *right;
660 self.nodes[node_id as usize].right =
661 self.nodes[children_id as usize].merged(children_id);
662 if right_child_is_leaf {
663 self.leaf_node_indices[right_child_leaf_data as usize] =
664 BvhNodeIndex::left(node_id);
665 } else {
666 self.parents[right_child_leaf_data as usize] = BvhNodeIndex::left(node_id);
667 }
668 if right_is_leaf {
669 self.leaf_node_indices[right_leaf_data as usize] =
670 BvhNodeIndex::right(children_id);
671 } else {
672 self.parents[right_leaf_data as usize] = BvhNodeIndex::right(children_id);
673 }
674 }
675 } else {
676 // Apply LEFT rotation.
677 let children_id = right.children;
678 let children = self.nodes[children_id as usize];
679 let left_child = &children.left;
680 let right_child = &children.right;
681
682 let left_is_leaf = left.is_leaf();
683 let left_child_is_leaf = left_child.is_leaf();
684 let right_child_is_leaf = right_child.is_leaf();
685
686 let left_leaf_data = left.children;
687 let left_child_leaf_data = left_child.children;
688 let right_child_leaf_data = right_child.children;
689
690 self.parents[children_id as usize] = BvhNodeIndex::left(node_id);
691
692 if left_child_moves_up1 {
693 // The left child moves into `right`, and `left` takes it place.
694 self.nodes[node_id as usize].right = *left_child;
695 self.nodes[children_id as usize].left = *left;
696 self.nodes[node_id as usize].left =
697 self.nodes[children_id as usize].merged(children_id);
698 if left_child_is_leaf {
699 self.leaf_node_indices[left_child_leaf_data as usize] =
700 BvhNodeIndex::right(node_id);
701 } else {
702 self.parents[left_child_leaf_data as usize] = BvhNodeIndex::right(node_id);
703 }
704 if left_is_leaf {
705 self.leaf_node_indices[left_leaf_data as usize] =
706 BvhNodeIndex::left(children_id);
707 } else {
708 self.parents[left_leaf_data as usize] = BvhNodeIndex::left(children_id);
709 }
710 } else {
711 // The right child moves into `right`, and `left` takes it place.
712 self.nodes[node_id as usize].right = *right_child;
713 self.nodes[children_id as usize].right = *left;
714 self.nodes[node_id as usize].left =
715 self.nodes[children_id as usize].merged(children_id);
716 if right_child_is_leaf {
717 set_leaf_data!(right_child_leaf_data, node_id, BvhNodeIndex::RIGHT);
718 } else {
719 self.parents[right_child_leaf_data as usize] = BvhNodeIndex::right(node_id);
720 }
721 if left_is_leaf {
722 set_leaf_data!(left_leaf_data, children_id, BvhNodeIndex::RIGHT);
723 } else {
724 self.parents[left_leaf_data as usize] = BvhNodeIndex::right(children_id);
725 }
726 }
727 }
728 }
729 }
730}