mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 22:41:30 +11:00
Fix stack overflow during BVH construction
The epsilon for float equality was too small which prevented the function from terminating. Additionally, it has been rewritten in terms of a loop because tail-call optimization was not happening.
This commit is contained in:
parent
b604dafe73
commit
044a735729
158
src/bvh.rs
158
src/bvh.rs
|
@ -6,7 +6,7 @@
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use approx::relative_eq;
|
use approx::abs_diff_eq;
|
||||||
use rayon::iter::{
|
use rayon::iter::{
|
||||||
IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator,
|
IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator,
|
||||||
};
|
};
|
||||||
|
@ -180,7 +180,7 @@ impl<'a, T> Internal<'a, T> {
|
||||||
|
|
||||||
fn build_rec<T: Send>(
|
fn build_rec<T: Send>(
|
||||||
idx: NodeIdx,
|
idx: NodeIdx,
|
||||||
bounds: Aabb<f64>,
|
mut bounds: Aabb<f64>,
|
||||||
internal_nodes: &mut [InternalNode],
|
internal_nodes: &mut [InternalNode],
|
||||||
leaf_nodes: &mut [LeafNode<T>],
|
leaf_nodes: &mut [LeafNode<T>],
|
||||||
total_leaf_count: NodeIdx,
|
total_leaf_count: NodeIdx,
|
||||||
|
@ -192,91 +192,91 @@ fn build_rec<T: Send>(
|
||||||
return (total_leaf_count - 1 + idx, leaf_nodes[0].bb);
|
return (total_leaf_count - 1 + idx, leaf_nodes[0].bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(bounds.is_valid());
|
loop {
|
||||||
let dims = bounds.max - bounds.min;
|
debug_assert!(bounds.is_valid());
|
||||||
|
let dims = bounds.max - bounds.min;
|
||||||
|
|
||||||
let (mut split, bounds_left, bounds_right) = if dims.x >= dims.y && dims.x >= dims.z {
|
let (mut split, bounds_left, bounds_right) = if dims.x >= dims.y && dims.x >= dims.z {
|
||||||
let mid = middle(bounds.min.x, bounds.max.x);
|
let mid = middle(bounds.min.x, bounds.max.x);
|
||||||
let [bounds_left, bounds_right] = bounds.split_at_x(mid);
|
let [bounds_left, bounds_right] = bounds.split_at_x(mid);
|
||||||
|
|
||||||
let p = partition(leaf_nodes, |l| middle(l.bb.min.x, l.bb.max.x) <= mid);
|
let p = partition(leaf_nodes, |l| middle(l.bb.min.x, l.bb.max.x) <= mid);
|
||||||
|
|
||||||
(p, bounds_left, bounds_right)
|
(p, bounds_left, bounds_right)
|
||||||
} else if dims.y >= dims.x && dims.y >= dims.z {
|
} else if dims.y >= dims.x && dims.y >= dims.z {
|
||||||
let mid = middle(bounds.min.y, bounds.max.y);
|
let mid = middle(bounds.min.y, bounds.max.y);
|
||||||
let [bounds_left, bounds_right] = bounds.split_at_y(mid);
|
let [bounds_left, bounds_right] = bounds.split_at_y(mid);
|
||||||
|
|
||||||
let p = partition(leaf_nodes, |l| middle(l.bb.min.y, l.bb.max.y) <= mid);
|
let p = partition(leaf_nodes, |l| middle(l.bb.min.y, l.bb.max.y) <= mid);
|
||||||
|
|
||||||
(p, bounds_left, bounds_right)
|
(p, bounds_left, bounds_right)
|
||||||
} else {
|
|
||||||
let mid = middle(bounds.min.z, bounds.max.z);
|
|
||||||
let [bounds_left, bounds_right] = bounds.split_at_z(mid);
|
|
||||||
|
|
||||||
let p = partition(leaf_nodes, |l| middle(l.bb.min.z, l.bb.max.z) <= mid);
|
|
||||||
|
|
||||||
(p, bounds_left, bounds_right)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check if one of the halves is empty. (We can't have empty nodes)
|
|
||||||
// Also take care to handle the edge case of overlapping points.
|
|
||||||
if split == 0 {
|
|
||||||
if relative_eq!(bounds_right.min, bounds_right.max) {
|
|
||||||
split += 1;
|
|
||||||
} else {
|
} else {
|
||||||
return build_rec(
|
let mid = middle(bounds.min.z, bounds.max.z);
|
||||||
idx,
|
let [bounds_left, bounds_right] = bounds.split_at_z(mid);
|
||||||
bounds_right,
|
|
||||||
internal_nodes,
|
let p = partition(leaf_nodes, |l| middle(l.bb.min.z, l.bb.max.z) <= mid);
|
||||||
leaf_nodes,
|
|
||||||
total_leaf_count,
|
(p, bounds_left, bounds_right)
|
||||||
);
|
};
|
||||||
}
|
|
||||||
} else if split == leaf_nodes.len() {
|
// Check if one of the halves is empty. (We can't have empty nodes)
|
||||||
if relative_eq!(bounds_left.min, bounds_left.max) {
|
// Also take care to handle the edge case of overlapping points.
|
||||||
split -= 1;
|
if split == 0 {
|
||||||
} else {
|
if abs_diff_eq!(
|
||||||
return build_rec(
|
bounds_right.min,
|
||||||
idx,
|
bounds_right.max,
|
||||||
bounds_left,
|
epsilon = f64::EPSILON * 100.0
|
||||||
internal_nodes,
|
) {
|
||||||
leaf_nodes,
|
split += 1;
|
||||||
total_leaf_count,
|
} else {
|
||||||
);
|
bounds = bounds_right;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if split == leaf_nodes.len() {
|
||||||
|
if abs_diff_eq!(
|
||||||
|
bounds_left.min,
|
||||||
|
bounds_left.max,
|
||||||
|
epsilon = f64::EPSILON * 100.0
|
||||||
|
) {
|
||||||
|
split -= 1;
|
||||||
|
} else {
|
||||||
|
bounds = bounds_left;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (leaves_left, leaves_right) = leaf_nodes.split_at_mut(split);
|
||||||
|
|
||||||
|
let (internal_left, internal_right) = internal_nodes.split_at_mut(split);
|
||||||
|
let (internal, internal_left) = internal_left.split_last_mut().unwrap();
|
||||||
|
|
||||||
|
let ((left, bounds_left), (right, bounds_right)) = rayon::join(
|
||||||
|
|| {
|
||||||
|
build_rec(
|
||||||
|
idx,
|
||||||
|
bounds_left,
|
||||||
|
internal_left,
|
||||||
|
leaves_left,
|
||||||
|
total_leaf_count,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|| {
|
||||||
|
build_rec(
|
||||||
|
idx + split as NodeIdx,
|
||||||
|
bounds_right,
|
||||||
|
internal_right,
|
||||||
|
leaves_right,
|
||||||
|
total_leaf_count,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
internal.bb = bounds_left.union(bounds_right);
|
||||||
|
internal.left = left;
|
||||||
|
internal.right = right;
|
||||||
|
|
||||||
|
break (idx + split as NodeIdx - 1, internal.bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (leaves_left, leaves_right) = leaf_nodes.split_at_mut(split);
|
|
||||||
|
|
||||||
let (internal_left, internal_right) = internal_nodes.split_at_mut(split);
|
|
||||||
let (internal, internal_left) = internal_left.split_last_mut().unwrap();
|
|
||||||
|
|
||||||
let ((left, bounds_left), (right, bounds_right)) = rayon::join(
|
|
||||||
|| {
|
|
||||||
build_rec(
|
|
||||||
idx,
|
|
||||||
bounds_left,
|
|
||||||
internal_left,
|
|
||||||
leaves_left,
|
|
||||||
total_leaf_count,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|| {
|
|
||||||
build_rec(
|
|
||||||
idx + split as NodeIdx,
|
|
||||||
bounds_right,
|
|
||||||
internal_right,
|
|
||||||
leaves_right,
|
|
||||||
total_leaf_count,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
internal.bb = bounds_left.union(bounds_right);
|
|
||||||
internal.left = left;
|
|
||||||
internal.right = right;
|
|
||||||
|
|
||||||
(idx + split as NodeIdx - 1, internal.bb)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partition<T>(s: &mut [T], mut pred: impl FnMut(&T) -> bool) -> usize {
|
fn partition<T>(s: &mut [T], mut pred: impl FnMut(&T) -> bool) -> usize {
|
||||||
|
|
Loading…
Reference in a new issue