1use std::collections::HashMap;
2use std::sync::atomic::{AtomicU64, Ordering};
3
4use fast_mesh_store::{Mesh, TriangleId};
5
6use crate::arena::{Arena, NONE, Node};
7use crate::build::{Prims, TreeBuild, build_full, expected_nodes};
8use crate::error::{Result, try_reserve, try_reserve_exact, try_reserve_map, try_vec};
9use crate::{Aabb, BuildOptions, NodeId, NodeKind, UpdatePolicy};
10
11static NEXT_GENERATION: AtomicU64 = AtomicU64::new(0);
14
15pub(crate) fn next_generation() -> u64 {
17 NEXT_GENERATION.fetch_add(1, Ordering::Relaxed)
18}
19
20#[derive(Debug, Clone, Copy)]
22pub(crate) struct Treelet {
23 pub(crate) root: u32,
24 pub(crate) build_cost: f32,
26}
27
28pub struct Bvh {
31 pub(crate) arena: Arena,
32 pub(crate) root: u32,
33 pub(crate) treelets: Vec<Treelet>,
34 pub(crate) treelet_of_root: HashMap<u32, usize>,
36 pub(crate) counts: Vec<u32>,
39 pub(crate) garbage_nodes: usize,
41 pub(crate) garbage_refs: usize,
42 pub(crate) mesh_uid: u64,
43 pub(crate) epoch: u32,
45 pub(crate) revision: u64,
46 pub(crate) generation: u64,
48 pub(crate) options: BuildOptions,
49 pub(crate) policy: UpdatePolicy,
50}
51
52impl std::fmt::Debug for Bvh {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("Bvh")
55 .field("nodes", &self.node_count())
56 .field("treelets", &self.treelets.len())
57 .field("depth", &self.depth())
58 .field("bytes", &self.byte_size())
59 .finish()
60 }
61}
62
63pub(crate) fn alive_prims(mesh: &Mesh) -> Result<Prims> {
65 let mut prims = Prims::with_capacity(mesh.triangle_count())?;
66 let (positions, triangles) = (mesh.positions(), mesh.triangles());
67 for (t, &tri) in triangles.iter().enumerate() {
68 if mesh.is_triangle_alive(t as TriangleId) {
69 prims.push(t as u32, Aabb::of_triangle(positions, tri));
70 }
71 }
72 Ok(prims)
73}
74
75const COUNTS_PER_UNIT: usize = 64;
78
79const COST_TRIANGLES_PER_UNIT: usize = 32;
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub(crate) enum Assign {
87 Start,
88 Counts(usize),
92 Roots,
95 Costs(usize),
97 Done,
98}
99
100enum BuildStage {
102 Prims(Prims, usize),
105 Tree(Box<TreeBuild>),
107 Treelets(Box<Bvh>, Assign),
109 Done(Box<Bvh>),
111 Gone,
113}
114
115pub struct BvhBuild {
125 stage: BuildStage,
126 options: BuildOptions,
127 arena: Option<Arena>,
128 mesh_uid: u64,
129 epoch: u32,
130 revision: u64,
131 triangle_slots: usize,
134}
135
136impl std::fmt::Debug for BvhBuild {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 f.debug_struct("BvhBuild")
139 .field("done", &self.is_done())
140 .finish()
141 }
142}
143
144impl BvhBuild {
145 pub fn new(mesh: &Mesh, options: &BuildOptions) -> Result<BvhBuild> {
156 options.validate()?;
157 let prims = Prims::with_capacity(mesh.triangle_count())?;
158 let mut tri_leaf = Vec::new();
159 try_reserve_exact(&mut tri_leaf, mesh.triangle_slots())?;
160 let arena = Arena {
161 nodes: Vec::new(),
162 parents: Vec::new(),
163 refs: Vec::new(),
164 tri_leaf,
165 };
166 Ok(BvhBuild {
167 stage: BuildStage::Prims(prims, 0),
168 options: *options,
169 arena: Some(arena),
170 mesh_uid: mesh.uid(),
171 epoch: mesh.epoch(),
172 revision: mesh.revision(),
173 triangle_slots: mesh.triangle_slots(),
174 })
175 }
176
177 pub fn is_for(&self, mesh: &Mesh) -> bool {
179 self.mesh_uid == mesh.uid()
180 && self.epoch == mesh.epoch()
181 && self.revision == mesh.revision()
182 && self.triangle_slots == mesh.triangle_slots()
183 }
184
185 pub fn is_done(&self) -> bool {
187 matches!(self.stage, BuildStage::Done(_))
188 }
189
190 pub fn step(&mut self, mesh: &Mesh, budget: usize) -> Result<bool> {
201 if !self.is_for(mesh) {
202 self.stage = BuildStage::Gone;
203 return Err(crate::BvhError::InvalidOptions(
204 "the BVH build started from another mesh state",
205 ));
206 }
207 let mut left = budget.max(1);
208 loop {
209 match std::mem::replace(&mut self.stage, BuildStage::Gone) {
210 BuildStage::Prims(mut prims, from) => {
211 let (positions, triangles) = (mesh.positions(), mesh.triangles());
212 let to = triangles.len().min(from.saturating_add(left));
213 for (t, &tri) in triangles.iter().enumerate().take(to).skip(from) {
214 if mesh.is_triangle_alive(t as TriangleId) {
215 prims.push(t as u32, Aabb::of_triangle(positions, tri));
216 }
217 }
218 if let Some(arena) = self.arena.as_mut()
222 && arena.tri_leaf.len() < to
223 {
224 arena.tri_leaf.resize(to, NONE);
225 }
226 left = left.saturating_sub(to - from);
227 if to < triangles.len() {
228 self.stage = BuildStage::Prims(prims, to);
229 return Ok(false);
230 }
231 let arena = self.arena.take().ok_or(crate::BvhError::OutOfMemory)?;
232 if prims.len() == 0 {
233 let mut bvh = self.finish_tree(mesh, arena, NONE)?;
234 bvh.assign_treelets()?;
235 self.stage = BuildStage::Done(Box::new(bvh));
236 return Ok(true);
237 }
238 self.stage =
239 BuildStage::Tree(Box::new(TreeBuild::new(arena, prims, &self.options)?));
240 if left == 0 {
241 return Ok(false);
242 }
243 }
244 BuildStage::Tree(mut tree) => {
245 if !tree.step(left)? {
246 self.stage = BuildStage::Tree(tree);
247 return Ok(false);
248 }
249 let root = tree.root;
250 let bvh = self.finish_tree(mesh, tree.arena, root)?;
251 self.stage = BuildStage::Treelets(Box::new(bvh), Assign::Start);
252 return Ok(false);
253 }
254 BuildStage::Treelets(mut bvh, mut at) => {
255 if bvh.assign_step(&mut at, left)? {
256 self.stage = BuildStage::Done(bvh);
257 return Ok(true);
258 }
259 self.stage = BuildStage::Treelets(bvh, at);
260 return Ok(false);
261 }
262 done @ BuildStage::Done(_) => {
263 self.stage = done;
264 return Ok(true);
265 }
266 BuildStage::Gone => return Err(crate::BvhError::OutOfMemory),
267 }
268 }
269 }
270
271 pub fn finish(self) -> Option<Bvh> {
273 match self.stage {
274 BuildStage::Done(bvh) => Some(*bvh),
275 _ => None,
276 }
277 }
278
279 fn finish_tree(&self, mesh: &Mesh, mut arena: Arena, root: u32) -> Result<Bvh> {
282 if root == NONE {
283 let expected = expected_nodes(0, self.options.max_leaf_size);
284 try_reserve_exact(&mut arena.nodes, expected)?;
285 try_reserve_exact(&mut arena.parents, expected)?;
286 }
287 Ok(Bvh {
288 arena,
289 root,
290 treelets: Vec::new(),
291 treelet_of_root: HashMap::new(),
292 counts: Vec::new(),
293 garbage_nodes: 0,
294 garbage_refs: 0,
295 mesh_uid: mesh.uid(),
296 epoch: mesh.epoch(),
297 revision: mesh.revision(),
298 generation: next_generation(),
299 options: self.options,
300 policy: UpdatePolicy::default(),
301 })
302 }
303}
304
305impl Bvh {
306 pub fn build(mesh: &Mesh, options: &BuildOptions) -> Result<Bvh> {
308 options.validate()?;
309 let mut bvh = Self::assemble(mesh, options, &UpdatePolicy::default())?;
310 bvh.revision = mesh.revision();
311 Ok(bvh)
312 }
313
314 pub fn set_policy(&mut self, policy: UpdatePolicy) -> Result<()> {
316 policy.validate()?;
317 self.policy = policy;
318 Ok(())
319 }
320
321 pub fn options(&self) -> &BuildOptions {
322 &self.options
323 }
324
325 pub fn policy(&self) -> &UpdatePolicy {
326 &self.policy
327 }
328
329 pub fn revision(&self) -> u64 {
332 self.revision
333 }
334
335 pub fn is_current(&self, mesh: &Mesh) -> bool {
338 self.mesh_uid == mesh.uid()
339 && self.epoch == mesh.epoch()
340 && self.revision == mesh.revision()
341 }
342
343 pub(crate) fn assemble(
346 mesh: &Mesh,
347 options: &BuildOptions,
348 policy: &UpdatePolicy,
349 ) -> Result<Bvh> {
350 let mut prims = alive_prims(mesh)?;
351 let n = prims.len();
352 let mut arena = Arena {
353 nodes: Vec::new(),
354 parents: Vec::new(),
355 refs: Vec::new(),
356 tri_leaf: try_vec(mesh.triangle_slots(), NONE)?,
357 };
358 let root = if n == 0 {
359 let expected = expected_nodes(n, options.max_leaf_size);
360 try_reserve_exact(&mut arena.nodes, expected)?;
361 try_reserve_exact(&mut arena.parents, expected)?;
362 NONE
363 } else {
364 build_full(&mut arena, &mut prims, options)?
365 };
366 let mut bvh = Bvh {
367 arena,
368 root,
369 treelets: Vec::new(),
370 treelet_of_root: HashMap::new(),
371 counts: Vec::new(),
372 garbage_nodes: 0,
373 garbage_refs: 0,
374 mesh_uid: mesh.uid(),
375 epoch: mesh.epoch(),
376 revision: mesh.revision(),
377 generation: next_generation(),
378 options: *options,
379 policy: *policy,
380 };
381 bvh.assign_treelets()?;
382 Ok(bvh)
383 }
384
385 fn assign_treelets(&mut self) -> Result<()> {
389 let mut at = Assign::Start;
390 while !self.assign_step(&mut at, usize::MAX)? {}
391 Ok(())
392 }
393
394 pub(crate) fn assign_step(&mut self, at: &mut Assign, budget: usize) -> Result<bool> {
399 let mut left = budget.max(1);
400 loop {
401 match *at {
402 Assign::Start => {
403 self.treelets.clear();
404 self.treelet_of_root.clear();
405 self.counts = Vec::new();
406 if self.root == NONE {
407 *at = Assign::Done;
408 return Ok(true);
409 }
410 self.counts = try_vec(self.arena.nodes.len(), 0u32)?;
411 *at = Assign::Counts(self.arena.nodes.len());
412 }
413 Assign::Counts(end) => {
414 let start = end.saturating_sub(left.saturating_mul(COUNTS_PER_UNIT));
415 left = left.saturating_sub((end - start).div_ceil(COUNTS_PER_UNIT));
416 let (nodes, counts) = (&self.arena.nodes, &mut self.counts);
417 for i in (start..end).rev() {
418 let n = &nodes[i];
419 counts[i] = if n.is_leaf() {
420 n.leaf_range().len() as u32
421 } else {
422 counts[n.a as usize] + counts[n.b as usize]
423 };
424 }
425 *at = if start == 0 {
426 Assign::Roots
427 } else {
428 Assign::Counts(start)
429 };
430 }
431 Assign::Roots => {
432 let limit = self.options.treelet_size;
434 let counts = &self.counts;
435 let is_treelet_root =
436 |i: u32, n: &Node| n.is_leaf() || counts[i as usize] <= limit;
437 let mut found = 0;
438 self.arena.traverse(self.root, |i, n| {
439 let root = is_treelet_root(i, n);
440 found += usize::from(root);
441 !root
442 });
443 try_reserve_map(&mut self.treelet_of_root, found)?;
444 try_reserve(&mut self.treelets, found)?;
445 let (treelets, map) = (&mut self.treelets, &mut self.treelet_of_root);
446 self.arena.traverse(self.root, |i, n| {
447 let root = is_treelet_root(i, n);
448 if root {
449 map.insert(i, treelets.len());
450 treelets.push(Treelet {
451 root: i,
452 build_cost: 0.0,
453 });
454 }
455 !root
456 });
457 left = left.saturating_sub(found.div_ceil(COUNTS_PER_UNIT));
458 *at = Assign::Costs(0);
459 }
460 Assign::Costs(from) => {
461 let mut i = from;
462 while i < self.treelets.len() && left > 0 {
463 let t = &mut self.treelets[i];
464 t.build_cost = self.arena.sah_cost(t.root);
465 let tris = self.counts[t.root as usize] as usize;
466 left = left.saturating_sub(tris.div_ceil(COST_TRIANGLES_PER_UNIT).max(1));
467 i += 1;
468 }
469 *at = if i == self.treelets.len() {
470 Assign::Done
471 } else {
472 Assign::Costs(i)
473 };
474 }
475 Assign::Done => return Ok(true),
476 }
477 if left == 0 {
478 return Ok(*at == Assign::Done);
479 }
480 }
481 }
482
483 pub fn subtree_triangles(&self, id: NodeId) -> usize {
495 self.counts[id.index()] as usize
496 }
497
498 pub(crate) fn treelet_of(&self, mut node: u32) -> Option<usize> {
502 while node != NONE {
503 if let Some(&t) = self.treelet_of_root.get(&node) {
504 return Some(t);
505 }
506 node = self.arena.parents[node as usize];
507 }
508 None
509 }
510
511 pub fn query_aabb(&self, mesh: &Mesh, min: [f32; 3], max: [f32; 3], out: &mut Vec<TriangleId>) {
517 debug_assert!(self.is_current(mesh), "query_aabb on a stale BVH");
518 let query = Aabb::new(min, max);
519 let (positions, triangles) = (mesh.positions(), mesh.triangles());
520 self.traverse(|_, node| {
521 if !node.bounds.overlaps(&query) {
522 return false;
523 }
524 if let NodeKind::Leaf { triangles: leaf } = node.kind {
525 for &t in leaf {
526 if Aabb::of_triangle(positions, triangles[t as usize]).overlaps(&query) {
527 out.push(t);
528 }
529 }
530 }
531 true
532 });
533 }
534
535 pub fn node_count(&self) -> usize {
537 self.arena.nodes.len() - self.garbage_nodes
538 }
539
540 pub fn treelet_count(&self) -> usize {
541 self.treelets.len()
542 }
543
544 pub fn root_bounds(&self) -> Option<Aabb> {
546 (self.root != NONE).then(|| self.arena.nodes[self.root as usize].bounds)
547 }
548
549 pub fn depth(&self) -> usize {
551 let mut deepest = 0;
552 self.arena.traverse(self.root, |i, n| {
553 if n.is_leaf() {
554 let mut d = 1;
555 let mut cur = i;
556 while self.arena.parents[cur as usize] != NONE {
557 cur = self.arena.parents[cur as usize];
558 d += 1;
559 }
560 deepest = deepest.max(d);
561 }
562 true
563 });
564 deepest
565 }
566
567 pub fn byte_size(&self) -> usize {
569 let a = &self.arena;
570 a.nodes.capacity() * size_of::<Node>()
571 + (a.parents.capacity() + a.refs.capacity() + a.tri_leaf.capacity()) * 4
572 + self.counts.capacity() * 4
573 + self.treelets.capacity() * size_of::<Treelet>()
574 + self.treelet_of_root.capacity() * 16
575 }
576
577 pub(crate) fn compact_storage(&mut self) -> Result<()> {
580 let live_nodes = self.node_count();
581 let live_refs = self.arena.refs.len() - self.garbage_refs;
582 let mut new_index = try_vec(self.arena.nodes.len(), NONE)?;
583 let mut order = Vec::new();
584 try_reserve(&mut order, live_nodes)?;
585 let mut nodes = Vec::new();
586 try_reserve(&mut nodes, live_nodes)?;
587 let mut parents = Vec::new();
588 try_reserve(&mut parents, live_nodes)?;
589 let mut counts = Vec::new();
590 try_reserve(&mut counts, live_nodes)?;
591 let mut refs = Vec::new();
592 try_reserve(&mut refs, live_refs)?;
593 let mut tri_leaf = try_vec(self.arena.tri_leaf.len(), NONE)?;
594 let mut roots = HashMap::new();
595 try_reserve_map(&mut roots, self.treelets.len())?;
596
597 self.arena.traverse(self.root, |i, _| {
599 new_index[i as usize] = order.len() as u32;
600 order.push(i);
601 true
602 });
603 for &old in &order {
604 let n = self.arena.nodes[old as usize];
605 let me = nodes.len() as u32;
606 let parent = self.arena.parents[old as usize];
607 parents.push(if parent == NONE {
608 NONE
609 } else {
610 new_index[parent as usize]
611 });
612 counts.push(self.counts[old as usize]);
613 if n.is_leaf() {
614 let first = refs.len() as u32;
615 for &t in &self.arena.refs[n.leaf_range()] {
616 refs.push(t);
617 tri_leaf[t as usize] = me;
618 }
619 nodes.push(Node::leaf(n.bounds, first, n.leaf_range().len() as u32));
620 } else {
621 nodes.push(Node {
622 bounds: n.bounds,
623 a: new_index[n.a as usize],
624 b: new_index[n.b as usize],
625 });
626 }
627 }
628 for (i, t) in self.treelets.iter_mut().enumerate() {
629 t.root = new_index[t.root as usize];
630 roots.insert(t.root, i);
631 }
632 if self.root != NONE {
633 self.root = new_index[self.root as usize];
634 }
635 self.arena = Arena {
636 nodes,
637 parents,
638 refs,
639 tri_leaf,
640 };
641 self.treelet_of_root = roots;
642 self.counts = counts;
643 self.garbage_nodes = 0;
644 self.garbage_refs = 0;
645 self.generation = next_generation();
646 Ok(())
647 }
648
649 pub(crate) fn rebuild_from(&mut self, mesh: &Mesh) -> Result<()> {
652 *self = Bvh::assemble(mesh, &self.options, &self.policy)?;
653 Ok(())
654 }
655
656 fn reachable(&self, mut node: u32) -> bool {
659 let a = &self.arena;
660 for _ in 0..a.nodes.len() {
661 if node == self.root {
662 return true;
663 }
664 let parent = a.parents[node as usize];
665 if parent == NONE {
666 return false;
667 }
668 let p = &a.nodes[parent as usize];
669 if p.is_leaf() || (p.a != node && p.b != node) {
670 return false;
671 }
672 node = parent;
673 }
674 false
675 }
676
677 pub fn validate(&self, mesh: &Mesh) -> std::result::Result<(), String> {
680 let a = &self.arena;
681 let (positions, triangles) = (mesh.positions(), mesh.triangles());
682 if self.root != NONE && a.parents[self.root as usize] != NONE {
683 return Err("root has a parent".into());
684 }
685 let mut error: Option<String> = None;
686 let (mut reachable, mut refs_seen) = (0usize, 0usize);
687 a.traverse(self.root, |i, n| {
688 if error.is_some() {
689 return false;
690 }
691 reachable += 1;
692 let count = if n.is_leaf() {
693 n.leaf_range().len() as u32
694 } else {
695 let c = |k: u32| self.counts.get(k as usize).copied().unwrap_or(u32::MAX);
696 c(n.a).saturating_add(c(n.b))
697 };
698 if self.counts.get(i as usize) != Some(&count) {
699 error = Some(format!("node {i} has the wrong subtree count"));
700 return false;
701 }
702 if n.is_leaf() {
703 for &t in &a.refs[n.leaf_range()] {
704 refs_seen += 1;
705 if !mesh.is_triangle_alive(t) {
706 error = Some(format!("leaf {i} references dead triangle {t}"));
707 } else if a.tri_leaf.get(t as usize) != Some(&i) {
708 error = Some(format!(
709 "tri_leaf of triangle {t} does not point at leaf {i}"
710 ));
711 } else if !n
712 .bounds
713 .contains(&Aabb::of_triangle(positions, triangles[t as usize]))
714 {
715 error = Some(format!("leaf {i} box does not contain triangle {t}"));
716 }
717 }
718 } else {
719 for c in [n.a, n.b] {
720 if a.parents[c as usize] != i {
721 error = Some(format!("child {c} of node {i} has the wrong parent"));
722 } else if !n.bounds.contains(&a.nodes[c as usize].bounds) {
723 error = Some(format!("node {i} box does not contain child {c}"));
724 }
725 }
726 }
727 true
728 });
729 if let Some(e) = error {
730 return Err(e);
731 }
732 if refs_seen != mesh.triangle_count() {
733 return Err(format!(
734 "tree holds {refs_seen} triangles, mesh has {}",
735 mesh.triangle_count()
736 ));
737 }
738 if reachable + self.garbage_nodes != a.nodes.len() {
739 return Err(format!(
740 "{reachable} reachable + {} garbage nodes != {} stored",
741 self.garbage_nodes,
742 a.nodes.len()
743 ));
744 }
745 if refs_seen + self.garbage_refs != a.refs.len() {
746 return Err("reachable + garbage refs != stored refs".into());
747 }
748 for (t, &leaf) in a.tri_leaf.iter().enumerate() {
750 if leaf == NONE {
751 continue;
752 }
753 let holds = a
754 .nodes
755 .get(leaf as usize)
756 .is_some_and(|n| n.is_leaf() && a.refs[n.leaf_range()].contains(&(t as u32)));
757 if !holds || !self.reachable(leaf) {
758 return Err(format!(
759 "tri_leaf of triangle {t} points at node {leaf}, which is not a reachable leaf holding it"
760 ));
761 }
762 }
763 for (i, t) in self.treelets.iter().enumerate() {
764 if self.treelet_of_root.get(&t.root) != Some(&i) {
765 return Err(format!("treelet {i} is not indexed by its root"));
766 }
767 }
768 if self.root != NONE {
769 let mut leaves_in_treelets = 0;
771 for t in &self.treelets {
772 a.traverse(t.root, |_, n| {
773 if n.is_leaf() {
774 leaves_in_treelets += 1;
775 }
776 true
777 });
778 }
779 let mut leaves = 0;
780 a.traverse(self.root, |_, n| {
781 if n.is_leaf() {
782 leaves += 1;
783 }
784 true
785 });
786 if leaves != leaves_in_treelets {
787 return Err(format!(
788 "{leaves} leaves but {leaves_in_treelets} inside treelets"
789 ));
790 }
791 }
792 Ok(())
793 }
794}
795
796#[cfg(test)]
797impl Bvh {
798 pub(crate) fn treelet_triangle_counts(&self) -> Vec<usize> {
800 self.treelets
801 .iter()
802 .map(|t| self.arena.subtree_size(t.root).1)
803 .collect()
804 }
805
806 pub(crate) fn fingerprint(&self) -> String {
810 let a = &self.arena;
811 let leaves: Vec<(usize, u32)> = a
812 .tri_leaf
813 .iter()
814 .enumerate()
815 .filter(|&(_, &l)| l != NONE)
816 .map(|(t, &l)| (t, l))
817 .collect();
818 let mut treelets: Vec<(u32, usize)> =
819 self.treelet_of_root.iter().map(|(&r, &i)| (r, i)).collect();
820 treelets.sort();
821 format!(
822 "{:?}|{:?}|{:?}|{:?}|{:?}|{}|{:?}|{}|{}|{}|{}",
823 a.nodes
824 .iter()
825 .map(|n| (n.bounds, n.a, n.b))
826 .collect::<Vec<_>>(),
827 a.parents,
828 self.counts,
829 a.refs,
830 leaves,
831 self.root,
832 treelets,
833 self.garbage_nodes,
834 self.garbage_refs,
835 self.epoch,
836 self.revision,
837 )
838 }
839}
840
841#[cfg(test)]
842mod tests {
843 use super::*;
844 use crate::{BvhError, Quality};
845
846 pub(crate) fn grid(n: u32) -> Mesh {
848 let mut positions = Vec::new();
849 for y in 0..=n {
850 for x in 0..=n {
851 positions.extend([x as f32, y as f32, 0.0]);
852 }
853 }
854 let mut indices = Vec::new();
855 for y in 0..n {
856 for x in 0..n {
857 let a = y * (n + 1) + x;
858 indices.extend([a, a + 1, a + n + 2, a, a + n + 2, a + n + 1]);
859 }
860 }
861 Mesh::from_buffers(&positions, &indices).unwrap()
862 }
863
864 fn brute(mesh: &Mesh, q: &Aabb) -> Vec<u32> {
865 (0..mesh.triangle_slots() as u32)
866 .filter(|&t| {
867 mesh.triangle(t)
868 .is_some_and(|tri| Aabb::of_triangle(mesh.positions(), tri).overlaps(q))
869 })
870 .collect()
871 }
872
873 #[test]
874 fn builds_and_queries_both_qualities() {
875 let mesh = grid(20);
876 for quality in [Quality::Sah, Quality::Fast] {
877 let opts = BuildOptions {
878 quality,
879 treelet_size: 64,
880 ..Default::default()
881 };
882 let bvh = Bvh::build(&mesh, &opts).unwrap();
883 bvh.validate(&mesh).unwrap();
884 assert!(bvh.treelet_count() > 1);
885 assert_eq!(
886 bvh.root_bounds(),
887 Some(Aabb::new([0.0; 3], [20.0, 20.0, 0.0]))
888 );
889 let q = Aabb::new([3.5, 3.5, -1.0], [7.0, 5.0, 1.0]);
890 let mut got = Vec::new();
891 bvh.query_aabb(&mesh, q.min, q.max, &mut got);
892 got.sort();
893 assert_eq!(got, brute(&mesh, &q), "{quality:?}");
894 }
895 }
896
897 #[test]
898 fn empty_mesh_and_single_triangle() {
899 let empty = Mesh::new();
900 let bvh = Bvh::build(&empty, &BuildOptions::default()).unwrap();
901 bvh.validate(&empty).unwrap();
902 assert_eq!(
903 (bvh.node_count(), bvh.depth(), bvh.root_bounds()),
904 (0, 0, None)
905 );
906 let mut out = Vec::new();
907 bvh.query_aabb(&empty, [-1.0; 3], [1.0; 3], &mut out);
908 assert!(out.is_empty());
909
910 let one = Mesh::from_buffers(&[0., 0., 0., 1., 0., 0., 0., 1., 0.], &[0, 1, 2]).unwrap();
911 let bvh = Bvh::build(&one, &BuildOptions::default()).unwrap();
912 bvh.validate(&one).unwrap();
913 assert_eq!(
914 (bvh.node_count(), bvh.depth(), bvh.treelet_count()),
915 (1, 1, 1)
916 );
917 }
918
919 #[test]
920 fn dead_slots_are_skipped() {
921 let mut mesh = grid(4);
922 let mut tx = mesh.begin_edit();
923 tx.remove_triangles(&[0, 5, 9]).unwrap();
924 tx.commit().unwrap();
925 let bvh = Bvh::build(&mesh, &BuildOptions::default()).unwrap();
926 bvh.validate(&mesh).unwrap();
927 let mut out = Vec::new();
928 bvh.query_aabb(&mesh, [-1.0; 3], [10.0; 3], &mut out);
929 assert_eq!(out.len(), mesh.triangle_count());
930 assert!(!out.contains(&5));
931 }
932
933 #[test]
934 fn degenerate_and_non_finite_triangles_do_not_break_the_build() {
935 let mesh = Mesh::from_buffers(
936 &[
937 0.,
938 0.,
939 0.,
940 1.,
941 0.,
942 0.,
943 f32::NAN,
944 1.,
945 0.,
946 1e30,
947 1e30,
948 1e30,
949 2.,
950 2.,
951 2.,
952 ],
953 &[0, 0, 0, 0, 1, 2, 1, 3, 4, 4, 4, 4, 0, 1, 4],
954 )
955 .unwrap();
956 for quality in [Quality::Sah, Quality::Fast] {
957 let opts = BuildOptions {
958 quality,
959 max_leaf_size: 1,
960 ..Default::default()
961 };
962 let bvh = Bvh::build(&mesh, &opts).unwrap();
963 bvh.validate(&mesh).unwrap();
964 let mut out = Vec::new();
965 let q = Aabb::new([0.5, -1.0, -1.0], [1.5, 1.0, 1.0]);
966 bvh.query_aabb(&mesh, q.min, q.max, &mut out);
967 out.sort();
968 assert_eq!(out, brute(&mesh, &q));
969 }
970 }
971
972 #[test]
973 fn invalid_options_are_rejected() {
974 let opts = BuildOptions {
975 max_leaf_size: 17,
976 ..Default::default()
977 };
978 assert!(matches!(
979 Bvh::build(&grid(1), &opts),
980 Err(BvhError::InvalidOptions(_))
981 ));
982 }
983
984 #[test]
985 fn separate_builds_get_different_generations() {
986 let mesh = grid(4);
987 let a = Bvh::build(&mesh, &BuildOptions::default()).unwrap();
988 let b = Bvh::build(&mesh, &BuildOptions::default()).unwrap();
989 assert_ne!(a.generation(), b.generation());
990 }
991
992 #[test]
993 fn compaction_preserves_the_tree() {
994 let mesh = grid(10);
995 let mut bvh = Bvh::build(
996 &mesh,
997 &BuildOptions {
998 treelet_size: 16,
999 ..Default::default()
1000 },
1001 )
1002 .unwrap();
1003 let before = bvh.node_count();
1004 bvh.compact_storage().unwrap();
1005 bvh.validate(&mesh).unwrap();
1006 assert_eq!(bvh.node_count(), before);
1007 }
1008
1009 fn wavy(n: u32) -> Mesh {
1012 let mut positions = Vec::new();
1013 for y in 0..=n {
1014 for x in 0..=n {
1015 let (fx, fy) = (x as f32, y as f32);
1016 positions.extend([fx, fy, (fx * 0.31).sin() * (fy * 0.17).cos() * 3.0]);
1017 }
1018 }
1019 let mut indices = Vec::new();
1020 for y in 0..n {
1021 for x in 0..n {
1022 let a = y * (n + 1) + x;
1023 indices.extend([a, a + 1, a + n + 2, a, a + n + 2, a + n + 1]);
1024 }
1025 }
1026 let mut mesh = Mesh::from_buffers(&positions, &indices).unwrap();
1027 let mut tx = mesh.begin_edit();
1028 let gone: Vec<u32> = [0, 7, 100, 101, 2 * n * n - 1]
1029 .into_iter()
1030 .filter(|&t| t < 2 * n * n)
1031 .collect();
1032 tx.remove_triangles(&gone).unwrap();
1033 tx.commit().unwrap();
1034 mesh
1035 }
1036
1037 type Fingerprint = (Vec<u64>, Vec<u32>, u32, Vec<(u32, u32)>);
1039
1040 fn fingerprint(b: &Bvh) -> Fingerprint {
1042 let a = &b.arena;
1043 let nodes = a
1044 .nodes
1045 .iter()
1046 .flat_map(|n| {
1047 n.bounds
1048 .min
1049 .iter()
1050 .chain(&n.bounds.max)
1051 .map(|c| u64::from(c.to_bits()))
1052 .chain([u64::from(n.a), u64::from(n.b)])
1053 .collect::<Vec<_>>()
1054 })
1055 .collect();
1056 let ids = a
1057 .parents
1058 .iter()
1059 .chain(&a.refs)
1060 .chain(&a.tri_leaf)
1061 .chain(&b.counts)
1062 .copied()
1063 .collect();
1064 let treelets = b
1065 .treelets
1066 .iter()
1067 .map(|t| (t.root, t.build_cost.to_bits()))
1068 .collect();
1069 (nodes, ids, b.root, treelets)
1070 }
1071
1072 fn stepped(mesh: &Mesh, opts: &BuildOptions, budget: usize) -> (Bvh, usize) {
1073 let mut build = BvhBuild::new(mesh, opts).unwrap();
1074 let mut steps = 1;
1075 while !build.step(mesh, budget).unwrap() {
1076 steps += 1;
1077 }
1078 assert!(build.is_done());
1079 (build.finish().unwrap(), steps)
1080 }
1081
1082 #[test]
1083 fn stepped_build_matches_one_call() {
1084 for quality in [Quality::Sah, Quality::Fast] {
1085 for (n, leaf) in [(3, 1), (40, 4), (90, 8)] {
1086 let mesh = wavy(n);
1087 let opts = BuildOptions {
1088 quality,
1089 max_leaf_size: leaf,
1090 treelet_size: 64,
1091 ..Default::default()
1092 };
1093 let want = fingerprint(&Bvh::build(&mesh, &opts).unwrap());
1094 for budget in [1, 50, 3000, usize::MAX] {
1095 let (got, steps) = stepped(&mesh, &opts, budget);
1096 got.validate(&mesh).unwrap();
1097 assert!(
1098 fingerprint(&got) == want,
1099 "{quality:?} n={n} budget {budget}"
1100 );
1101 if budget == 1 && n == 90 {
1102 assert!(steps > 1000, "{steps}");
1103 }
1104 }
1105 }
1106 }
1107 for mesh in [Mesh::new(), grid(1)] {
1109 let opts = BuildOptions::default();
1110 let want = fingerprint(&Bvh::build(&mesh, &opts).unwrap());
1111 assert!(fingerprint(&stepped(&mesh, &opts, 1).0) == want);
1112 }
1113 }
1114
1115 #[test]
1116 fn new_only_reserves_the_leaf_table() {
1117 let mesh = grid(10);
1122 let opts = BuildOptions::default();
1123 let mut build = BvhBuild::new(&mesh, &opts).unwrap();
1124 assert!(build.arena.as_ref().unwrap().tri_leaf.is_empty());
1125 let mut last = 0;
1126 while !build.step(&mesh, 7).unwrap() {
1127 if matches!(build.stage, BuildStage::Prims(..)) {
1129 let len = build.arena.as_ref().unwrap().tri_leaf.len();
1130 assert!(len >= last);
1131 last = len;
1132 }
1133 }
1134 let got = build.finish().unwrap();
1135 got.validate(&mesh).unwrap();
1136 assert!(fingerprint(&got) == fingerprint(&Bvh::build(&mesh, &opts).unwrap()));
1137 }
1138
1139 #[test]
1140 fn stepped_build_assigns_treelets_in_steps() {
1141 let mesh = wavy(90);
1145 let opts = BuildOptions {
1146 max_leaf_size: 1,
1147 treelet_size: 64,
1148 ..Default::default()
1149 };
1150 let want = Bvh::build(&mesh, &opts).unwrap();
1151 let mut build = BvhBuild::new(&mesh, &opts).unwrap();
1152 while !matches!(build.stage, BuildStage::Treelets(..)) {
1153 assert!(!build.step(&mesh, 1).unwrap());
1154 }
1155 let mut steps = 1;
1156 while !build.step(&mesh, 1).unwrap() {
1157 steps += 1;
1158 }
1159 assert!(steps > want.node_count() / 64, "{steps}");
1160 let got = build.finish().unwrap();
1161 got.validate(&mesh).unwrap();
1162 assert!(fingerprint(&got) == fingerprint(&want));
1163 }
1164
1165 #[test]
1166 fn stepped_build_bit_identical_in_parallel() {
1167 use fast_mesh_store::par;
1168 let mesh = wavy(260);
1170 for quality in [Quality::Sah, Quality::Fast] {
1171 let opts = BuildOptions {
1172 quality,
1173 ..Default::default()
1174 };
1175 par::set_force_sequential(true);
1176 let want = fingerprint(&Bvh::build(&mesh, &opts).unwrap());
1177 par::set_force_sequential(false);
1178 assert!(fingerprint(&Bvh::build(&mesh, &opts).unwrap()) == want);
1179 for budget in [5_000, 20_000, 300_000, usize::MAX] {
1180 for k in [1, 8] {
1181 let got =
1182 par::with_threads(k, || fingerprint(&stepped(&mesh, &opts, budget).0));
1183 assert!(got == want, "{quality:?} budget {budget}, {k} threads");
1184 }
1185 }
1186 }
1187 }
1188
1189 #[test]
1190 fn stepped_build_refuses_another_state() {
1191 let mut mesh = wavy(10);
1192 let mut build = BvhBuild::new(&mesh, &BuildOptions::default()).unwrap();
1193 assert!(!build.step(&mesh, 5).unwrap());
1194 let mut tx = mesh.begin_edit();
1195 tx.set_positions(&[0], &[[0.0, 0.0, 9.0]]).unwrap();
1196 tx.commit().unwrap();
1197 assert!(!build.is_for(&mesh));
1198 assert!(matches!(
1199 build.step(&mesh, 5),
1200 Err(BvhError::InvalidOptions(_))
1201 ));
1202 assert!(build.finish().is_none());
1203 let bad = BuildOptions {
1204 max_leaf_size: 0,
1205 ..Default::default()
1206 };
1207 assert!(matches!(
1208 BvhBuild::new(&mesh, &bad),
1209 Err(BvhError::InvalidOptions(_))
1210 ));
1211 }
1212
1213 #[test]
1214 fn stepped_build_refuses_a_grown_mesh_after_undo() {
1215 let mut mesh = wavy(10);
1216 let mut build = BvhBuild::new(&mesh, &BuildOptions::default()).unwrap();
1217 assert!(!build.step(&mesh, 5).unwrap());
1218 let revision = mesh.revision();
1219 let mut tx = mesh.begin_edit();
1220 let vs = tx
1221 .add_vertices(&[[20.0, 20.0, 0.0], [21.0, 20.0, 0.0], [20.0, 21.0, 0.0]])
1222 .unwrap();
1223 tx.add_triangles(&[[vs[0], vs[1], vs[2]]]).unwrap();
1224 let patch = tx.commit().unwrap();
1225 patch.revert(&mut mesh).unwrap();
1226 assert_eq!(mesh.revision(), revision);
1227 assert!(!build.is_for(&mesh));
1228 assert!(matches!(
1229 build.step(&mesh, 5),
1230 Err(BvhError::InvalidOptions(_))
1231 ));
1232 }
1233}