beevee: bvh: use Accelerated trait for objects

This will allow for the use of meshes inside the BVH.

Returning the reference to a triangle inside the mesh directly instead
of returning the reference to the mesh itself allows for more optimum
execution.
This commit is contained in:
Bruno BELANYI 2020-03-25 23:44:04 +01:00
parent 5c0fc9689e
commit 3039607e4f
4 changed files with 62 additions and 15 deletions

View file

@ -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<T> 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))
}
}

View file

@ -1,8 +1,11 @@
use crate::aabb::Bounded; use crate::aabb::Bounded;
use crate::ray::Ray; 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 /// [`BVH`]: struct.BVH.html
pub trait Intersected: Bounded { pub trait Intersected: Bounded {
/// Return None if there is no intersection, or the distance along the ray to the closest /// Return None if there is no intersection, or the distance along the ray to the closest

View file

@ -1,5 +1,8 @@
//! The Boudning Volume Hiearchy //! The Boudning Volume Hiearchy
mod accelerated;
pub use accelerated::*;
mod intersected; mod intersected;
pub use intersected::*; pub use intersected::*;

View file

@ -1,4 +1,4 @@
use super::Intersected; use super::Accelerated;
use crate::aabb::AABB; use crate::aabb::AABB;
use crate::ray::Ray; use crate::ray::Ray;
use crate::Axis; use crate::Axis;
@ -23,9 +23,9 @@ struct Node {
} }
/// The BVH containing all the objects of type O. /// 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)] #[derive(Clone, Debug, PartialEq)]
pub struct BVH { pub struct BVH {
tree: Node, tree: Node,
@ -92,7 +92,7 @@ impl BVH {
/// let spheres: &mut [Sphere] = &mut [Sphere{ center: Point::origin(), radius: 2.5 }]; /// let spheres: &mut [Sphere] = &mut [Sphere{ center: Point::origin(), radius: 2.5 }];
/// let bvh = BVH::build(spheres); /// let bvh = BVH::build(spheres);
/// ``` /// ```
pub fn build<O: Intersected>(objects: &mut [O]) -> Self { pub fn build<O: Accelerated>(objects: &mut [O]) -> Self {
Self::with_max_capacity(objects, 32) 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 spheres: &mut [Sphere] = &mut [Sphere{ center: Point::origin(), radius: 2.5 }];
/// let bvh = BVH::with_max_capacity(spheres, 32); /// let bvh = BVH::with_max_capacity(spheres, 32);
/// ``` /// ```
pub fn with_max_capacity<O: Intersected>(objects: &mut [O], max_cap: usize) -> Self { pub fn with_max_capacity<O: Accelerated>(objects: &mut [O], max_cap: usize) -> Self {
let tree = build_node(objects, 0, objects.len(), max_cap); let tree = build_node(objects, 0, objects.len(), max_cap);
Self { tree } Self { tree }
} }
@ -226,8 +226,8 @@ impl BVH {
/// let bvh = BVH::with_max_capacity(spheres, 32); /// let bvh = BVH::with_max_capacity(spheres, 32);
/// assert!(bvh.is_sound(spheres)); /// assert!(bvh.is_sound(spheres));
/// ``` /// ```
pub fn is_sound<O: Intersected>(&self, objects: &[O]) -> bool { pub fn is_sound<O: Accelerated>(&self, objects: &[O]) -> bool {
fn check_node<O: Intersected>(objects: &[O], node: &Node) -> bool { fn check_node<O: Accelerated>(objects: &[O], node: &Node) -> bool {
if node.begin > node.end { if node.begin > node.end {
return false; return false;
} }
@ -322,17 +322,21 @@ impl BVH {
/// assert_eq!(dist, 0.5); /// assert_eq!(dist, 0.5);
/// assert_eq!(obj, &spheres[0]); /// 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) 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, ray: &Ray,
objects: &'o [O], objects: &'o [O],
node: &Node, node: &Node,
min: f32, min: f32,
) -> Option<(f32, &'o O)> { ) -> Option<(f32, &'o O::Output)> {
use std::cmp::Ordering; use std::cmp::Ordering;
match &node.kind { match &node.kind {
@ -340,7 +344,7 @@ fn walk_rec_helper<'o, O: Intersected>(
NodeEnum::Leaf => objects[node.begin..node.end] NodeEnum::Leaf => objects[node.begin..node.end]
.iter() .iter()
// This turns the Option<f32> of an intersection into an Option<(f32, &O)> // This turns the Option<f32> 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 // Discard values that are too far away
.filter(|(dist, _)| dist < &min) .filter(|(dist, _)| dist < &min)
// Only keep the minimum value, if there is one // Only keep the minimum value, if there is one
@ -382,14 +386,14 @@ fn walk_rec_helper<'o, O: Intersected>(
} }
} }
fn bounds_from_slice<O: Intersected>(objects: &[O]) -> AABB { fn bounds_from_slice<O: Accelerated>(objects: &[O]) -> AABB {
objects objects
.iter() .iter()
.map(|o| o.aabb()) .map(|o| o.aabb())
.fold(AABB::empty(), |acc, other| acc.union(&other)) .fold(AABB::empty(), |acc, other| acc.union(&other))
} }
fn build_node<O: Intersected>(objects: &mut [O], begin: usize, end: usize, max_cap: usize) -> Node { fn build_node<O: Accelerated>(objects: &mut [O], begin: usize, end: usize, max_cap: usize) -> Node {
let aabb = bounds_from_slice(objects); let aabb = bounds_from_slice(objects);
// Don't split nodes under capacity // Don't split nodes under capacity
if objects.len() <= max_cap { if objects.len() <= max_cap {
@ -437,7 +441,7 @@ fn build_node<O: Intersected>(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 /// Returns the index at which to split for SAH, the Axis along which to split, and the calculated
/// cost. /// cost.
fn compute_sah<O: Intersected>( fn compute_sah<O: Accelerated>(
objects: &mut [O], objects: &mut [O],
surface: f32, surface: f32,
max_cap: usize, max_cap: usize,