diff --git a/beevee/src/bvh/accelerated.rs b/beevee/src/bvh/accelerated.rs new file mode 100644 index 0000000..2161148 --- /dev/null +++ b/beevee/src/bvh/accelerated.rs @@ -0,0 +1,37 @@ +use super::Intersected; +use crate::aabb::Bounded; +use crate::ray::Ray; + +/// The trait for any mesh-like object to be used in the [`BVH`]. If your object is not an +/// aggregate, you should instead implement [`Intersected`] which derives this trait automatically. +/// +/// This trait is there to accomodate for aggregate objects inside the [`BVH`]: you can implement a +/// faster look-up of information using a [`BVH`] in a mesh for example, returning directly the +/// reference to a hit triangle. This enables us to return this triangle instead of returning a +/// reference to the whole mesh. +/// +/// [`BVH`]: struct.BVH.html +/// [`Intersected`]: struct.Intersected.html +pub trait Accelerated: Bounded { + /// The type contained in your [`Accelerated`] structure + /// + /// [`Accelerated`]: struct.Accelerated.html + type Output; + + /// Return None if no intersection happens with the ray, or a tuple of distance along the ray + /// and a reference to the object that was hit. + fn intersect(&self, ray: &Ray) -> Option<(f32, &Self::Output)>; +} + +/// The automatic implementation for any [`Intersected`] object to be used in the [`BVH`]. +impl Accelerated for T +where + T: Intersected, +{ + type Output = Self; + + /// Return a reference to `self` when a distance was found. + fn intersect(&self, ray: &Ray) -> Option<(f32, &Self::Output)> { + self.intersect(ray).map(|t| (t, self)) + } +} diff --git a/beevee/src/bvh/intersected.rs b/beevee/src/bvh/intersected.rs index e5a4316..a079e94 100644 --- a/beevee/src/bvh/intersected.rs +++ b/beevee/src/bvh/intersected.rs @@ -1,8 +1,11 @@ use crate::aabb::Bounded; use crate::ray::Ray; -/// The trait for any object to be used in the [`BVH`]. +/// The trait for any object to be used in the [`BVH`]. Its derivation for [`Accelerated`] is +/// automatically derived to return a reference to itself. If this not the intended semantics, see +/// [`Accelerated`]. /// +/// [`Accelerated`]: struct.Accelerated.html /// [`BVH`]: struct.BVH.html pub trait Intersected: Bounded { /// Return None if there is no intersection, or the distance along the ray to the closest diff --git a/beevee/src/bvh/mod.rs b/beevee/src/bvh/mod.rs index 53a389c..14d6a5c 100644 --- a/beevee/src/bvh/mod.rs +++ b/beevee/src/bvh/mod.rs @@ -1,5 +1,8 @@ //! The Boudning Volume Hiearchy +mod accelerated; +pub use accelerated::*; + mod intersected; pub use intersected::*; diff --git a/beevee/src/bvh/tree.rs b/beevee/src/bvh/tree.rs index ae1493f..3e8cd5a 100644 --- a/beevee/src/bvh/tree.rs +++ b/beevee/src/bvh/tree.rs @@ -1,4 +1,4 @@ -use super::Intersected; +use super::Accelerated; use crate::aabb::AABB; use crate::ray::Ray; use crate::Axis; @@ -23,9 +23,9 @@ struct Node { } /// The BVH containing all the objects of type O. -/// This type must implement [`Intersected`]. +/// This type must implement [`Accelerated`]. /// -/// [`Intersected`]: trait.Intersected.html +/// [`Accelerated`]: trait.Accelerated.html #[derive(Clone, Debug, PartialEq)] pub struct BVH { tree: Node, @@ -92,7 +92,7 @@ impl BVH { /// let spheres: &mut [Sphere] = &mut [Sphere{ center: Point::origin(), radius: 2.5 }]; /// let bvh = BVH::build(spheres); /// ``` - pub fn build(objects: &mut [O]) -> Self { + pub fn build(objects: &mut [O]) -> Self { Self::with_max_capacity(objects, 32) } @@ -157,7 +157,7 @@ impl BVH { /// let spheres: &mut [Sphere] = &mut [Sphere{ center: Point::origin(), radius: 2.5 }]; /// let bvh = BVH::with_max_capacity(spheres, 32); /// ``` - pub fn with_max_capacity(objects: &mut [O], max_cap: usize) -> Self { + pub fn with_max_capacity(objects: &mut [O], max_cap: usize) -> Self { let tree = build_node(objects, 0, objects.len(), max_cap); Self { tree } } @@ -226,8 +226,8 @@ impl BVH { /// let bvh = BVH::with_max_capacity(spheres, 32); /// assert!(bvh.is_sound(spheres)); /// ``` - pub fn is_sound(&self, objects: &[O]) -> bool { - fn check_node(objects: &[O], node: &Node) -> bool { + pub fn is_sound(&self, objects: &[O]) -> bool { + fn check_node(objects: &[O], node: &Node) -> bool { if node.begin > node.end { return false; } @@ -322,17 +322,21 @@ impl BVH { /// assert_eq!(dist, 0.5); /// assert_eq!(obj, &spheres[0]); /// ``` - pub fn walk<'o, O: Intersected>(&self, ray: &Ray, objects: &'o [O]) -> Option<(f32, &'o O)> { + pub fn walk<'o, O: Accelerated>( + &self, + ray: &Ray, + objects: &'o [O], + ) -> Option<(f32, &'o O::Output)> { walk_rec_helper(ray, objects, &self.tree, std::f32::INFINITY) } } -fn walk_rec_helper<'o, O: Intersected>( +fn walk_rec_helper<'o, O: Accelerated>( ray: &Ray, objects: &'o [O], node: &Node, min: f32, -) -> Option<(f32, &'o O)> { +) -> Option<(f32, &'o O::Output)> { use std::cmp::Ordering; match &node.kind { @@ -340,7 +344,7 @@ fn walk_rec_helper<'o, O: Intersected>( NodeEnum::Leaf => objects[node.begin..node.end] .iter() // This turns the Option of an intersection into an Option<(f32, &O)> - .filter_map(|o| o.intersect(ray).map(|d| (d, o))) + .filter_map(|o| o.intersect(ray)) // Discard values that are too far away .filter(|(dist, _)| dist < &min) // Only keep the minimum value, if there is one @@ -382,14 +386,14 @@ fn walk_rec_helper<'o, O: Intersected>( } } -fn bounds_from_slice(objects: &[O]) -> AABB { +fn bounds_from_slice(objects: &[O]) -> AABB { objects .iter() .map(|o| o.aabb()) .fold(AABB::empty(), |acc, other| acc.union(&other)) } -fn build_node(objects: &mut [O], begin: usize, end: usize, max_cap: usize) -> Node { +fn build_node(objects: &mut [O], begin: usize, end: usize, max_cap: usize) -> Node { let aabb = bounds_from_slice(objects); // Don't split nodes under capacity if objects.len() <= max_cap { @@ -437,7 +441,7 @@ fn build_node(objects: &mut [O], begin: usize, end: usize, max_c /// Returns the index at which to split for SAH, the Axis along which to split, and the calculated /// cost. -fn compute_sah( +fn compute_sah( objects: &mut [O], surface: f32, max_cap: usize,