use super::bvh_tree::{BvhBuildStrategy, BvhNodeIndex, BvhNodeWide};
use super::{Bvh, BvhNode, BvhWorkspace};
use crate::bounding_volume::{Aabb, BoundingVolume};
use crate::math::Real;
impl Bvh {
pub fn rebuild(&mut self, workspace: &mut BvhWorkspace, strategy: BvhBuildStrategy) {
if self.nodes.len() < 2 {
return;
}
workspace.rebuild_leaves.clear();
for node in self.nodes.iter() {
if node.left.is_leaf() {
workspace.rebuild_leaves.push(node.left);
}
if node.right.is_leaf() {
workspace.rebuild_leaves.push(node.right);
}
}
self.nodes.clear();
self.parents.clear();
self.nodes.push(BvhNodeWide::zeros());
self.parents.push(BvhNodeIndex::default());
match strategy {
BvhBuildStrategy::Binned => self.rebuild_range_binned(0, &mut workspace.rebuild_leaves),
BvhBuildStrategy::Ploc => self.rebuild_range_ploc(0, &mut workspace.rebuild_leaves),
}
}
pub(crate) fn rebuild_range_binned(&mut self, target_node_id: u32, leaves: &mut [BvhNode]) {
const NUM_BINS: usize = 8;
const BIN_EPSILON: Real = 1.0e-5;
let mut bins = [BvhBin::default(); NUM_BINS];
assert!(leaves.len() > 1);
let centroid_aabb = Aabb::from_points(leaves.iter().map(|node| node.center()));
let bins_axis = centroid_aabb.extents().max_position();
let bins_range = [centroid_aabb.mins[bins_axis], centroid_aabb.maxs[bins_axis]];
let k1 = NUM_BINS as Real * (1.0 - BIN_EPSILON) / (bins_range[1] - bins_range[0]);
let k0 = bins_range[0];
for leaf in &*leaves {
let bin_id = (k1 * (leaf.center()[bins_axis] - k0)) as usize;
let bin = &mut bins[bin_id];
bin.aabb.merge(&leaf.aabb());
bin.leaf_count += 1;
}
let mut right_merges = bins;
let mut right_acc = bins[NUM_BINS - 1];
for i in 1..NUM_BINS - 1 {
right_acc.aabb.merge(&right_merges[NUM_BINS - 1 - i].aabb);
right_acc.leaf_count += &right_merges[NUM_BINS - 1 - i].leaf_count;
right_merges[NUM_BINS - 1 - i] = right_acc;
}
let mut best_cost = Real::MAX;
let mut best_plane = 0;
let mut left_merge = bins[0];
let mut best_leaf_count = bins[0].leaf_count;
for i in 0..NUM_BINS - 1 {
let right = &right_merges[i + 1];
let cost = left_merge.aabb.volume() * left_merge.leaf_count as Real
+ right.aabb.volume() * right.leaf_count as Real;
if cost < best_cost {
best_cost = cost;
best_plane = i;
best_leaf_count = left_merge.leaf_count;
}
left_merge.aabb.merge(&bins[i + 1].aabb);
left_merge.leaf_count += bins[i + 1].leaf_count;
}
let mut mid = best_leaf_count as usize;
if mid == 0 || mid == leaves.len() {
mid = leaves.len() / 2;
} else {
let bin = |leaves: &mut [BvhNode], id: usize| {
let node = &leaves[id];
(k1 * (node.center()[bins_axis] - k0)) as usize
};
let mut left_id = 0;
let mut right_id = mid;
'outer: while left_id != mid && right_id != leaves.len() {
while bin(leaves, left_id) <= best_plane {
left_id += 1;
if left_id == mid {
break 'outer;
}
}
while bin(leaves, right_id) > best_plane {
right_id += 1;
if right_id == leaves.len() {
break 'outer;
}
}
leaves.swap(left_id, right_id);
left_id += 1;
right_id += 1;
}
}
let (left_leaves, right_leaves) = leaves.split_at_mut(mid);
assert!(!left_leaves.is_empty() && !right_leaves.is_empty());
if left_leaves.len() == 1 {
let target = &mut self.nodes[target_node_id as usize];
target.left = left_leaves[0];
if target.left.is_leaf() {
self.leaf_node_indices[target.left.children as usize] =
BvhNodeIndex::left(target_node_id);
} else {
self.parents[target.left.children as usize] = BvhNodeIndex::left(target_node_id);
}
} else {
let left_id = self.nodes.len() as u32;
self.nodes.push(BvhNodeWide::zeros());
self.parents.push(BvhNodeIndex::left(target_node_id));
self.rebuild_range_binned(left_id, left_leaves);
self.nodes[target_node_id as usize].left = self.nodes[left_id as usize].merged(left_id);
}
if right_leaves.len() == 1 {
let target = &mut self.nodes[target_node_id as usize];
target.right = right_leaves[0];
if target.right.is_leaf() {
self.leaf_node_indices[target.right.children as usize] =
BvhNodeIndex::right(target_node_id);
} else {
self.parents[target.right.children as usize] = BvhNodeIndex::right(target_node_id);
}
} else {
let right_id = self.nodes.len() as u32;
self.nodes.push(BvhNodeWide::zeros());
self.parents.push(BvhNodeIndex::right(target_node_id));
self.rebuild_range_binned(right_id, right_leaves);
self.nodes[target_node_id as usize].right =
self.nodes[right_id as usize].merged(right_id);
}
}
}
#[derive(Copy, Clone, Debug)]
struct BvhBin {
aabb: Aabb,
leaf_count: u32,
}
impl Default for BvhBin {
fn default() -> Self {
Self {
aabb: Aabb::new_invalid(),
leaf_count: 0,
}
}
}