use crate::{Aabb, Vec3};
pub trait BvhSource: Sync {
fn len(&self) -> usize;
fn bound(&self, index: u32) -> Aabb;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl BvhSource for [Aabb] {
#[inline]
fn len(&self) -> usize {
<[Aabb]>::len(self)
}
#[inline]
fn bound(&self, index: u32) -> Aabb {
match self.get(index as usize) {
Some(bound) => *bound,
None => Aabb::EMPTY,
}
}
}
impl BvhSource for Vec<Aabb> {
#[inline]
fn len(&self) -> usize {
self.as_slice().len()
}
#[inline]
fn bound(&self, index: u32) -> Aabb {
self.as_slice().bound(index)
}
}
impl<const N: usize> BvhSource for [Aabb; N] {
#[inline]
fn len(&self) -> usize {
N
}
#[inline]
fn bound(&self, index: u32) -> Aabb {
self.as_slice().bound(index)
}
}
impl<T: BvhSource + ?Sized> BvhSource for &T {
#[inline]
fn len(&self) -> usize {
(**self).len()
}
#[inline]
fn bound(&self, index: u32) -> Aabb {
(**self).bound(index)
}
}
#[derive(Clone, Copy, Debug)]
pub struct SphereBounds<'a> {
centers: &'a [[f32; 3]],
radii: &'a [f32],
}
impl<'a> SphereBounds<'a> {
#[must_use]
pub fn new(centers: &'a [[f32; 3]], radii: &'a [f32]) -> Self {
Self { centers, radii }
}
}
impl BvhSource for SphereBounds<'_> {
#[inline]
fn len(&self) -> usize {
self.centers.len().min(self.radii.len())
}
#[inline]
fn bound(&self, index: u32) -> Aabb {
let index = index as usize;
let (Some(center), Some(radius)) = (self.centers.get(index), self.radii.get(index)) else {
return Aabb::EMPTY;
};
let center = Vec3::from_array(*center);
let extent = Vec3::splat(radius.abs());
Aabb::new(center - extent, center + extent)
}
}
#[derive(Clone, Copy, Debug)]
pub struct SweptSphereBounds<'a> {
start: &'a [[f32; 3]],
end: &'a [[f32; 3]],
radii: &'a [f32],
}
impl<'a> SweptSphereBounds<'a> {
#[must_use]
pub fn new(start: &'a [[f32; 3]], end: &'a [[f32; 3]], radii: &'a [f32]) -> Self {
Self { start, end, radii }
}
}
impl BvhSource for SweptSphereBounds<'_> {
#[inline]
fn len(&self) -> usize {
self.start.len().min(self.end.len()).min(self.radii.len())
}
#[inline]
fn bound(&self, index: u32) -> Aabb {
let index = index as usize;
let (Some(start), Some(end), Some(radius)) = (
self.start.get(index),
self.end.get(index),
self.radii.get(index),
) else {
return Aabb::EMPTY;
};
let start = Vec3::from_array(*start);
let end = Vec3::from_array(*end);
let extent = Vec3::splat(radius.abs());
Aabb::new(start.min(end) - extent, start.max(end) + extent)
}
}
#[cfg(test)]
#[path = "source_tests.rs"]
mod tests;