use crate::aabb::Bounded;
use crate::bvh::{BVHNode, BVH};
use crate::ray::Ray;
#[allow(clippy::upper_case_acronyms)]
pub struct BVHTraverseIterator<'a, Shape: Bounded> {
bvh: &'a BVH,
ray: &'a Ray,
shapes: &'a [Shape],
stack: [usize; 32],
node_index: usize,
stack_size: usize,
has_node: bool,
}
impl<'a, Shape: Bounded> BVHTraverseIterator<'a, Shape> {
pub fn new(bvh: &'a BVH, ray: &'a Ray, shapes: &'a [Shape]) -> Self {
BVHTraverseIterator {
bvh,
ray,
shapes,
stack: [0; 32],
node_index: 0,
stack_size: 0,
has_node: true,
}
}
fn is_stack_empty(&self) -> bool {
self.stack_size == 0
}
fn stack_push(&mut self, node: usize) {
self.stack[self.stack_size] = node;
self.stack_size += 1;
}
fn stack_pop(&mut self) -> usize {
self.stack_size -= 1;
self.stack[self.stack_size]
}
fn move_left(&mut self) {
match self.bvh.nodes[self.node_index] {
BVHNode::Node {
child_l_index,
ref child_l_aabb,
..
} => {
if self.ray.intersects_aabb(child_l_aabb) {
self.node_index = child_l_index;
self.has_node = true;
} else {
self.has_node = false;
}
}
BVHNode::Leaf { .. } => {
self.has_node = false;
}
}
}
fn move_right(&mut self) {
match self.bvh.nodes[self.node_index] {
BVHNode::Node {
child_r_index,
ref child_r_aabb,
..
} => {
if self.ray.intersects_aabb(child_r_aabb) {
self.node_index = child_r_index;
self.has_node = true;
} else {
self.has_node = false;
}
}
BVHNode::Leaf { .. } => {
self.has_node = false;
}
}
}
}
impl<'a, Shape: Bounded> Iterator for BVHTraverseIterator<'a, Shape> {
type Item = &'a Shape;
fn next(&mut self) -> Option<&'a Shape> {
loop {
if self.is_stack_empty() && !self.has_node {
break;
}
if self.has_node {
self.stack_push(self.node_index);
self.move_left();
} else {
self.node_index = self.stack_pop();
match self.bvh.nodes[self.node_index] {
BVHNode::Node { .. } => {
self.move_right();
}
BVHNode::Leaf { shape_index, .. } => {
self.has_node = false;
return Some(&self.shapes[shape_index]);
}
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use crate::bvh::BVH;
use crate::ray::Ray;
use crate::testbase::{generate_aligned_boxes, UnitBox};
use crate::{Point3, Vector3};
use std::collections::HashSet;
pub fn build_some_bvh() -> (Vec<UnitBox>, BVH) {
let mut boxes = generate_aligned_boxes();
let bvh = BVH::build(&mut boxes);
(boxes, bvh)
}
fn traverse_and_verify_vec(
ray_origin: Point3,
ray_direction: Vector3,
all_shapes: &[UnitBox],
bvh: &BVH,
expected_shapes: &HashSet<i32>,
) {
let ray = Ray::new(ray_origin, ray_direction);
let hit_shapes = bvh.traverse(&ray, all_shapes);
assert_eq!(expected_shapes.len(), hit_shapes.len());
for shape in hit_shapes {
assert!(expected_shapes.contains(&shape.id));
}
}
fn traverse_and_verify_iterator(
ray_origin: Point3,
ray_direction: Vector3,
all_shapes: &[UnitBox],
bvh: &BVH,
expected_shapes: &HashSet<i32>,
) {
let ray = Ray::new(ray_origin, ray_direction);
let it = bvh.traverse_iterator(&ray, all_shapes);
let mut count = 0;
for shape in it {
assert!(expected_shapes.contains(&shape.id));
count += 1;
}
assert_eq!(expected_shapes.len(), count);
}
fn traverse_and_verify_base(
ray_origin: Point3,
ray_direction: Vector3,
all_shapes: &[UnitBox],
bvh: &BVH,
expected_shapes: &HashSet<i32>,
) {
traverse_and_verify_vec(ray_origin, ray_direction, all_shapes, bvh, expected_shapes);
traverse_and_verify_iterator(ray_origin, ray_direction, all_shapes, bvh, expected_shapes);
}
pub fn traverse_some_bvh() {
let (all_shapes, bvh) = build_some_bvh();
{
let origin = Point3::new(-1000.0, 0.0, 0.0);
let direction = Vector3::new(1.0, 0.0, 0.0);
let mut expected_shapes = HashSet::new();
for id in -10..11 {
expected_shapes.insert(id);
}
traverse_and_verify_base(origin, direction, &all_shapes, &bvh, &expected_shapes);
}
{
let origin = Point3::new(0.0, -1000.0, 0.0);
let direction = Vector3::new(0.0, 1.0, 0.0);
let mut expected_shapes = HashSet::new();
expected_shapes.insert(0);
traverse_and_verify_base(origin, direction, &all_shapes, &bvh, &expected_shapes);
}
{
let origin = Point3::new(6.0, 0.5, 0.0);
let direction = Vector3::new(-2.0, -1.0, 0.0);
let mut expected_shapes = HashSet::new();
expected_shapes.insert(4);
expected_shapes.insert(5);
expected_shapes.insert(6);
traverse_and_verify_base(origin, direction, &all_shapes, &bvh, &expected_shapes);
}
}
#[test]
fn test_traverse_bvh() {
traverse_some_bvh();
}
}
#[cfg(all(feature = "bench", test))]
mod bench {
use crate::bvh::BVH;
use crate::testbase::{create_ray, load_sponza_scene};
#[bench]
fn bench_intersect_128rays_sponza_vec(b: &mut ::test::Bencher) {
let (mut triangles, bounds) = load_sponza_scene();
let bvh = BVH::build(&mut triangles);
let mut seed = 0;
b.iter(|| {
for _ in 0..128 {
let ray = create_ray(&mut seed, &bounds);
let hits = bvh.traverse(&ray, &triangles);
for triangle in &hits {
ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c);
}
}
});
}
#[bench]
fn bench_intersect_128rays_sponza_iter(b: &mut ::test::Bencher) {
let (mut triangles, bounds) = load_sponza_scene();
let bvh = BVH::build(&mut triangles);
let mut seed = 0;
b.iter(|| {
for _ in 0..128 {
let ray = create_ray(&mut seed, &bounds);
let hits = bvh.traverse_iterator(&ray, &triangles);
for triangle in hits {
ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c);
}
}
});
}
}