use std::f32;
use std::iter::repeat;
use std::slice::Iter;
use partition::partition;
use geometry::{BBox, Boundable};
use linalg::{Point, Ray, Axis, Vector};
pub struct BVH<T: Boundable> {
geometry: Vec<T>,
ordered_geom: Vec<usize>,
tree: Vec<FlatNode>,
max_geom: usize,
}
impl<T: Boundable> BVH<T> {
pub fn unanimated(max_geom: usize, geometry: Vec<T>) -> BVH<T> {
BVH::<T>::new(max_geom, geometry, 0.0, 0.0)
}
pub fn new(max_geom: usize, geometry: Vec<T>, start: f32, end: f32) -> BVH<T> {
assert!(!geometry.is_empty());
let mut flat_tree = Vec::new();
let mut ordered_geom = Vec::with_capacity(geometry.len());
{
let mut build_geom = Vec::with_capacity(geometry.len());
for (i, g) in geometry.iter().enumerate() {
build_geom.push(GeomInfo::new(g, i, start, end));
}
let mut total_nodes = 0;
let root = Box::new(BVH::build(&mut build_geom[..], &mut ordered_geom, &mut total_nodes,
max_geom, start, end));
flat_tree.reserve(total_nodes);
BVH::<T>::flatten_tree(&root, &mut flat_tree);
assert_eq!(flat_tree.len(), total_nodes);
assert_eq!(ordered_geom.len(), geometry.len());
}
BVH { geometry: geometry, ordered_geom: ordered_geom, tree: flat_tree, max_geom: max_geom }
}
pub fn rebuild(&mut self, start: f32, end: f32) {
self.tree.clear();
self.ordered_geom.clear();
let mut build_geom = Vec::with_capacity(self.geometry.len());
for (i, g) in self.geometry.iter().enumerate() {
build_geom.push(GeomInfo::new(g, i, start, end));
}
let mut total_nodes = 0;
let root = Box::new(BVH::build(&mut build_geom[..], &mut self.ordered_geom, &mut total_nodes,
self.max_geom, start, end));
self.tree.reserve(total_nodes);
BVH::<T>::flatten_tree(&root, &mut self.tree);
}
pub fn intersect<'a, F, R>(&'a self, ray: &mut Ray, f: F) -> Option<R>
where F: Fn(&mut Ray, &'a T) -> Option<R> {
let mut result = None;
let inv_dir = Vector::new(1.0 / ray.d.x, 1.0 / ray.d.y, 1.0 / ray.d.z);
let neg_dir = [(ray.d.x < 0.0) as usize, (ray.d.y < 0.0) as usize, (ray.d.z < 0.0) as usize];
let mut stack = [0; 64];
let mut stack_ptr = 0;
let mut current = 0;
loop {
let node = &self.tree[current];
if node.bounds.fast_intersect(ray, &inv_dir, &neg_dir) {
match node.node {
FlatNodeData::Leaf { ref geom_offset, ref ngeom } => {
for i in &self.ordered_geom[*geom_offset..*geom_offset + *ngeom] {
let o = &self.geometry[*i];
result = f(ray, o).or(result);
}
if stack_ptr == 0 {
break;
}
stack_ptr -= 1;
current = stack[stack_ptr];
},
FlatNodeData::Interior { ref second_child, ref axis } => {
let a = match *axis {
Axis::X => 0,
Axis::Y => 1,
Axis::Z => 2,
};
if neg_dir[a] != 0 {
stack[stack_ptr] = current + 1;
current = *second_child;
} else {
stack[stack_ptr] = *second_child;
current += 1;
}
stack_ptr += 1;
},
}
} else {
if stack_ptr == 0 {
break;
}
stack_ptr -= 1;
current = stack[stack_ptr];
}
}
result
}
pub fn iter(&self) -> Iter<T> {
self.geometry.iter()
}
fn build(build_info: &mut [GeomInfo<T>], ordered_geom: &mut Vec<usize>,
total_nodes: &mut usize, max_geom: usize, start: f32, end: f32) -> BuildNode {
*total_nodes += 1;
let bounds = build_info.iter().fold(BBox::new(), |b, g| b.box_union(&g.geom.bounds(start, end)));
let ngeom = build_info.len();
if ngeom == 1 {
return BVH::build_leaf(build_info, ordered_geom, bounds);
}
let centroids = build_info.iter().fold(BBox::new(), |b, g| b.point_union(&g.center));
let split_axis = centroids.max_extent();
let mut mid = build_info.len() / 2;
if (centroids.max[split_axis] - centroids.min[split_axis]).abs() < f32::EPSILON {
if ngeom < max_geom {
return BVH::build_leaf(&mut build_info[..], ordered_geom, bounds);
} else {
let l = Box::new(BVH::build(&mut build_info[..mid], ordered_geom,
total_nodes, max_geom, start, end));
let r = Box::new(BVH::build(&mut build_info[mid..], ordered_geom,
total_nodes, max_geom, start, end));
return BuildNode::interior([l, r], split_axis);
}
}
if ngeom < 5 {
build_info.sort_by(|a, b| {
match a.center[split_axis].partial_cmp(&b.center[split_axis]) {
Some(o) => o,
None => panic!("NaNs in build info centers?!"),
}
});
} else {
let mut buckets = [SAHBucket::new(); 12];
for g in build_info.iter() {
let b = ((g.center[split_axis] - centroids.min[split_axis])
/ (centroids.max[split_axis] - centroids.min[split_axis])
* buckets.len() as f32) as usize;
let b = if b == buckets.len() { b - 1 } else { b };
buckets[b].count += 1;
buckets[b].bounds = buckets[b].bounds.box_union(&g.bounds);
}
let mut cost = [0.0; 11];
for (i, c) in cost.iter_mut().enumerate() {
let left = buckets.iter().take(i + 1).fold(SAHBucket::new(), |mut s, b| {
s.bounds = s.bounds.box_union(&b.bounds);
s.count += b.count;
s
});
let right = buckets.iter().skip(i + 1).fold(SAHBucket::new(), |mut s, b| {
s.bounds = s.bounds.box_union(&b.bounds);
s.count += b.count;
s
});
*c = 0.125 + (left.count as f32 * left.bounds.surface_area()
+ right.count as f32 * right.bounds.surface_area()) / bounds.surface_area();
}
let (min_bucket, min_cost) = cost.iter().enumerate().fold((0, f32::INFINITY),
|(pi, pc), (i, c)| {
if *c < pc { (i, *c) } else { (pi, pc) }
});
if ngeom > max_geom || min_cost < ngeom as f32 {
mid = partition(build_info.iter_mut(),
|g| {
let b = ((g.center[split_axis] - centroids.min[split_axis])
/ (centroids.max[split_axis] - centroids.min[split_axis])
* buckets.len() as f32) as usize;
let b = if b == buckets.len() { b - 1 } else { b };
b <= min_bucket
});
}
else {
return BVH::build_leaf(build_info, ordered_geom, bounds);
}
}
assert!(mid != 0 && mid != build_info.len());
let l = Box::new(BVH::build(&mut build_info[..mid], ordered_geom,
total_nodes, max_geom, start, end));
let r = Box::new(BVH::build(&mut build_info[mid..], ordered_geom,
total_nodes, max_geom, start, end));
BuildNode::interior([l, r], split_axis)
}
fn build_leaf(build_info: &mut [GeomInfo<T>], ordered_geom: &mut Vec<usize>, bounds: BBox)
-> BuildNode {
let geom_offset = ordered_geom.len();
for g in build_info.iter() {
ordered_geom.push(g.geom_idx);
}
BuildNode::leaf(build_info.len(), geom_offset, bounds)
}
fn flatten_tree(node: &Box<BuildNode>, tree: &mut Vec<FlatNode>) -> usize {
let offset = tree.len();
match node.node {
BuildNodeData::Interior { children: ref c, split_axis: ref a } => {
tree.push(FlatNode::interior(node.bounds, 0, *a));
BVH::<T>::flatten_tree(&c[0], tree);
let second_child = BVH::<T>::flatten_tree(&c[1], tree);
match tree[offset].node {
FlatNodeData::Interior { second_child: ref mut s, .. } => *s = second_child,
_ => panic!("Interior node switched to leaf!?"),
};
},
BuildNodeData::Leaf { ngeom: ref n, geom_offset: ref o } => {
tree.push(FlatNode::leaf(node.bounds, *o, *n));
},
}
offset
}
}
impl<T: Boundable> Boundable for BVH<T> {
fn bounds(&self, _: f32, _: f32) -> BBox {
self.tree[0].bounds
}
}
#[derive(Debug)]
enum FlatNodeData {
Interior { second_child: usize, axis: Axis },
Leaf { geom_offset: usize, ngeom: usize },
}
#[derive(Debug)]
struct FlatNode {
bounds: BBox,
node: FlatNodeData,
}
impl FlatNode {
fn interior(bounds: BBox, second_child: usize, axis: Axis) -> FlatNode {
FlatNode { bounds: bounds, node: FlatNodeData::Interior { second_child: second_child, axis: axis } }
}
fn leaf(bounds: BBox, geom_offset: usize, ngeom: usize) -> FlatNode {
FlatNode { bounds: bounds, node: FlatNodeData::Leaf { geom_offset: geom_offset, ngeom: ngeom } }
}
}
struct GeomInfo<'a, T: 'a> {
geom: &'a T,
geom_idx: usize,
center: Point,
bounds: BBox,
}
impl<'a, T: Boundable> GeomInfo<'a, T> {
fn new(geom: &'a T, geom_idx: usize, start: f32, end: f32) -> GeomInfo<T> {
let bounds = geom.bounds(start, end);
GeomInfo { geom: geom, geom_idx: geom_idx,
center: bounds.lerp(0.5, 0.5, 0.5),
bounds: bounds }
}
}
#[derive(Debug)]
enum BuildNodeData {
Interior {
children: [Box<BuildNode>; 2],
split_axis: Axis,
},
Leaf {
ngeom: usize,
geom_offset: usize,
},
}
#[derive(Debug)]
struct BuildNode {
node: BuildNodeData,
bounds: BBox,
}
impl BuildNode {
fn interior(children: [Box<BuildNode>; 2], split_axis: Axis) -> BuildNode {
let bounds = children[0].bounds.box_union(&children[1].bounds);
BuildNode { node: BuildNodeData::Interior { children: children, split_axis: split_axis },
bounds: bounds }
}
fn leaf(ngeom: usize, geom_offset: usize, bounds: BBox) -> BuildNode {
BuildNode { node: BuildNodeData::Leaf { ngeom: ngeom, geom_offset: geom_offset }, bounds: bounds }
}
fn print_tree(&self, depth: usize) {
let ident: String = repeat(" ").take(depth).collect();
println!("{}BuildNode: {{", ident);
let pad: String = repeat(" ").take(depth + 2).collect();
println!("{}bounds: {:?}", pad, self.bounds);
match self.node {
BuildNodeData::Interior { children: ref c, split_axis: ref a } => {
println!("{}type: Interior", pad);
println!("{}split axis: {:?}", pad, a);
println!("{}left child:", pad);
c[0].print_tree(depth + 2);
println!("{}right child:", pad);
c[1].print_tree(depth + 2);
},
BuildNodeData::Leaf { ngeom: ref n, geom_offset: ref o } => {
println!("{}type: Leaf", pad);
println!("{}ngeom: {}", pad, n);
println!("{}geom offset: {}", pad, o);
},
}
println!("{}}}", ident);
}
}
#[derive(Copy, Clone, Debug)]
struct SAHBucket {
count: usize,
bounds: BBox,
}
impl SAHBucket {
fn new() -> SAHBucket {
SAHBucket { count: 0, bounds: BBox::new() }
}
}