1use oxiphysics_core::math::Vec3;
6
7use super::functions::{FAT_MARGIN, NodeIdx};
8
9pub(super) struct DbvtNode {
11 pub(super) aabb: BvhAabb,
13 pub(super) parent: Option<NodeIdx>,
15 pub(super) left: Option<NodeIdx>,
17 pub(super) right: Option<NodeIdx>,
19 pub(super) data: Option<u32>,
21 pub(super) height: i32,
23}
24impl DbvtNode {
25 fn leaf(aabb: BvhAabb, data: u32) -> Self {
26 Self {
27 aabb,
28 parent: None,
29 left: None,
30 right: None,
31 data: Some(data),
32 height: 0,
33 }
34 }
35 fn internal(aabb: BvhAabb, left: NodeIdx, right: NodeIdx) -> Self {
36 Self {
37 aabb,
38 parent: None,
39 left: Some(left),
40 right: Some(right),
41 data: None,
42 height: 1,
43 }
44 }
45 pub(crate) fn is_leaf(&self) -> bool {
46 self.data.is_some()
47 }
48}
49#[derive(Debug, Clone)]
51pub struct NearestLeaf {
52 pub data: u32,
54 pub dist_sq: f64,
56}
57#[derive(Debug, Clone, Copy)]
59pub struct BvhAabb {
60 pub min: Vec3,
62 pub max: Vec3,
64}
65impl BvhAabb {
66 #[inline]
68 pub fn new(min: Vec3, max: Vec3) -> Self {
69 Self { min, max }
70 }
71 #[inline]
73 pub fn point(p: Vec3) -> Self {
74 Self { min: p, max: p }
75 }
76 #[inline]
78 pub fn from_sphere(center: Vec3, radius: f64) -> Self {
79 let r = Vec3::new(radius, radius, radius);
80 Self {
81 min: center - r,
82 max: center + r,
83 }
84 }
85 #[inline]
87 pub fn center(&self) -> Vec3 {
88 (self.min + self.max) * 0.5
89 }
90 #[inline]
92 pub fn half_extents(&self) -> Vec3 {
93 (self.max - self.min) * 0.5
94 }
95 #[inline]
97 pub fn surface_area(&self) -> f64 {
98 let d = self.max - self.min;
99 2.0 * (d.x * d.y + d.y * d.z + d.z * d.x)
100 }
101 #[inline]
103 pub fn volume(&self) -> f64 {
104 let d = self.max - self.min;
105 d.x * d.y * d.z
106 }
107 #[inline]
109 pub fn contains(&self, other: &Self) -> bool {
110 self.min.x <= other.min.x
111 && self.min.y <= other.min.y
112 && self.min.z <= other.min.z
113 && self.max.x >= other.max.x
114 && self.max.y >= other.max.y
115 && self.max.z >= other.max.z
116 }
117 #[inline]
119 pub fn intersects(&self, other: &Self) -> bool {
120 self.min.x <= other.max.x
121 && self.max.x >= other.min.x
122 && self.min.y <= other.max.y
123 && self.max.y >= other.min.y
124 && self.min.z <= other.max.z
125 && self.max.z >= other.min.z
126 }
127 #[inline]
129 pub fn merge(a: &Self, b: &Self) -> Self {
130 Self {
131 min: Vec3::new(
132 a.min.x.min(b.min.x),
133 a.min.y.min(b.min.y),
134 a.min.z.min(b.min.z),
135 ),
136 max: Vec3::new(
137 a.max.x.max(b.max.x),
138 a.max.y.max(b.max.y),
139 a.max.z.max(b.max.z),
140 ),
141 }
142 }
143 #[inline]
145 pub fn expand(&self, margin: f64) -> Self {
146 let d = Vec3::new(margin, margin, margin);
147 Self {
148 min: self.min - d,
149 max: self.max + d,
150 }
151 }
152}
153impl BvhAabb {
154 #[inline]
156 pub fn longest_axis(&self) -> usize {
157 let d = self.max - self.min;
158 if d.x >= d.y && d.x >= d.z {
159 0
160 } else if d.y >= d.z {
161 1
162 } else {
163 2
164 }
165 }
166 #[inline]
168 pub fn approx_eq(&self, other: &Self, eps: f64) -> bool {
169 (self.min.x - other.min.x).abs() <= eps
170 && (self.min.y - other.min.y).abs() <= eps
171 && (self.min.z - other.min.z).abs() <= eps
172 && (self.max.x - other.max.x).abs() <= eps
173 && (self.max.y - other.max.y).abs() <= eps
174 && (self.max.z - other.max.z).abs() <= eps
175 }
176 #[inline]
180 pub fn point_dist_sq(&self, point: Vec3) -> f64 {
181 let dx = (point.x - self.min.x.max(self.max.x.min(point.x))).powi(2);
182 let dy = (point.y - self.min.y.max(self.max.y.min(point.y))).powi(2);
183 let dz = (point.z - self.min.z.max(self.max.z.min(point.z))).powi(2);
184 dx + dy + dz
185 }
186 #[inline]
188 pub fn translate(&self, offset: Vec3) -> Self {
189 Self {
190 min: self.min + offset,
191 max: self.max + offset,
192 }
193 }
194 #[inline]
196 pub fn scale(&self, factor: f64) -> Self {
197 let c = self.center();
198 let he = self.half_extents() * factor;
199 Self {
200 min: c - he,
201 max: c + he,
202 }
203 }
204}
205impl BvhAabb {
206 pub fn ray_intersect(&self, origin: Vec3, dir: Vec3, t_min: f64, t_max: f64) -> Option<f64> {
210 let orig = [origin.x, origin.y, origin.z];
211 let d = [dir.x, dir.y, dir.z];
212 let mn = [self.min.x, self.min.y, self.min.z];
213 let mx = [self.max.x, self.max.y, self.max.z];
214 let mut tmin = t_min;
215 let mut tmax = t_max;
216 for i in 0..3 {
217 if d[i].abs() < 1e-300 {
218 if orig[i] < mn[i] || orig[i] > mx[i] {
219 return None;
220 }
221 } else {
222 let inv = 1.0 / d[i];
223 let t1 = (mn[i] - orig[i]) * inv;
224 let t2 = (mx[i] - orig[i]) * inv;
225 let (ta, tb) = if t1 <= t2 { (t1, t2) } else { (t2, t1) };
226 tmin = tmin.max(ta);
227 tmax = tmax.min(tb);
228 if tmin > tmax {
229 return None;
230 }
231 }
232 }
233 if tmax < t_min {
234 return None;
235 }
236 Some(tmin.max(t_min))
237 }
238}
239#[derive(Debug, Clone, Default)]
241pub struct DbvtStats {
242 pub leaf_count: usize,
244 pub node_count: usize,
246 pub height: i32,
248 pub root_surface_area: f64,
250 pub overlap_pairs: usize,
252}
253#[derive(Debug, Clone)]
257pub struct BvhFrustum {
258 pub planes: [(Vec3, f64); 6],
260}
261impl BvhFrustum {
262 pub fn new(planes: [(Vec3, f64); 6]) -> Self {
264 Self { planes }
265 }
266 pub fn intersects_aabb(&self, aabb: &BvhAabb) -> bool {
268 for &(n, d) in &self.planes {
269 let px = if n.x >= 0.0 { aabb.max.x } else { aabb.min.x };
270 let py = if n.y >= 0.0 { aabb.max.y } else { aabb.min.y };
271 let pz = if n.z >= 0.0 { aabb.max.z } else { aabb.min.z };
272 let dot = n.x * px + n.y * py + n.z * pz;
273 if dot < d {
274 return false;
275 }
276 }
277 true
278 }
279}
280#[derive(Debug, Clone, Copy)]
282pub struct BvhCapsule {
283 pub start: Vec3,
285 pub end: Vec3,
287 pub radius: f64,
289}
290pub struct DynamicBvh {
296 pub(super) nodes: Vec<DbvtNode>,
297 pub(super) root: Option<NodeIdx>,
298 pub(super) free_list: Vec<NodeIdx>,
299}
300impl DynamicBvh {
301 pub fn new() -> Self {
303 Self {
304 nodes: Vec::new(),
305 root: None,
306 free_list: Vec::new(),
307 }
308 }
309 fn alloc_node(&mut self, node: DbvtNode) -> NodeIdx {
310 if let Some(idx) = self.free_list.pop() {
311 self.nodes[idx] = node;
312 idx
313 } else {
314 let idx = self.nodes.len();
315 self.nodes.push(node);
316 idx
317 }
318 }
319 fn free_node(&mut self, idx: NodeIdx) {
320 self.nodes[idx].parent = None;
321 self.nodes[idx].left = None;
322 self.nodes[idx].right = None;
323 self.nodes[idx].data = None;
324 self.free_list.push(idx);
325 }
326 pub fn insert(&mut self, aabb: BvhAabb, data: u32) -> NodeIdx {
331 let fat_aabb = aabb.expand(FAT_MARGIN);
332 let leaf_idx = self.alloc_node(DbvtNode::leaf(fat_aabb, data));
333 if self.root.is_none() {
334 self.root = Some(leaf_idx);
335 return leaf_idx;
336 }
337 let sibling = self.find_best_sibling(leaf_idx);
338 let old_parent = self.nodes[sibling].parent;
339 let new_aabb = BvhAabb::merge(&self.nodes[sibling].aabb, &self.nodes[leaf_idx].aabb);
340 let new_parent_idx = self.alloc_node(DbvtNode::internal(new_aabb, sibling, leaf_idx));
341 self.nodes[new_parent_idx].parent = old_parent;
342 self.nodes[sibling].parent = Some(new_parent_idx);
343 self.nodes[leaf_idx].parent = Some(new_parent_idx);
344 match old_parent {
345 None => {
346 self.root = Some(new_parent_idx);
347 }
348 Some(gp) => {
349 if self.nodes[gp].left == Some(sibling) {
350 self.nodes[gp].left = Some(new_parent_idx);
351 } else {
352 self.nodes[gp].right = Some(new_parent_idx);
353 }
354 }
355 }
356 self.refit(new_parent_idx);
357 leaf_idx
358 }
359 pub fn remove(&mut self, leaf: NodeIdx) {
364 assert!(self.nodes[leaf].is_leaf(), "remove: node is not a leaf");
365 let parent = self.nodes[leaf].parent;
366 match parent {
367 None => {
368 debug_assert_eq!(self.root, Some(leaf));
369 self.root = None;
370 self.free_node(leaf);
371 }
372 Some(parent_idx) => {
373 let grandparent = self.nodes[parent_idx].parent;
374 let sibling = if self.nodes[parent_idx].left == Some(leaf) {
375 self.nodes[parent_idx]
376 .right
377 .expect("internal node has no right child")
378 } else {
379 self.nodes[parent_idx]
380 .left
381 .expect("internal node has no left child")
382 };
383 self.nodes[sibling].parent = grandparent;
384 match grandparent {
385 None => {
386 self.root = Some(sibling);
387 }
388 Some(gp) => {
389 if self.nodes[gp].left == Some(parent_idx) {
390 self.nodes[gp].left = Some(sibling);
391 } else {
392 self.nodes[gp].right = Some(sibling);
393 }
394 self.refit(gp);
395 }
396 }
397 self.free_node(parent_idx);
398 self.free_node(leaf);
399 }
400 }
401 }
402 pub fn update(&mut self, leaf: NodeIdx, new_aabb: BvhAabb) -> bool {
408 assert!(self.nodes[leaf].is_leaf(), "update: node is not a leaf");
409 if self.nodes[leaf].aabb.contains(&new_aabb) {
410 return false;
411 }
412 let data = self.nodes[leaf].data.expect("leaf must have data");
413 self.remove(leaf);
414 self.insert(new_aabb, data);
415 true
416 }
417 pub fn query_overlapping_pairs(&self) -> Vec<(u32, u32)> {
420 let mut pairs = Vec::new();
421 let Some(root) = self.root else {
422 return pairs;
423 };
424 let mut leaves: Vec<NodeIdx> = Vec::new();
425 let mut stack = vec![root];
426 while let Some(idx) = stack.pop() {
427 let node = &self.nodes[idx];
428 if node.is_leaf() {
429 leaves.push(idx);
430 } else {
431 if let Some(l) = node.left {
432 stack.push(l);
433 }
434 if let Some(r) = node.right {
435 stack.push(r);
436 }
437 }
438 }
439 for (i, &la) in leaves.iter().enumerate() {
440 let aabb_a = self.nodes[la].aabb;
441 let data_a = match self.nodes[la].data {
442 Some(d) => d,
443 None => continue,
444 };
445 for &lb in &leaves[(i + 1)..] {
446 if aabb_a.intersects(&self.nodes[lb].aabb) {
447 let Some(data_b) = self.nodes[lb].data else {
448 continue;
449 };
450 let (x, y) = if data_a < data_b {
451 (data_a, data_b)
452 } else {
453 (data_b, data_a)
454 };
455 pairs.push((x, y));
456 }
457 }
458 }
459 pairs
460 }
461 pub fn query_aabb(&self, aabb: &BvhAabb) -> Vec<u32> {
463 let mut results = Vec::new();
464 if let Some(root) = self.root {
465 self.query_aabb_recursive(root, aabb, &mut results);
466 }
467 results
468 }
469 pub fn query_point(&self, point: Vec3) -> Vec<u32> {
471 let pt_aabb = BvhAabb::point(point);
472 self.query_aabb(&pt_aabb)
473 }
474 pub fn n_leaves(&self) -> usize {
476 self.nodes
477 .iter()
478 .enumerate()
479 .filter(|(idx, n)| n.is_leaf() && !self.free_list.contains(idx))
480 .count()
481 }
482 pub fn n_nodes(&self) -> usize {
484 self.nodes.len() - self.free_list.len()
485 }
486 pub fn height(&self) -> i32 {
488 match self.root {
489 None => 0,
490 Some(r) => self.nodes[r].height,
491 }
492 }
493 pub fn validate(&self) -> bool {
500 match self.root {
501 None => true,
502 Some(root) => {
503 if self.nodes[root].parent.is_some() {
504 return false;
505 }
506 self.validate_node(root)
507 }
508 }
509 }
510 fn find_best_sibling(&self, leaf: NodeIdx) -> NodeIdx {
512 let leaf_sa = self.nodes[leaf].aabb.surface_area();
513 let Some(root) = self.root else {
514 return leaf;
515 };
516 let mut best = root;
517 let mut best_cost =
518 BvhAabb::merge(&self.nodes[root].aabb, &self.nodes[leaf].aabb).surface_area();
519 let mut stack: Vec<(NodeIdx, f64)> = vec![(root, 0.0)];
520 while let Some((idx, inherited_cost)) = stack.pop() {
521 let node = &self.nodes[idx];
522 let direct_cost = BvhAabb::merge(&node.aabb, &self.nodes[leaf].aabb).surface_area();
523 let cost = direct_cost + inherited_cost;
524 if cost < best_cost {
525 best_cost = cost;
526 best = idx;
527 }
528 if !node.is_leaf() {
529 let child_inherited = inherited_cost + direct_cost - node.aabb.surface_area();
530 let lower_bound = leaf_sa + child_inherited;
531 if lower_bound < best_cost {
532 if let Some(l) = node.left {
533 stack.push((l, child_inherited));
534 }
535 if let Some(r) = node.right {
536 stack.push((r, child_inherited));
537 }
538 }
539 }
540 }
541 best
542 }
543 fn refit(&mut self, start: NodeIdx) {
545 let mut idx = Some(start);
546 while let Some(i) = idx {
547 let node = &self.nodes[i];
548 if node.is_leaf() {
549 idx = node.parent;
550 continue;
551 }
552 let (Some(left), Some(right)) = (node.left, node.right) else {
553 idx = node.parent;
554 continue;
555 };
556 let merged = BvhAabb::merge(&self.nodes[left].aabb, &self.nodes[right].aabb);
557 let height = 1 + self.nodes[left].height.max(self.nodes[right].height);
558 self.nodes[i].aabb = merged;
559 self.nodes[i].height = height;
560 idx = self.nodes[i].parent;
561 }
562 }
563 fn query_aabb_recursive(&self, idx: NodeIdx, aabb: &BvhAabb, out: &mut Vec<u32>) {
564 let node = &self.nodes[idx];
565 if !node.aabb.intersects(aabb) {
566 return;
567 }
568 if node.is_leaf() {
569 if let Some(d) = node.data {
570 out.push(d);
571 }
572 } else {
573 if let Some(l) = node.left {
574 self.query_aabb_recursive(l, aabb, out);
575 }
576 if let Some(r) = node.right {
577 self.query_aabb_recursive(r, aabb, out);
578 }
579 }
580 }
581 fn validate_node(&self, idx: NodeIdx) -> bool {
582 let node = &self.nodes[idx];
583 if node.is_leaf() {
584 return true;
585 }
586 let left = match node.left {
587 Some(l) => l,
588 None => return false,
589 };
590 let right = match node.right {
591 Some(r) => r,
592 None => return false,
593 };
594 if self.nodes[left].parent != Some(idx) {
595 return false;
596 }
597 if self.nodes[right].parent != Some(idx) {
598 return false;
599 }
600 if !node.aabb.contains(&self.nodes[left].aabb) {
601 return false;
602 }
603 if !node.aabb.contains(&self.nodes[right].aabb) {
604 return false;
605 }
606 self.validate_node(left) && self.validate_node(right)
607 }
608}
609impl DynamicBvh {
610 pub fn stats(&self) -> DbvtStats {
612 DbvtStats {
613 leaf_count: self.n_leaves(),
614 node_count: self.n_nodes(),
615 height: self.height(),
616 root_surface_area: self
617 .root
618 .map(|r| self.nodes[r].aabb.surface_area())
619 .unwrap_or(0.0),
620 overlap_pairs: 0,
621 }
622 }
623 pub fn refit_all(&mut self) {
627 if let Some(root) = self.root {
628 self.refit(root);
629 }
630 }
631 pub fn ray_cast(&self, origin: Vec3, dir: Vec3, t_min: f64, t_max: f64) -> Vec<(u32, f64)> {
635 let mut hits = Vec::new();
636 if let Some(root) = self.root {
637 self.ray_cast_recursive(root, origin, dir, t_min, t_max, &mut hits);
638 }
639 hits.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
640 hits
641 }
642 fn ray_cast_recursive(
643 &self,
644 idx: NodeIdx,
645 origin: Vec3,
646 dir: Vec3,
647 t_min: f64,
648 t_max: f64,
649 hits: &mut Vec<(u32, f64)>,
650 ) {
651 let node = &self.nodes[idx];
652 if let Some(t) = Self::ray_aabb_slab(origin, dir, node.aabb, t_min, t_max) {
653 if node.is_leaf() {
654 if let Some(d) = node.data {
655 hits.push((d, t));
656 }
657 } else {
658 if let Some(l) = node.left {
659 self.ray_cast_recursive(l, origin, dir, t_min, t_max, hits);
660 }
661 if let Some(r) = node.right {
662 self.ray_cast_recursive(r, origin, dir, t_min, t_max, hits);
663 }
664 }
665 }
666 }
667 fn ray_aabb_slab(
669 origin: Vec3,
670 dir: Vec3,
671 aabb: BvhAabb,
672 t_min: f64,
673 t_max: f64,
674 ) -> Option<f64> {
675 let mut tmin = t_min;
676 let mut tmax = t_max;
677 let orig = [origin.x, origin.y, origin.z];
678 let d = [dir.x, dir.y, dir.z];
679 let mn = [aabb.min.x, aabb.min.y, aabb.min.z];
680 let mx = [aabb.max.x, aabb.max.y, aabb.max.z];
681 for i in 0..3 {
682 if d[i].abs() < 1e-300 {
683 if orig[i] < mn[i] || orig[i] > mx[i] {
684 return None;
685 }
686 } else {
687 let inv = 1.0 / d[i];
688 let t1 = (mn[i] - orig[i]) * inv;
689 let t2 = (mx[i] - orig[i]) * inv;
690 let (ta, tb) = if t1 <= t2 { (t1, t2) } else { (t2, t1) };
691 tmin = tmin.max(ta);
692 tmax = tmax.min(tb);
693 if tmin > tmax {
694 return None;
695 }
696 }
697 }
698 if tmax < t_min {
699 return None;
700 }
701 Some(tmin.max(t_min))
702 }
703 pub fn query_aabb_filtered(&self, aabb: &BvhAabb, filter: impl Fn(u32) -> bool) -> Vec<u32> {
708 let mut results = Vec::new();
709 if let Some(root) = self.root {
710 self.query_aabb_filtered_recursive(root, aabb, &filter, &mut results);
711 }
712 results
713 }
714 fn query_aabb_filtered_recursive(
715 &self,
716 idx: NodeIdx,
717 aabb: &BvhAabb,
718 filter: &impl Fn(u32) -> bool,
719 out: &mut Vec<u32>,
720 ) {
721 let node = &self.nodes[idx];
722 if !node.aabb.intersects(aabb) {
723 return;
724 }
725 if node.is_leaf() {
726 if let Some(d) = node.data
727 && filter(d)
728 {
729 out.push(d);
730 }
731 } else {
732 if let Some(l) = node.left {
733 self.query_aabb_filtered_recursive(l, aabb, filter, out);
734 }
735 if let Some(r) = node.right {
736 self.query_aabb_filtered_recursive(r, aabb, filter, out);
737 }
738 }
739 }
740 pub fn insert_bulk(&mut self, items: &[(BvhAabb, u32)]) -> Vec<NodeIdx> {
742 items
743 .iter()
744 .map(|(aabb, data)| self.insert(*aabb, *data))
745 .collect()
746 }
747 pub fn clear(&mut self) {
749 self.nodes.clear();
750 self.free_list.clear();
751 self.root = None;
752 }
753 pub fn all_leaf_data(&self) -> Vec<u32> {
755 self.nodes
756 .iter()
757 .enumerate()
758 .filter(|(idx, n)| n.is_leaf() && !self.free_list.contains(idx))
759 .filter_map(|(_, n)| n.data)
760 .collect()
761 }
762 pub fn query_pairs_with_stats(&self) -> (Vec<(u32, u32)>, DbvtStats) {
764 let pairs = self.query_overlapping_pairs();
765 let mut s = self.stats();
766 s.overlap_pairs = pairs.len();
767 (pairs, s)
768 }
769}
770impl DynamicBvh {
771 pub fn quality_metrics(&self) -> Option<BvhQuality> {
775 let root = self.root?;
776 let root_sa = self.nodes[root].aabb.surface_area();
777 if root_sa <= 0.0 {
778 return None;
779 }
780 let mut sah_cost = 0.0_f64;
781 let mut max_depth = 0usize;
782 let mut min_leaf_depth = usize::MAX;
783 let mut total_leaf_depth = 0usize;
784 let mut leaf_count = 0usize;
785 let mut internal_count = 0usize;
786 let mut stack: Vec<(NodeIdx, usize)> = vec![(root, 0)];
787 while let Some((idx, depth)) = stack.pop() {
788 let node = &self.nodes[idx];
789 if depth > max_depth {
790 max_depth = depth;
791 }
792 if node.is_leaf() {
793 leaf_count += 1;
794 total_leaf_depth += depth;
795 if depth < min_leaf_depth {
796 min_leaf_depth = depth;
797 }
798 } else {
799 internal_count += 1;
800 let sa = node.aabb.surface_area();
801 sah_cost += sa / root_sa;
802 if let Some(l) = node.left {
803 stack.push((l, depth + 1));
804 }
805 if let Some(r) = node.right {
806 stack.push((r, depth + 1));
807 }
808 }
809 }
810 let avg_leaf_depth = if leaf_count > 0 {
811 total_leaf_depth as f64 / leaf_count as f64
812 } else {
813 0.0
814 };
815 let min_leaf_depth_out = if min_leaf_depth == usize::MAX {
816 0
817 } else {
818 min_leaf_depth
819 };
820 Some(BvhQuality {
821 sah_cost,
822 max_depth,
823 min_leaf_depth: min_leaf_depth_out,
824 avg_leaf_depth,
825 leaf_count,
826 internal_count,
827 })
828 }
829 pub fn quality_degraded(&self, threshold: f64) -> bool {
833 match self.quality_metrics() {
834 Some(q) => q.sah_cost > threshold,
835 None => false,
836 }
837 }
838}
839impl DynamicBvh {
840 pub fn frustum_query(&self, frustum: &BvhFrustum) -> Vec<u32> {
845 let mut results = Vec::new();
846 let Some(root) = self.root else {
847 return results;
848 };
849 let mut stack = vec![root];
850 while let Some(idx) = stack.pop() {
851 let node = &self.nodes[idx];
852 if !frustum.intersects_aabb(&node.aabb) {
853 continue;
854 }
855 if node.is_leaf() {
856 if let Some(d) = node.data {
857 results.push(d);
858 }
859 } else {
860 if let Some(l) = node.left {
861 stack.push(l);
862 }
863 if let Some(r) = node.right {
864 stack.push(r);
865 }
866 }
867 }
868 results
869 }
870}
871impl DynamicBvh {
872 pub fn k_nearest(&self, point: Vec3, k: usize) -> Vec<NearestLeaf> {
876 if k == 0 {
877 return Vec::new();
878 }
879 let mut candidates: Vec<NearestLeaf> = self
880 .nodes
881 .iter()
882 .enumerate()
883 .filter(|(idx, n)| n.is_leaf() && !self.free_list.contains(idx))
884 .filter_map(|(_, n)| {
885 let d = n.data?;
886 let c = n.aabb.center();
887 let dx = c.x - point.x;
888 let dy = c.y - point.y;
889 let dz = c.z - point.z;
890 Some(NearestLeaf {
891 data: d,
892 dist_sq: dx * dx + dy * dy + dz * dz,
893 })
894 })
895 .collect();
896 candidates.sort_by(|a, b| {
897 a.dist_sq
898 .partial_cmp(&b.dist_sq)
899 .unwrap_or(std::cmp::Ordering::Equal)
900 });
901 candidates.truncate(k);
902 candidates
903 }
904}
905impl DynamicBvh {
906 pub fn serialize(&self) -> Vec<f64> {
915 const STRIDE: usize = 14;
916 let mut buf = Vec::with_capacity(1 + self.nodes.len() * STRIDE);
917 buf.push(self.nodes.len() as f64);
918 for (idx, node) in self.nodes.iter().enumerate() {
919 buf.push(node.aabb.min.x);
920 buf.push(node.aabb.min.y);
921 buf.push(node.aabb.min.z);
922 buf.push(node.aabb.max.x);
923 buf.push(node.aabb.max.y);
924 buf.push(node.aabb.max.z);
925 buf.push(node.parent.map(|p| p as f64).unwrap_or(-1.0));
926 buf.push(node.left.map(|l| l as f64).unwrap_or(-1.0));
927 buf.push(node.right.map(|r| r as f64).unwrap_or(-1.0));
928 buf.push(node.data.map(|d| d as f64).unwrap_or(-1.0));
929 buf.push(node.height as f64);
930 buf.push(if node.is_leaf() { 1.0 } else { 0.0 });
931 buf.push(if self.free_list.contains(&idx) {
932 1.0
933 } else {
934 0.0
935 });
936 buf.push(0.0);
937 }
938 buf
939 }
940 pub fn root(&self) -> Option<NodeIdx> {
942 self.root
943 }
944}
945impl DynamicBvh {
946 pub fn move_proxy(
952 &mut self,
953 leaf: NodeIdx,
954 new_aabb: BvhAabb,
955 velocity: Vec3,
956 dt: f64,
957 ) -> bool {
958 assert!(self.nodes[leaf].is_leaf(), "move_proxy: node is not a leaf");
959 let displacement = Vec3::new(velocity.x * dt, velocity.y * dt, velocity.z * dt);
960 let predicted = BvhAabb {
961 min: Vec3::new(
962 new_aabb.min.x + displacement.x.min(0.0),
963 new_aabb.min.y + displacement.y.min(0.0),
964 new_aabb.min.z + displacement.z.min(0.0),
965 ),
966 max: Vec3::new(
967 new_aabb.max.x + displacement.x.max(0.0),
968 new_aabb.max.y + displacement.y.max(0.0),
969 new_aabb.max.z + displacement.z.max(0.0),
970 ),
971 };
972 let swept = BvhAabb::merge(&new_aabb, &predicted);
973 if self.nodes[leaf].aabb.contains(&swept) {
974 return false;
975 }
976 let data = self.nodes[leaf].data.expect("leaf must have data");
977 self.remove(leaf);
978 self.insert(swept, data);
979 true
980 }
981}
982impl DynamicBvh {
983 pub fn try_rotate(&mut self, idx: NodeIdx) -> bool {
991 let node = &self.nodes[idx];
992 if node.is_leaf() {
993 return false;
994 }
995 let left = match node.left {
996 Some(l) => l,
997 None => return false,
998 };
999 let right = match node.right {
1000 Some(r) => r,
1001 None => return false,
1002 };
1003 let current_cost =
1004 self.nodes[left].aabb.surface_area() + self.nodes[right].aabb.surface_area();
1005 let mut best_cost = current_cost;
1006 let mut best_rotation: Option<(NodeIdx, NodeIdx, NodeIdx)> = None;
1007 if !self.nodes[right].is_leaf() {
1008 let (Some(rl), Some(rr)) = (self.nodes[right].left, self.nodes[right].right) else {
1009 return false;
1010 };
1011 let new_right_sa =
1012 BvhAabb::merge(&self.nodes[left].aabb, &self.nodes[rr].aabb).surface_area();
1013 let candidate_cost = new_right_sa + self.nodes[left].aabb.surface_area();
1014 if candidate_cost < best_cost {
1015 best_cost = candidate_cost;
1016 best_rotation = Some((idx, left, rl));
1017 }
1018 let new_right_sa2 =
1019 BvhAabb::merge(&self.nodes[left].aabb, &self.nodes[rl].aabb).surface_area();
1020 let candidate_cost2 = new_right_sa2 + self.nodes[left].aabb.surface_area();
1021 if candidate_cost2 < best_cost {
1022 best_cost = candidate_cost2;
1023 best_rotation = Some((idx, left, rr));
1024 }
1025 }
1026 if !self.nodes[left].is_leaf() {
1027 let (Some(ll), Some(lr)) = (self.nodes[left].left, self.nodes[left].right) else {
1028 return false;
1029 };
1030 let new_left_sa =
1031 BvhAabb::merge(&self.nodes[right].aabb, &self.nodes[lr].aabb).surface_area();
1032 let candidate_cost = new_left_sa + self.nodes[right].aabb.surface_area();
1033 if candidate_cost < best_cost {
1034 best_cost = candidate_cost;
1035 best_rotation = Some((idx, right, ll));
1036 }
1037 let new_left_sa2 =
1038 BvhAabb::merge(&self.nodes[right].aabb, &self.nodes[ll].aabb).surface_area();
1039 let candidate_cost2 = new_left_sa2 + self.nodes[right].aabb.surface_area();
1040 if candidate_cost2 < best_cost {
1041 let _ = best_cost;
1042 best_rotation = Some((idx, right, lr));
1043 }
1044 }
1045 if let Some((parent_idx, child_in_parent, grandchild_to_swap)) = best_rotation {
1046 let Some(grandchild_parent) = self.nodes[grandchild_to_swap].parent else {
1047 return false;
1048 };
1049 let gp_left = self.nodes[grandchild_parent].left;
1050 let gp_right = self.nodes[grandchild_parent].right;
1051 let pl = self.nodes[parent_idx].left;
1052 let pr = self.nodes[parent_idx].right;
1053 if pl == Some(child_in_parent) {
1054 self.nodes[parent_idx].left = Some(grandchild_to_swap);
1055 } else if pr == Some(child_in_parent) {
1056 self.nodes[parent_idx].right = Some(grandchild_to_swap);
1057 }
1058 if gp_left == Some(grandchild_to_swap) {
1059 self.nodes[grandchild_parent].left = Some(child_in_parent);
1060 } else if gp_right == Some(grandchild_to_swap) {
1061 self.nodes[grandchild_parent].right = Some(child_in_parent);
1062 }
1063 self.nodes[grandchild_to_swap].parent = Some(parent_idx);
1064 self.nodes[child_in_parent].parent = Some(grandchild_parent);
1065 self.refit(grandchild_parent);
1066 self.refit(parent_idx);
1067 true
1068 } else {
1069 false
1070 }
1071 }
1072 pub fn optimize_rotations(&mut self) {
1076 let internal: Vec<NodeIdx> = self
1077 .nodes
1078 .iter()
1079 .enumerate()
1080 .filter(|(idx, n)| !n.is_leaf() && !self.free_list.contains(idx))
1081 .map(|(i, _)| i)
1082 .collect();
1083 let mut ordered = internal;
1084 ordered.sort_by_key(|&i| self.nodes[i].height);
1085 for idx in ordered {
1086 if !self.nodes[idx].is_leaf() {
1087 self.try_rotate(idx);
1088 }
1089 }
1090 }
1091}
1092impl DynamicBvh {
1093 pub fn refit_leaf_ancestors(&mut self, leaf: NodeIdx) {
1097 assert!(
1098 self.nodes[leaf].is_leaf(),
1099 "refit_leaf_ancestors: not a leaf"
1100 );
1101 if let Some(parent) = self.nodes[leaf].parent {
1102 self.refit(parent);
1103 }
1104 }
1105 pub fn update_leaf_inplace(&mut self, leaf: NodeIdx, new_aabb: BvhAabb) {
1112 assert!(
1113 self.nodes[leaf].is_leaf(),
1114 "update_leaf_inplace: not a leaf"
1115 );
1116 self.nodes[leaf].aabb = new_aabb;
1117 self.refit_leaf_ancestors(leaf);
1118 }
1119 pub fn sah_cost(&self) -> f64 {
1124 let Some(root) = self.root else {
1125 return 0.0;
1126 };
1127 let root_sa = self.nodes[root].aabb.surface_area();
1128 if root_sa <= 0.0 {
1129 return 0.0;
1130 }
1131 let mut cost = 0.0_f64;
1132 let mut stack = vec![root];
1133 while let Some(idx) = stack.pop() {
1134 let node = &self.nodes[idx];
1135 if !node.is_leaf() {
1136 cost += node.aabb.surface_area() / root_sa;
1137 if let Some(l) = node.left {
1138 stack.push(l);
1139 }
1140 if let Some(r) = node.right {
1141 stack.push(r);
1142 }
1143 }
1144 }
1145 cost
1146 }
1147}
1148impl DynamicBvh {
1149 pub fn compute_sah_cost(&self) -> f64 {
1157 self.sah_cost()
1158 }
1159 pub fn balance_rotation(&mut self) -> usize {
1168 let internal: Vec<NodeIdx> = self
1169 .nodes
1170 .iter()
1171 .enumerate()
1172 .filter(|(idx, n)| !n.is_leaf() && !self.free_list.contains(idx))
1173 .map(|(i, _)| i)
1174 .collect();
1175 let mut ordered = internal;
1176 ordered.sort_by_key(|&i| self.nodes[i].height);
1177 let mut accepted = 0usize;
1178 for idx in ordered {
1179 if !self.nodes[idx].is_leaf() && self.try_rotate(idx) {
1180 accepted += 1;
1181 }
1182 }
1183 accepted
1184 }
1185 pub fn traverse_frustum(&self, frustum: &BvhFrustum) -> Vec<u32> {
1192 self.frustum_query(frustum)
1193 }
1194}
1195impl DynamicBvh {
1196 pub fn node_depth(&self, idx: NodeIdx) -> Option<usize> {
1200 if self.free_list.contains(&idx) {
1201 return None;
1202 }
1203 let mut depth = 0usize;
1204 let mut cur = self.nodes[idx].parent;
1205 while let Some(p) = cur {
1206 depth += 1;
1207 cur = self.nodes[p].parent;
1208 }
1209 Some(depth)
1210 }
1211 pub fn find_leaf(&self, target: u32) -> Option<NodeIdx> {
1213 let root = self.root?;
1214 self.find_leaf_recursive(root, target)
1215 }
1216 fn find_leaf_recursive(&self, idx: NodeIdx, target: u32) -> Option<NodeIdx> {
1217 let node = &self.nodes[idx];
1218 if node.is_leaf() {
1219 return if node.data == Some(target) {
1220 Some(idx)
1221 } else {
1222 None
1223 };
1224 }
1225 if let Some(l) = node.left
1226 && let Some(found) = self.find_leaf_recursive(l, target)
1227 {
1228 return Some(found);
1229 }
1230 if let Some(r) = node.right
1231 && let Some(found) = self.find_leaf_recursive(r, target)
1232 {
1233 return Some(found);
1234 }
1235 None
1236 }
1237 pub fn leaf_data_preorder(&self) -> Vec<u32> {
1239 let Some(root) = self.root else {
1240 return Vec::new();
1241 };
1242 let mut result = Vec::new();
1243 self.leaf_data_preorder_recursive(root, &mut result);
1244 result
1245 }
1246 fn leaf_data_preorder_recursive(&self, idx: NodeIdx, out: &mut Vec<u32>) {
1247 let node = &self.nodes[idx];
1248 if node.is_leaf() {
1249 if let Some(d) = node.data {
1250 out.push(d);
1251 }
1252 return;
1253 }
1254 if let Some(l) = node.left {
1255 self.leaf_data_preorder_recursive(l, out);
1256 }
1257 if let Some(r) = node.right {
1258 self.leaf_data_preorder_recursive(r, out);
1259 }
1260 }
1261 pub fn root_aabb(&self) -> Option<BvhAabb> {
1263 self.root.map(|r| self.nodes[r].aabb)
1264 }
1265 pub fn max_depth(&self) -> usize {
1267 let Some(root) = self.root else {
1268 return 0;
1269 };
1270 let mut max_d = 0usize;
1271 let mut stack: Vec<(NodeIdx, usize)> = vec![(root, 0)];
1272 while let Some((idx, d)) = stack.pop() {
1273 if d > max_d {
1274 max_d = d;
1275 }
1276 let node = &self.nodes[idx];
1277 if !node.is_leaf() {
1278 if let Some(l) = node.left {
1279 stack.push((l, d + 1));
1280 }
1281 if let Some(r) = node.right {
1282 stack.push((r, d + 1));
1283 }
1284 }
1285 }
1286 max_d
1287 }
1288 pub fn n_internal(&self) -> usize {
1290 self.nodes
1291 .iter()
1292 .enumerate()
1293 .filter(|(idx, n)| !n.is_leaf() && !self.free_list.contains(idx))
1294 .count()
1295 }
1296}
1297impl DynamicBvh {
1298 pub fn query_sphere(&self, center: Vec3, radius: f64) -> Vec<u32> {
1304 let radius_sq = radius * radius;
1305 let mut results = Vec::new();
1306 if let Some(root) = self.root {
1307 self.query_sphere_recursive(root, center, radius_sq, &mut results);
1308 }
1309 results
1310 }
1311 fn query_sphere_recursive(
1312 &self,
1313 idx: NodeIdx,
1314 center: Vec3,
1315 radius_sq: f64,
1316 out: &mut Vec<u32>,
1317 ) {
1318 let node = &self.nodes[idx];
1319 if node.aabb.point_dist_sq(center) > radius_sq {
1320 return;
1321 }
1322 if node.is_leaf() {
1323 if let Some(d) = node.data {
1324 out.push(d);
1325 }
1326 } else {
1327 if let Some(l) = node.left {
1328 self.query_sphere_recursive(l, center, radius_sq, out);
1329 }
1330 if let Some(r) = node.right {
1331 self.query_sphere_recursive(r, center, radius_sq, out);
1332 }
1333 }
1334 }
1335 pub fn any_in_sphere(&self, center: Vec3, radius: f64) -> bool {
1337 !self.query_sphere(center, radius).is_empty()
1338 }
1339}
1340impl DynamicBvh {
1341 pub fn query_capsule(&self, capsule: BvhCapsule) -> Vec<u32> {
1347 let mut results = Vec::new();
1348 if let Some(root) = self.root {
1349 self.query_capsule_recursive(root, &capsule, &mut results);
1350 }
1351 results
1352 }
1353 fn query_capsule_recursive(&self, idx: NodeIdx, capsule: &BvhCapsule, out: &mut Vec<u32>) {
1354 let node = &self.nodes[idx];
1355 if self.aabb_segment_dist_sq(&node.aabb, capsule.start, capsule.end)
1356 > capsule.radius * capsule.radius
1357 {
1358 return;
1359 }
1360 if node.is_leaf() {
1361 if let Some(d) = node.data {
1362 out.push(d);
1363 }
1364 } else {
1365 if let Some(l) = node.left {
1366 self.query_capsule_recursive(l, capsule, out);
1367 }
1368 if let Some(r) = node.right {
1369 self.query_capsule_recursive(r, capsule, out);
1370 }
1371 }
1372 }
1373 fn aabb_segment_dist_sq(&self, aabb: &BvhAabb, p: Vec3, q: Vec3) -> f64 {
1375 let d = q - p;
1376 let len_sq = d.x * d.x + d.y * d.y + d.z * d.z;
1377 if len_sq < 1e-300 {
1378 return aabb.point_dist_sq(p);
1379 }
1380 let c = aabb.center();
1381 let t = ((c.x - p.x) * d.x + (c.y - p.y) * d.y + (c.z - p.z) * d.z) / len_sq;
1382 let t = t.clamp(0.0, 1.0);
1383 let closest = Vec3::new(p.x + t * d.x, p.y + t * d.y, p.z + t * d.z);
1384 aabb.point_dist_sq(closest)
1385 }
1386}
1387impl DynamicBvh {
1388 pub fn rebuild(&mut self) {
1394 let leaves: Vec<(BvhAabb, u32)> = self
1395 .nodes
1396 .iter()
1397 .enumerate()
1398 .filter(|(idx, n)| n.is_leaf() && !self.free_list.contains(idx))
1399 .filter_map(|(_, n)| n.data.map(|d| (n.aabb, d)))
1400 .collect();
1401 self.clear();
1402 for (aabb, data) in leaves {
1403 let tight = BvhAabb {
1404 min: Vec3::new(
1405 aabb.min.x + FAT_MARGIN,
1406 aabb.min.y + FAT_MARGIN,
1407 aabb.min.z + FAT_MARGIN,
1408 ),
1409 max: Vec3::new(
1410 aabb.max.x - FAT_MARGIN,
1411 aabb.max.y - FAT_MARGIN,
1412 aabb.max.z - FAT_MARGIN,
1413 ),
1414 };
1415 self.insert(tight, data);
1416 }
1417 }
1418 pub fn leaf_snapshot(&self) -> Vec<(u32, BvhAabb)> {
1421 self.nodes
1422 .iter()
1423 .enumerate()
1424 .filter(|(idx, n)| n.is_leaf() && !self.free_list.contains(idx))
1425 .filter_map(|(_, n)| n.data.map(|d| (d, n.aabb)))
1426 .collect()
1427 }
1428 pub fn leaf_aabb(&self, idx: NodeIdx) -> Option<BvhAabb> {
1431 if idx < self.nodes.len() && self.nodes[idx].is_leaf() && !self.free_list.contains(&idx) {
1432 Some(self.nodes[idx].aabb)
1433 } else {
1434 None
1435 }
1436 }
1437 pub fn remove_where(&mut self, pred: impl Fn(u32) -> bool) -> usize {
1441 let to_remove: Vec<NodeIdx> = self
1442 .nodes
1443 .iter()
1444 .enumerate()
1445 .filter(|(idx, n)| {
1446 n.is_leaf() && !self.free_list.contains(idx) && n.data.map(&pred).unwrap_or(false)
1447 })
1448 .map(|(i, _)| i)
1449 .collect();
1450 let count = to_remove.len();
1451 for idx in to_remove {
1452 self.remove(idx);
1453 }
1454 count
1455 }
1456 pub fn relabel_leaf(&mut self, leaf: NodeIdx, new_data: u32) -> bool {
1460 if leaf < self.nodes.len() && self.nodes[leaf].is_leaf() && !self.free_list.contains(&leaf)
1461 {
1462 self.nodes[leaf].data = Some(new_data);
1463 true
1464 } else {
1465 false
1466 }
1467 }
1468}
1469impl DynamicBvh {
1470 pub fn subtree_leaf_count(&self, idx: NodeIdx) -> usize {
1472 let node = &self.nodes[idx];
1473 if node.is_leaf() {
1474 return 1;
1475 }
1476 let left_count = node.left.map_or(0, |l| self.subtree_leaf_count(l));
1477 let right_count = node.right.map_or(0, |r| self.subtree_leaf_count(r));
1478 left_count + right_count
1479 }
1480 pub fn validate_heights(&self) -> bool {
1483 let Some(root) = self.root else {
1484 return true;
1485 };
1486 self.validate_heights_recursive(root)
1487 }
1488 fn validate_heights_recursive(&self, idx: NodeIdx) -> bool {
1489 let node = &self.nodes[idx];
1490 if node.is_leaf() {
1491 return node.height == 0;
1492 }
1493 let left = match node.left {
1494 Some(l) => l,
1495 None => return false,
1496 };
1497 let right = match node.right {
1498 Some(r) => r,
1499 None => return false,
1500 };
1501 let expected = 1 + self.nodes[left].height.max(self.nodes[right].height);
1502 if node.height != expected {
1503 return false;
1504 }
1505 self.validate_heights_recursive(left) && self.validate_heights_recursive(right)
1506 }
1507}
1508#[derive(Debug, Clone)]
1510pub struct BvhQuality {
1511 pub sah_cost: f64,
1513 pub max_depth: usize,
1515 pub min_leaf_depth: usize,
1517 pub avg_leaf_depth: f64,
1519 pub leaf_count: usize,
1521 pub internal_count: usize,
1523}