From 29f41d2ec94356c73704634b1b86e28a8cb74ebd Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Fri, 21 Apr 2023 21:19:14 -0700 Subject: [PATCH 01/20] Move math types over to nalgebra --- Cargo.toml | 7 +- README.md | 10 +- examples/simple.rs | 18 +- src/aabb.rs | 390 +++++++++++++++++++++----------------- src/axis.rs | 153 --------------- src/bounding_hierarchy.rs | 68 ++++--- src/bvh/bvh_impl.rs | 210 +++++++++++++------- src/bvh/iter.rs | 31 +-- src/bvh/optimization.rs | 100 +++++----- src/flat_bvh.rs | 91 +++++---- src/lib.rs | 21 +- src/ray.rs | 388 ++++++++++++++----------------------- src/testbase.rs | 56 +++--- src/utils.rs | 26 ++- 14 files changed, 730 insertions(+), 839 deletions(-) delete mode 100644 src/axis.rs diff --git a/Cargo.toml b/Cargo.toml index bc3249a..5fee554 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ version = "0.7.2" edition = "2018" authors = [ "Sven-Hendrik Haase ", - "Alexander Dmitriev " + "Alexander Dmitriev ", ] readme = "README.md" repository = "https://github.com/svenstaro/bvh" @@ -18,9 +18,9 @@ resolver = "2" approx = "0.5" rand = "0.8" log = "0.4" -num = "0.4" -glam = "0.23" serde = { optional = true, version = "1", features = ["derive"] } +num = "0.4.0" +nalgebra = { version = "0.32.2", features = ["serde"] } [dev-dependencies] proptest = "1.0" @@ -30,7 +30,6 @@ doc-comment = "0.3" [features] bench = [] -serde = ["dep:serde", "glam/serde"] [profile.release] lto = true diff --git a/README.md b/README.md index 5a0b0e3..bac2311 100644 --- a/README.md +++ b/README.md @@ -25,21 +25,21 @@ iterative traversal of the BVH. use bvh::aabb::{AABB, Bounded}; use bvh::bounding_hierarchy::BHShape; use bvh::bvh::BVH; -use bvh::{Point3, Vector3}; use bvh::ray::Ray; +use nalgebra::{Point3, Vector3}; let origin = Point3::new(0.0,0.0,0.0); let direction = Vector3::new(1.0,0.0,0.0); let ray = Ray::new(origin, direction); struct Sphere { - position: Point3, + position: Point3, radius: f32, node_index: usize, } -impl Bounded for Sphere { - fn aabb(&self) -> AABB { +impl Bounded for Sphere { + fn aabb(&self) -> AABB { let half_size = Vector3::new(self.radius, self.radius, self.radius); let min = self.position - half_size; let max = self.position + half_size; @@ -47,7 +47,7 @@ impl Bounded for Sphere { } } -impl BHShape for Sphere { +impl BHShape for Sphere { fn set_bh_node_index(&mut self, index: usize) { self.node_index = index; } diff --git a/examples/simple.rs b/examples/simple.rs index 180d973..d1e9af2 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -2,25 +2,25 @@ use bvh::aabb::{Bounded, AABB}; use bvh::bounding_hierarchy::BHShape; use bvh::bvh::BVH; use bvh::ray::Ray; -use bvh::{Point3, Vector3}; +use nalgebra::{Point, SVector}; #[derive(Debug)] struct Sphere { - position: Point3, + position: Point, radius: f32, node_index: usize, } -impl Bounded for Sphere { - fn aabb(&self) -> AABB { - let half_size = Vector3::new(self.radius, self.radius, self.radius); +impl Bounded for Sphere { + fn aabb(&self) -> AABB { + let half_size = SVector::::new(self.radius, self.radius, self.radius); let min = self.position - half_size; let max = self.position + half_size; AABB::with_bounds(min, max) } } -impl BHShape for Sphere { +impl BHShape for Sphere { fn set_bh_node_index(&mut self, index: usize) { self.node_index = index; } @@ -33,7 +33,7 @@ impl BHShape for Sphere { pub fn main() { let mut spheres = Vec::new(); for i in 0..1000000u32 { - let position = Point3::new(i as f32, i as f32, i as f32); + let position = Point::::new(i as f32, i as f32, i as f32); let radius = (i % 10) as f32 + 1.0; spheres.push(Sphere { position, @@ -43,8 +43,8 @@ pub fn main() { } let bvh = BVH::build(&mut spheres); - let origin = Point3::new(0.0, 0.0, 0.0); - let direction = Vector3::new(1.0, 0.0, 0.0); + let origin = Point::::new(0.0, 0.0, 0.0); + let direction = SVector::::new(1.0, 0.0, 0.0); let ray = Ray::new(origin, direction); let hit_sphere_aabbs = bvh.traverse(&ray, &spheres); dbg!(hit_sphere_aabbs); diff --git a/src/aabb.rs b/src/aabb.rs index 1347d63..b93bd22 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -1,26 +1,34 @@ //! Axis Aligned Bounding Boxes. -use std::f32; use std::fmt; use std::ops::Index; -use crate::{Point3, Vector3}; - -use crate::axis::Axis; +use nalgebra::ClosedAdd; +use nalgebra::ClosedMul; +use nalgebra::ClosedSub; +use nalgebra::Point; +use nalgebra::SVector; +use nalgebra::Scalar; +use nalgebra::SimdPartialOrd; +use num::Float; +use num::FromPrimitive; +use num::One; +use num::Signed; +use num::Zero; /// AABB struct. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(clippy::upper_case_acronyms)] -pub struct AABB { +pub struct AABB { /// Minimum coordinates - pub min: Point3, + pub min: Point, /// Maximum coordinates - pub max: Point3, + pub max: Point, } -impl fmt::Display for AABB { +impl fmt::Display for AABB { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Min bound: {}; Max bound: {}", self.min, self.max) } @@ -30,18 +38,18 @@ impl fmt::Display for AABB { /// /// [`AABB`]: struct.AABB.html /// -pub trait Bounded { +pub trait Bounded { /// Returns the geometric bounds of this object in the form of an [`AABB`]. /// /// # Examples /// ``` /// use bvh::aabb::{AABB, Bounded}; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// struct Something; /// - /// impl Bounded for Something { - /// fn aabb(&self) -> AABB { + /// impl Bounded for Something { + /// fn aabb(&self) -> AABB { /// let point1 = Point3::new(0.0,0.0,0.0); /// let point2 = Point3::new(1.0,1.0,1.0); /// AABB::with_bounds(point1, point2) @@ -57,34 +65,34 @@ pub trait Bounded { /// /// [`AABB`]: struct.AABB.html /// - fn aabb(&self) -> AABB; + fn aabb(&self) -> AABB; } -impl Bounded for &T { - fn aabb(&self) -> AABB { - T::aabb(self) +impl> Bounded for &B { + fn aabb(&self) -> AABB { + B::aabb(self) } } -impl Bounded for &mut T { - fn aabb(&self) -> AABB { - T::aabb(self) +impl> Bounded for &mut B { + fn aabb(&self) -> AABB { + B::aabb(self) } } -impl Bounded for Box { - fn aabb(&self) -> AABB { - T::aabb(self) +impl> Bounded for Box { + fn aabb(&self) -> AABB { + B::aabb(self) } } -impl AABB { +impl AABB { /// Creates a new [`AABB`] with the given bounds. /// /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb = AABB::with_bounds(Point3::new(-1.0,-1.0,-1.0), Point3::new(1.0,1.0,1.0)); /// assert_eq!(aabb.min.x, -1.0); @@ -93,7 +101,7 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn with_bounds(min: Point3, max: Point3) -> AABB { + pub fn with_bounds(min: Point, max: Point) -> Self { AABB { min, max } } @@ -104,7 +112,7 @@ impl AABB { /// use bvh::aabb::AABB; /// /// # fn main() { - /// let aabb = AABB::empty(); + /// let aabb = AABB::::empty(); /// let min = &aabb.min; /// let max = &aabb.max; /// @@ -121,10 +129,47 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn empty() -> AABB { - AABB { - min: Point3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY), - max: Point3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY), + pub fn empty() -> Self + where + T: Float, + { + Self { + min: SVector::::from_element(T::infinity()).into(), + max: SVector::::from_element(T::neg_infinity()).into(), + } + } + + /// Creates a new infinite [`AABB`]. + /// + /// # Examples + /// ``` + /// use bvh::aabb::AABB; + /// + /// # fn main() { + /// let aabb :AABB = AABB::infinite(); + /// let min = &aabb.min; + /// let max = &aabb.max; + /// + /// // For any point + /// let x = rand::random(); + /// let y = rand::random(); + /// let z = rand::random(); + /// + /// // An infintie AABB should contain it + /// assert!(x > min.x && y > min.y && z > min.z); + /// assert!(max.x > x && max.y > y && max.z > z); + /// # } + /// ``` + /// + /// [`AABB`]: struct.AABB.html + /// + pub fn infinite() -> Self + where + T: Float, + { + Self { + min: SVector::::from_element(T::neg_infinity()).into(), + max: SVector::::from_element(T::infinity()).into(), } } @@ -133,7 +178,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_inside = Point3::new(0.125, -0.25, 0.5); @@ -144,15 +189,13 @@ impl AABB { /// ``` /// /// [`AABB`]: struct.AABB.html - /// [`Point3`]: glam::Vec3 - /// - pub fn contains(&self, p: &Point3) -> bool { - p.x >= self.min.x - && p.x <= self.max.x - && p.y >= self.min.y - && p.y <= self.max.y - && p.z >= self.min.z - && p.z <= self.max.z + /// [`Point3`]: nalgebra::Vec3 + /// + pub fn contains(&self, p: &Point) -> bool + where + T: PartialOrd, + { + p >= &self.min && p <= &self.max } /// Returns true if the [`Point3`] is approximately inside the [`AABB`] @@ -160,28 +203,27 @@ impl AABB { /// /// # Examples /// ``` - /// use bvh::EPSILON; /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_barely_outside = Point3::new(1.000_000_1, -1.000_000_1, 1.000_000_001); /// let point_outside = Point3::new(1.0, -2.0, 4.0); /// - /// assert!(aabb.approx_contains_eps(&point_barely_outside, EPSILON)); - /// assert!(!aabb.approx_contains_eps(&point_outside, EPSILON)); + /// assert!(aabb.approx_contains_eps(&point_barely_outside, 0.00001)); + /// assert!(!aabb.approx_contains_eps(&point_outside, 0.00001)); /// ``` /// /// [`AABB`]: struct.AABB.html - /// [`Point3`]: glam::Vec3 - /// - pub fn approx_contains_eps(&self, p: &Point3, epsilon: f32) -> bool { - (p.x - self.min.x) > -epsilon - && (p.x - self.max.x) < epsilon - && (p.y - self.min.y) > -epsilon - && (p.y - self.max.y) < epsilon - && (p.z - self.min.z) > -epsilon - && (p.z - self.max.z) < epsilon + /// [`Point3`]: nalgebra::Point3 + /// + pub fn approx_contains_eps(&self, p: &Point, epsilon: T) -> bool + where + T: ClosedSub + PartialOrd + Float, + { + let ne = -epsilon; + (p - self.min) > SVector::from_element(ne) + && (p - self.max) < SVector::from_element(epsilon) } /// Returns true if the `other` [`AABB`] is approximately inside this [`AABB`] @@ -189,20 +231,22 @@ impl AABB { /// /// # Examples /// ``` - /// use bvh::EPSILON; /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_barely_outside = Point3::new(1.000_000_1, 1.000_000_1, 1.000_000_1); /// let center = aabb.center(); /// let inner_aabb = AABB::with_bounds(center, point_barely_outside); /// - /// assert!(aabb.approx_contains_aabb_eps(&inner_aabb, EPSILON)); + /// assert!(aabb.approx_contains_aabb_eps(&inner_aabb, 0.00001)); /// ``` /// /// [`AABB`]: struct.AABB.html - pub fn approx_contains_aabb_eps(&self, other: &AABB, epsilon: f32) -> bool { + pub fn approx_contains_aabb_eps(&self, other: &AABB, epsilon: T) -> bool + where + T: ClosedSub + PartialOrd + Float, + { self.approx_contains_eps(&other.min, epsilon) && self.approx_contains_eps(&other.max, epsilon) } @@ -212,26 +256,24 @@ impl AABB { /// /// # Examples /// ``` - /// use bvh::EPSILON; /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_barely_outside_min = Point3::new(-1.000_000_1, -1.000_000_1, -1.000_000_1); /// let point_barely_outside_max = Point3::new(1.000_000_1, 1.000_000_1, 1.000_000_1); /// let other = AABB::with_bounds(point_barely_outside_min, point_barely_outside_max); /// - /// assert!(aabb.relative_eq(&other, EPSILON)); + /// assert!(aabb.relative_eq(&other, 0.00001)); /// ``` /// /// [`AABB`]: struct.AABB.html - pub fn relative_eq(&self, other: &AABB, epsilon: f32) -> bool { - f32::abs(self.min.x - other.min.x) < epsilon - && f32::abs(self.min.y - other.min.y) < epsilon - && f32::abs(self.min.z - other.min.z) < epsilon - && f32::abs(self.max.x - other.max.x) < epsilon - && f32::abs(self.max.y - other.max.y) < epsilon - && f32::abs(self.max.z - other.max.z) < epsilon + pub fn relative_eq(&self, other: &AABB, epsilon: T) -> bool + where + T: ClosedSub + PartialOrd + Signed, + { + let ep_vec = SVector::from_element(epsilon); + (self.min - other.min).abs() < ep_vec && (self.max - other.max).abs() < ep_vec } /// Returns a new minimal [`AABB`] which contains both this [`AABB`] and `other`. @@ -240,7 +282,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb1 = AABB::with_bounds(Point3::new(-101.0, 0.0, 0.0), Point3::new(-100.0, 1.0, 1.0)); /// let aabb2 = AABB::with_bounds(Point3::new(100.0, 0.0, 0.0), Point3::new(101.0, 1.0, 1.0)); @@ -265,18 +307,13 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn join(&self, other: &AABB) -> AABB { + pub fn join(&self, other: &AABB) -> AABB + where + T: SimdPartialOrd, + { AABB::with_bounds( - Point3::new( - self.min.x.min(other.min.x), - self.min.y.min(other.min.y), - self.min.z.min(other.min.z), - ), - Point3::new( - self.max.x.max(other.max.x), - self.max.y.max(other.max.y), - self.max.z.max(other.max.z), - ), + self.min.coords.inf(&other.min.coords).into(), + self.max.coords.sup(&other.max.coords).into(), ) } @@ -285,7 +322,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::{Point3, Vector3}; + /// use nalgebra::{Point3, Vector3}; /// /// let size = Vector3::new(1.0, 1.0, 1.0); /// let aabb_pos = Point3::new(-101.0, 0.0, 0.0); @@ -315,17 +352,11 @@ impl AABB { /// /// [`AABB::join`]: struct.AABB.html /// - pub fn join_mut(&mut self, other: &AABB) { - self.min = Point3::new( - self.min.x.min(other.min.x), - self.min.y.min(other.min.y), - self.min.z.min(other.min.z), - ); - self.max = Point3::new( - self.max.x.max(other.max.x), - self.max.y.max(other.max.y), - self.max.z.max(other.max.z), - ); + pub fn join_mut(&mut self, other: &AABB) + where + T: SimdPartialOrd, + { + *self = self.join(other); } /// Returns a new minimal [`AABB`] which contains both @@ -334,7 +365,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let point1 = Point3::new(0.0, 0.0, 0.0); /// let point2 = Point3::new(1.0, 1.0, 1.0); @@ -352,20 +383,15 @@ impl AABB { /// ``` /// /// [`AABB`]: struct.AABB.html - /// [`Point3`]: glam::Vec3 + /// [`Point3`]: nalgebra::Point3 /// - pub fn grow(&self, other: &Point3) -> AABB { + pub fn grow(&self, other: &Point) -> AABB + where + T: SimdPartialOrd, + { AABB::with_bounds( - Point3::new( - self.min.x.min(other.x), - self.min.y.min(other.y), - self.min.z.min(other.z), - ), - Point3::new( - self.max.x.max(other.x), - self.max.y.max(other.y), - self.max.z.max(other.z), - ), + self.min.coords.inf(&other.coords).into(), + self.max.coords.sup(&other.coords).into(), ) } @@ -374,7 +400,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let point1 = Point3::new(0.0, 0.0, 0.0); /// let point2 = Point3::new(1.0, 1.0, 1.0); @@ -393,19 +419,13 @@ impl AABB { /// ``` /// /// [`AABB::grow`]: struct.AABB.html - /// [`Point3`]: glam::Vec3 - /// - pub fn grow_mut(&mut self, other: &Point3) { - self.min = Point3::new( - self.min.x.min(other.x), - self.min.y.min(other.y), - self.min.z.min(other.z), - ); - self.max = Point3::new( - self.max.x.max(other.x), - self.max.y.max(other.y), - self.max.z.max(other.z), - ); + /// [`Point3`]: nalgebra::Point3 + /// + pub fn grow_mut(&mut self, other: &Point) + where + T: SimdPartialOrd, + { + *self = self.grow(other); } /// Returns a new minimal [`AABB`] which contains both this [`AABB`] and the [`Bounded`] @@ -414,12 +434,12 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::{AABB, Bounded}; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// struct Something; /// - /// impl Bounded for Something { - /// fn aabb(&self) -> AABB { + /// impl Bounded for Something { + /// fn aabb(&self) -> AABB { /// let point1 = Point3::new(0.0,0.0,0.0); /// let point2 = Point3::new(1.0,1.0,1.0); /// AABB::with_bounds(point1, point2) @@ -437,7 +457,10 @@ impl AABB { /// [`AABB`]: struct.AABB.html /// [`Bounded`]: trait.Bounded.html /// - pub fn join_bounded(&self, other: &T) -> AABB { + pub fn join_bounded>(&self, other: &B) -> AABB + where + T: SimdPartialOrd, + { self.join(&other.aabb()) } @@ -446,7 +469,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let aabb = AABB::with_bounds(Point3::new(-1.0,-1.0,-1.0), Point3::new(1.0,1.0,1.0)); /// let size = aabb.size(); @@ -455,7 +478,10 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn size(&self) -> Vector3 { + pub fn size(&self) -> SVector + where + T: ClosedSub, + { self.max - self.min } @@ -464,7 +490,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); @@ -475,10 +501,13 @@ impl AABB { /// ``` /// /// [`AABB`]: struct.AABB.html - /// [`Point3`]: glam::Vec3 + /// [`Point3`]: nalgebra::Point3 /// - pub fn center(&self) -> Point3 { - self.min + (self.size() / 2.0) + pub fn center(&self) -> Point + where + T: ClosedSub + ClosedAdd + ClosedMul + FromPrimitive, + { + (self.min.coords + (self.size() * T::from_f32(0.5).unwrap())).into() } /// An empty [`AABB`] is an [`AABB`] where the lower bound is greater than @@ -487,9 +516,9 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// - /// let empty_aabb = AABB::empty(); + /// let empty_aabb: AABB = AABB::empty(); /// assert!(empty_aabb.is_empty()); /// /// let min = Point3::new(41.0,41.0,41.0); @@ -501,8 +530,15 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn is_empty(&self) -> bool { - self.min.x > self.max.x || self.min.y > self.max.y || self.min.z > self.max.z + pub fn is_empty(&self) -> bool + where + T: SimdPartialOrd, + { + // Special trick here, we use a join/supremum to pick the highest values, and if the highest + // values are not equal to the max, then obviously a min was higher! + // This should be two simd instructions (I hope) for vectors/points up to size 4. + // It might need to be changed to an iterative method later for dimensions above 4 + self.min.coords.sup(&self.max.coords) != self.max.coords } /// Returns the total surface area of this [`AABB`]. @@ -510,7 +546,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); @@ -522,9 +558,12 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn surface_area(&self) -> f32 { + pub fn surface_area(&self) -> T + where + T: FromPrimitive + ClosedSub + ClosedAdd + ClosedMul + Zero, + { let size = self.size(); - 2.0 * (size.x * size.y + size.x * size.z + size.y * size.z) + T::from_f32(2.0).unwrap() * size.dot(&size) } /// Returns the volume of this [`AABB`]. @@ -532,7 +571,7 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); @@ -544,9 +583,11 @@ impl AABB { /// /// [`AABB`]: struct.AABB.html /// - pub fn volume(&self) -> f32 { - let size = self.size(); - size.x * size.y * size.z + pub fn volume(&self) -> T + where + T: ClosedSub + ClosedMul + One, + { + self.size().product() } /// Returns the axis along which the [`AABB`] is stretched the most. @@ -554,28 +595,23 @@ impl AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; - /// use bvh::axis::Axis; - /// use bvh::Point3; + /// use nalgebra::Point3; /// /// let min = Point3::new(-100.0,0.0,0.0); /// let max = Point3::new(100.0,0.0,0.0); /// /// let aabb = AABB::with_bounds(min, max); /// let axis = aabb.largest_axis(); - /// assert!(axis == Axis::X); + /// assert!(axis == 0); /// ``` /// /// [`AABB`]: struct.AABB.html /// - pub fn largest_axis(&self) -> Axis { - let size = self.size(); - if size.x > size.y && size.x > size.z { - Axis::X - } else if size.y > size.z { - Axis::Y - } else { - Axis::Z - } + pub fn largest_axis(&self) -> usize + where + T: ClosedSub + PartialOrd, + { + self.size().imax() } } @@ -584,8 +620,8 @@ impl AABB { /// [`AABB`]: struct.AABB.html /// [`empty()`]: #method.empty /// -impl Default for AABB { - fn default() -> AABB { +impl Default for AABB { + fn default() -> AABB { AABB::empty() } } @@ -596,7 +632,7 @@ impl Default for AABB { /// # Examples /// ``` /// use bvh::aabb::AABB; -/// use bvh::Point3; +/// use nalgebra::Point3; /// /// let min = Point3::new(3.0,4.0,5.0); /// let max = Point3::new(123.0,123.0,123.0); @@ -608,15 +644,11 @@ impl Default for AABB { /// /// [`AABB`]: struct.AABB.html /// -impl Index for AABB { - type Output = Point3; - - fn index(&self, index: usize) -> &Point3 { - if index == 0 { - &self.min - } else { - &self.max - } +impl Index for AABB { + type Output = Point; + + fn index(&self, index: usize) -> &Point { + [&self.min, &self.max][index] } } @@ -625,7 +657,7 @@ impl Index for AABB { /// # Examples /// ``` /// use bvh::aabb::{AABB, Bounded}; -/// use bvh::Point3; +/// use nalgebra::Point3; /// /// let point_a = Point3::new(3.0,4.0,5.0); /// let point_b = Point3::new(17.0,18.0,19.0); @@ -640,8 +672,8 @@ impl Index for AABB { /// [`AABB`]: struct.AABB.html /// [`Bounded`]: trait.Bounded.html /// -impl Bounded for AABB { - fn aabb(&self) -> AABB { +impl Bounded for AABB { + fn aabb(&self) -> AABB { *self } } @@ -651,7 +683,7 @@ impl Bounded for AABB { /// # Examples /// ``` /// use bvh::aabb::{AABB, Bounded}; -/// use bvh::Point3; +/// use nalgebra::Point3; /// /// let point = Point3::new(3.0,4.0,5.0); /// @@ -660,20 +692,20 @@ impl Bounded for AABB { /// ``` /// /// [`Bounded`]: trait.Bounded.html -/// [`Point3`]: glam::Vec3 +/// [`Point3`]: nalgebra::Point3 /// -impl Bounded for Point3 { - fn aabb(&self) -> AABB { +impl Bounded for Point { + fn aabb(&self) -> AABB { AABB::with_bounds(*self, *self) } } #[cfg(test)] mod tests { - use crate::aabb::{Bounded, AABB}; - use crate::testbase::{tuple_to_point, tuple_to_vector, tuplevec_large_strategy, TupleVec}; - use crate::EPSILON; - use crate::{Point3, Vector3}; + use crate::aabb::Bounded; + use crate::testbase::{ + tuple_to_point, tuple_to_vector, tuplevec_large_strategy, Point3, TupleVec, Vector3, AABB, + }; use float_eq::assert_float_eq; use proptest::prelude::*; @@ -776,8 +808,8 @@ mod tests { let outside_ppp = inside_ppp + size_half * 1.1; let outside_mmm = inside_mmm - size_half * 1.1; - assert!(aabb.approx_contains_eps(&inside_ppp, EPSILON)); - assert!(aabb.approx_contains_eps(&inside_mmm, EPSILON)); + assert!(aabb.approx_contains_eps(&inside_ppp, f32::EPSILON)); + assert!(aabb.approx_contains_eps(&inside_mmm, f32::EPSILON)); assert!(!aabb.contains(&outside_ppp)); assert!(!aabb.contains(&outside_mmm)); } @@ -793,7 +825,7 @@ mod tests { // Compute and compare the surface area of an AABB by hand. #[test] - fn test_surface_area_cube(pos: TupleVec, size in EPSILON..10e30_f32) { + fn test_surface_area_cube(pos: TupleVec, size in f32::EPSILON..10e30_f32) { // Generate some non-empty AABB let pos = tuple_to_point(&pos); let size_vec = Vector3::new(size, size, size); @@ -802,7 +834,7 @@ mod tests { // Check its surface area let area_a = aabb.surface_area(); let area_b = 6.0 * size * size; - assert_float_eq!(area_a, area_b, rmax <= EPSILON); + assert_float_eq!(area_a, area_b, rmax <= f32::EPSILON); } // Test whether the volume of a nonempty AABB is always positive. @@ -825,7 +857,7 @@ mod tests { // Check its volume let volume_a = aabb.volume(); let volume_b = (size.x * size.y * size.z).abs(); - assert_float_eq!(volume_a, volume_b, rmax <= EPSILON); + assert_float_eq!(volume_a, volume_b, rmax <= f32::EPSILON); } // Test whether generating an `AABB` from the min and max bounds yields the same `AABB`. diff --git a/src/axis.rs b/src/axis.rs deleted file mode 100644 index 1c57335..0000000 --- a/src/axis.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! Axis enum for indexing three-dimensional structures. - -#![allow(unused)] -use crate::{Point3, Vector3}; -use std::fmt::{Display, Formatter, Result}; -use std::ops::{Index, IndexMut}; - -struct MyType(T); - -/// An `Axis` in a three-dimensional coordinate system. -/// Used to access `Vector3`/`Point3` structs via index. -/// -/// # Examples -/// ``` -/// use bvh::axis::Axis; -/// -/// let mut position = [1.0, 0.5, 42.0]; -/// position[Axis::Y] *= 4.0; -/// -/// assert_eq!(position[Axis::Y], 2.0); -/// ``` -/// -/// [`Point3`] and [`Vector3`] are also indexable using `Axis`. -/// -/// ``` -/// use bvh::axis::Axis; -/// use bvh::Point3; -/// -/// # fn main() { -/// let mut position: Point3 = Point3::new(1.0, 2.0, 3.0); -/// position[Axis::X] = 1000.0; -/// -/// assert_eq!(position[Axis::X], 1000.0); -/// # } -/// ``` -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Axis { - /// Index of the X axis. - X = 0, - - /// Index of the Y axis. - Y = 1, - - /// Index of the Z axis. - Z = 2, -} - -/// Display implementation for `Axis`. -impl Display for Axis { - fn fmt(&self, f: &mut Formatter) -> Result { - write!( - f, - "{}", - match *self { - Axis::X => "x", - Axis::Y => "y", - Axis::Z => "z", - } - ) - } -} - -/// Make slices indexable by `Axis`. -impl Index for [f32] { - type Output = f32; - - fn index(&self, axis: Axis) -> &f32 { - &self[axis as usize] - } -} - -/// Make `Point3` indexable by `Axis`. -impl Index for Point3 { - type Output = f32; - - fn index(&self, axis: Axis) -> &f32 { - match axis { - Axis::X => &self.x, - Axis::Y => &self.y, - Axis::Z => &self.z, - } - } -} - -/// Make `Vector3` indexable by `Axis`. -impl Index for MyType { - type Output = f32; - - fn index(&self, axis: Axis) -> &f32 { - match axis { - Axis::X => &self.0.x, - Axis::Y => &self.0.y, - Axis::Z => &self.0.z, - } - } -} - -/// Make slices mutably accessible by `Axis`. -impl IndexMut for [f32] { - fn index_mut(&mut self, axis: Axis) -> &mut f32 { - &mut self[axis as usize] - } -} - -/// Make `Point3` mutably accessible by `Axis`. -impl IndexMut for Point3 { - fn index_mut(&mut self, axis: Axis) -> &mut f32 { - match axis { - Axis::X => &mut self.x, - Axis::Y => &mut self.y, - Axis::Z => &mut self.z, - } - } -} - -/// Make `Vector3` mutably accessible by `Axis`. -impl IndexMut for MyType { - fn index_mut(&mut self, axis: Axis) -> &mut f32 { - match axis { - Axis::X => &mut self.0.x, - Axis::Y => &mut self.0.y, - Axis::Z => &mut self.0.z, - } - } -} - -#[cfg(test)] -mod test { - use crate::axis::Axis; - use proptest::prelude::*; - - proptest! { - // Test whether accessing arrays by index is the same as accessing them by `Axis`. - #[test] - fn test_index_by_axis(tpl: (f32, f32, f32)) { - let a = [tpl.0, tpl.1, tpl.2]; - - assert!((a[0] - a[Axis::X]).abs() < f32::EPSILON && (a[1] - a[Axis::Y]).abs() < f32::EPSILON && (a[2] - a[Axis::Z]).abs() < f32::EPSILON); - } - - // Test whether arrays can be mutably set, by indexing via `Axis`. - #[test] - fn test_set_by_axis(tpl: (f32, f32, f32)) { - let mut a = [0.0, 0.0, 0.0]; - - a[Axis::X] = tpl.0; - a[Axis::Y] = tpl.1; - a[Axis::Z] = tpl.2; - - assert!((a[0] - tpl.0).abs() < f32::EPSILON && (a[1] - tpl.1).abs() < f32::EPSILON && (a[2] - tpl.2).abs() < f32::EPSILON); - } - } -} diff --git a/src/bounding_hierarchy.rs b/src/bounding_hierarchy.rs index dcc06bc..e23f059 100644 --- a/src/bounding_hierarchy.rs +++ b/src/bounding_hierarchy.rs @@ -1,5 +1,7 @@ //! This module defines the `BoundingHierarchy` trait. +use nalgebra::Scalar; + use crate::aabb::Bounded; use crate::ray::Ray; @@ -9,7 +11,7 @@ use crate::ray::Ray; /// [`BoundingHierarchy`]: struct.BoundingHierarchy.html /// #[allow(clippy::upper_case_acronyms)] -pub trait BHShape: Bounded { +pub trait BHShape: Bounded { /// Sets the index of the referenced [`BoundingHierarchy`] node. /// /// [`BoundingHierarchy`]: struct.BoundingHierarchy.html @@ -23,29 +25,29 @@ pub trait BHShape: Bounded { fn bh_node_index(&self) -> usize; } -impl BHShape for &mut T { +impl> BHShape for &mut S { fn set_bh_node_index(&mut self, idx: usize) { - T::set_bh_node_index(self, idx) + S::set_bh_node_index(self, idx) } fn bh_node_index(&self) -> usize { - T::bh_node_index(self) + S::bh_node_index(self) } } -impl BHShape for Box { +impl> BHShape for Box { fn set_bh_node_index(&mut self, idx: usize) { - T::set_bh_node_index(self, idx) + S::set_bh_node_index(self, idx) } fn bh_node_index(&self) -> usize { - T::bh_node_index(self) + S::bh_node_index(self) } } /// This trait defines an acceleration structure with space partitioning. /// This structure is used to efficiently compute ray-scene intersections. -pub trait BoundingHierarchy { +pub trait BoundingHierarchy { /// Creates a new [`BoundingHierarchy`] from the `shapes` slice. /// /// # Examples @@ -53,16 +55,16 @@ pub trait BoundingHierarchy { /// ``` /// use bvh::aabb::{AABB, Bounded}; /// use bvh::bounding_hierarchy::BoundingHierarchy; - /// use bvh::{Point3, Vector3}; + /// use nalgebra::{Point3, Vector3}; /// # use bvh::bounding_hierarchy::BHShape; /// # pub struct UnitBox { /// # pub id: i32, - /// # pub pos: Point3, + /// # pub pos: Point3, /// # node_index: usize, /// # } /// # /// # impl UnitBox { - /// # pub fn new(id: i32, pos: Point3) -> UnitBox { + /// # pub fn new(id: i32, pos: Point3) -> UnitBox { /// # UnitBox { /// # id: id, /// # pos: pos, @@ -71,15 +73,15 @@ pub trait BoundingHierarchy { /// # } /// # } /// # - /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # impl Bounded for UnitBox { + /// # fn aabb(&self) -> AABB { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); /// # AABB::with_bounds(min, max) /// # } /// # } /// # - /// # impl BHShape for UnitBox { + /// # impl BHShape for UnitBox { /// # fn set_bh_node_index(&mut self, index: usize) { /// # self.node_index = index; /// # } @@ -114,7 +116,7 @@ pub trait BoundingHierarchy { /// /// [`BoundingHierarchy`]: trait.BoundingHierarchy.html /// - fn build(shapes: &mut [Shape]) -> Self; + fn build>(shapes: &mut [Shape]) -> Self; /// Traverses the [`BoundingHierarchy`]. /// Returns a subset of `shapes`, in which the [`AABB`]s of the elements were hit by `ray`. @@ -125,17 +127,17 @@ pub trait BoundingHierarchy { /// use bvh::aabb::{AABB, Bounded}; /// use bvh::bounding_hierarchy::BoundingHierarchy; /// use bvh::bvh::BVH; - /// use bvh::{Point3, Vector3}; + /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; /// # pub struct UnitBox { /// # pub id: i32, - /// # pub pos: Point3, + /// # pub pos: Point3, /// # node_index: usize, /// # } /// # /// # impl UnitBox { - /// # pub fn new(id: i32, pos: Point3) -> UnitBox { + /// # pub fn new(id: i32, pos: Point3) -> UnitBox { /// # UnitBox { /// # id: id, /// # pos: pos, @@ -144,15 +146,15 @@ pub trait BoundingHierarchy { /// # } /// # } /// # - /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # impl Bounded for UnitBox { + /// # fn aabb(&self) -> AABB { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); /// # AABB::with_bounds(min, max) /// # } /// # } /// # - /// # impl BHShape for UnitBox { + /// # impl BHShape for UnitBox { /// # fn set_bh_node_index(&mut self, index: usize) { /// # self.node_index = index; /// # } @@ -162,7 +164,7 @@ pub trait BoundingHierarchy { /// # } /// # } /// # - /// # fn create_bvh() -> (BVH, Vec) { + /// # fn create_bvh() -> (BVH, Vec) { /// # let mut shapes = Vec::new(); /// # for i in 0..1000 { /// # let position = Point3::new(i as f32, i as f32, i as f32); @@ -183,7 +185,11 @@ pub trait BoundingHierarchy { /// [`BoundingHierarchy`]: trait.BoundingHierarchy.html /// [`AABB`]: ../aabb/struct.AABB.html /// - fn traverse<'a, Shape: BHShape>(&'a self, ray: &Ray, shapes: &'a [Shape]) -> Vec<&Shape>; + fn traverse<'a, Shape: BHShape>( + &'a self, + ray: &Ray, + shapes: &'a [Shape], + ) -> Vec<&Shape>; /// Prints the [`BoundingHierarchy`] in a tree-like visualization. /// @@ -192,12 +198,18 @@ pub trait BoundingHierarchy { fn pretty_print(&self) {} } -impl BoundingHierarchy for Box { - fn build(shapes: &mut [Shape]) -> Self { - Box::new(T::build(shapes)) +impl> BoundingHierarchy + for Box +{ + fn build>(shapes: &mut [Shape]) -> Self { + Box::new(H::build(shapes)) } - fn traverse<'a, Shape: BHShape>(&'a self, ray: &Ray, shapes: &'a [Shape]) -> Vec<&Shape> { - T::traverse(self, ray, shapes) + fn traverse<'a, Shape: BHShape>( + &'a self, + ray: &Ray, + shapes: &'a [Shape], + ) -> Vec<&Shape> { + H::traverse(self, ray, shapes) } } diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index 26817c6..9f61535 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -4,14 +4,15 @@ //! [`BVHNode`]: struct.BVHNode.html //! +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::{Float, FromPrimitive, Signed, ToPrimitive, Zero}; + use crate::aabb::{Bounded, AABB}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; +//use crate::bounds::ScalarType; use crate::bvh::iter::BVHTraverseIterator; use crate::ray::Ray; use crate::utils::{concatenate_vectors, joint_aabb_of_shapes, Bucket}; -use crate::Point3; -use crate::EPSILON; -use std::f32; /// The [`BVHNode`] enum that describes a node in a [`BVH`]. /// It's either a leaf node and references a shape (by holding its index) @@ -25,7 +26,7 @@ use std::f32; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(clippy::upper_case_acronyms)] -pub enum BVHNode { +pub enum BVHNode { /// Leaf node. Leaf { /// The node's parent. @@ -49,19 +50,19 @@ pub enum BVHNode { child_l_index: usize, /// The convex hull of the shapes' `AABB`s in child_l. - child_l_aabb: AABB, + child_l_aabb: AABB, /// Index of the right subtree's root node. child_r_index: usize, /// The convex hull of the shapes' `AABB`s in child_r. - child_r_aabb: AABB, + child_r_aabb: AABB, }, } -impl PartialEq for BVHNode { +impl PartialEq for BVHNode { // TODO Consider also comparing AABBs - fn eq(&self, other: &BVHNode) -> bool { + fn eq(&self, other: &BVHNode) -> bool { match (self, other) { ( &BVHNode::Node { @@ -105,7 +106,7 @@ impl PartialEq for BVHNode { } } -impl BVHNode { +impl BVHNode { /// Returns the index of the parent node. pub fn parent(&self) -> usize { match *self { @@ -136,7 +137,7 @@ impl BVHNode { } /// Returns the `AABB` of the right child node. - pub fn child_l_aabb(&self) -> AABB { + pub fn child_l_aabb(&self) -> AABB { match *self { BVHNode::Node { child_l_aabb, .. } => child_l_aabb, _ => panic!(), @@ -144,7 +145,7 @@ impl BVHNode { } /// Returns a mutable reference to the `AABB` of the left child node. - pub fn child_l_aabb_mut(&mut self) -> &mut AABB { + pub fn child_l_aabb_mut(&mut self) -> &mut AABB { match *self { BVHNode::Node { ref mut child_l_aabb, @@ -163,7 +164,7 @@ impl BVHNode { } /// Returns the `AABB` of the right child node. - pub fn child_r_aabb(&self) -> AABB { + pub fn child_r_aabb(&self) -> AABB { match *self { BVHNode::Node { child_r_aabb, .. } => child_r_aabb, _ => panic!(), @@ -171,7 +172,7 @@ impl BVHNode { } /// Returns a mutable reference to the `AABB` of the right child node. - pub fn child_r_aabb_mut(&mut self) -> &mut AABB { + pub fn child_r_aabb_mut(&mut self) -> &mut AABB { match *self { BVHNode::Node { ref mut child_r_aabb, @@ -191,7 +192,10 @@ impl BVHNode { /// Gets the `AABB` for a `BVHNode`. /// Returns the shape's `AABB` for leaves, and the joined `AABB` of /// the two children's `AABB`s for non-leaves. - pub fn get_node_aabb(&self, shapes: &[Shape]) -> AABB { + pub fn get_node_aabb>(&self, shapes: &[Shape]) -> AABB + where + T: SimdPartialOrd, + { match *self { BVHNode::Node { child_l_aabb, @@ -213,7 +217,7 @@ impl BVHNode { /// The build function sometimes needs to add nodes while their data is not available yet. /// A dummy cerated by this function serves the purpose of being changed later on. - fn create_dummy() -> BVHNode { + fn create_dummy() -> BVHNode { BVHNode::Leaf { parent_index: 0, depth: 0, @@ -226,15 +230,31 @@ impl BVHNode { /// /// [`BVHNode`]: enum.BVHNode.html /// - pub fn build( - shapes: &mut [T], + pub fn build>( + shapes: &mut [S], indices: &[usize], - nodes: &mut Vec, + nodes: &mut Vec>, parent_index: usize, depth: u32, - ) -> usize { + ) -> usize + where + T: FromPrimitive + ClosedSub + ClosedAdd + SimdPartialOrd + ClosedMul + Float, + { // Helper function to accumulate the AABB joint and the centroids AABB - fn grow_convex_hull(convex_hull: (AABB, AABB), shape_aabb: &AABB) -> (AABB, AABB) { + fn grow_convex_hull< + T: Scalar + + Copy + + ClosedSub + + ClosedMul + + SimdPartialOrd + + ClosedAdd + + FromPrimitive + + ToPrimitive, + const D: usize, + >( + convex_hull: (AABB, AABB), + shape_aabb: &AABB, + ) -> (AABB, AABB) { let center = &shape_aabb.center(); let convex_hull_aabbs = &convex_hull.0; let convex_hull_centroids = &convex_hull.1; @@ -275,7 +295,7 @@ impl BVHNode { // The following `if` partitions `indices` for recursively calling `BVH::build`. let (child_l_index, child_l_aabb, child_r_index, child_r_aabb) = if split_axis_size - < EPSILON + < T::epsilon() { // In this branch the shapes lie too close together so that splitting them in a // sensible way is not possible. Instead we just split the list of shapes in half. @@ -307,7 +327,11 @@ impl BVHNode { (shape_center[split_axis] - centroid_bounds.min[split_axis]) / split_axis_size; // Convert that to the actual `Bucket` number. - let bucket_num = (bucket_num_relative * (NUM_BUCKETS as f32 - 0.01)) as usize; + // TODO check this for integers + let bucket_num = (bucket_num_relative + * (T::from_usize(NUM_BUCKETS).unwrap() - T::from_f32(0.01).unwrap())) + .to_usize() + .unwrap(); // Extend the selected `Bucket` and add the index to the actual bucket. buckets[bucket_num].add_aabb(&shape_aabb); @@ -316,7 +340,7 @@ impl BVHNode { // Compute the costs for each configuration and select the best configuration. let mut min_bucket = 0; - let mut min_cost = f32::INFINITY; + let mut min_cost = T::infinity(); let mut child_l_aabb = AABB::empty(); let mut child_r_aabb = AABB::empty(); for i in 0..(NUM_BUCKETS - 1) { @@ -324,8 +348,8 @@ impl BVHNode { let child_l = l_buckets.iter().fold(Bucket::empty(), Bucket::join_bucket); let child_r = r_buckets.iter().fold(Bucket::empty(), Bucket::join_bucket); - let cost = (child_l.size as f32 * child_l.aabb.surface_area() - + child_r.size as f32 * child_r.aabb.surface_area()) + let cost = (T::from_usize(child_l.size).unwrap() * child_l.aabb.surface_area() + + T::from_usize(child_r.size).unwrap() * child_r.aabb.surface_area()) / aabb_bounds.surface_area(); if cost < min_cost { min_bucket = i; @@ -371,11 +395,13 @@ impl BVHNode { /// [`Ray`]: ../ray/struct.Ray.html /// pub fn traverse_recursive( - nodes: &[BVHNode], + nodes: &[BVHNode], node_index: usize, - ray: &Ray, + ray: &Ray, indices: &mut Vec, - ) { + ) where + T: PartialOrd + Zero + ClosedSub + ClosedMul + SimdPartialOrd, + { match nodes[node_index] { BVHNode::Node { ref child_l_aabb, @@ -405,20 +431,23 @@ impl BVHNode { #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BVH { +pub struct BVH { /// The list of nodes of the [`BVH`]. /// /// [`BVH`]: struct.BVH.html /// - pub nodes: Vec, + pub nodes: Vec>, } -impl BVH { +impl BVH { /// Creates a new [`BVH`] from the `shapes` slice. /// /// [`BVH`]: struct.BVH.html /// - pub fn build(shapes: &mut [Shape]) -> BVH { + pub fn build>(shapes: &mut [Shape]) -> BVH + where + T: FromPrimitive + ToPrimitive + Float + ClosedSub + ClosedAdd + ClosedMul + SimdPartialOrd, + { let indices = (0..shapes.len()).collect::>(); let expected_node_count = shapes.len() * 2; let mut nodes = Vec::with_capacity(expected_node_count); @@ -432,7 +461,14 @@ impl BVH { /// [`BVH`]: struct.BVH.html /// [`AABB`]: ../aabb/struct.AABB.html /// - pub fn traverse<'a, Shape: Bounded>(&'a self, ray: &Ray, shapes: &'a [Shape]) -> Vec<&Shape> { + pub fn traverse<'a, Shape: Bounded>( + &'a self, + ray: &Ray, + shapes: &'a [Shape], + ) -> Vec<&Shape> + where + T: PartialOrd + Zero + ClosedSub + ClosedMul + SimdPartialOrd, + { let mut indices = Vec::new(); BVHNode::traverse_recursive(&self.nodes, 0, ray, &mut indices); indices @@ -447,11 +483,14 @@ impl BVH { /// [`BVH`]: struct.BVH.html /// [`AABB`]: ../aabb/struct.AABB.html /// - pub fn traverse_iterator<'bvh, 'shape, Shape: Bounded>( + pub fn traverse_iterator<'bvh, 'shape, Shape: Bounded>( &'bvh self, - ray: &'bvh Ray, + ray: &'bvh Ray, shapes: &'shape [Shape], - ) -> BVHTraverseIterator<'bvh, 'shape, Shape> { + ) -> BVHTraverseIterator<'bvh, 'shape, T, D, Shape> + where + T: SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, + { BVHTraverseIterator::new(self, ray, shapes) } @@ -459,9 +498,15 @@ impl BVH { /// /// [`BVH`]: struct.BVH.html /// - pub fn pretty_print(&self) { + pub fn pretty_print(&self) + where + T: std::fmt::Display, + { let nodes = &self.nodes; - fn print_node(nodes: &[BVHNode], node_index: usize) { + fn print_node(nodes: &[BVHNode], node_index: usize) + where + T: std::fmt::Display, + { match nodes[node_index] { BVHNode::Node { child_l_index, @@ -491,15 +536,18 @@ impl BVH { /// Verifies that the node at index `node_index` lies inside `expected_outer_aabb`, /// its parent index is equal to `expected_parent_index`, its depth is equal to /// `expected_depth`. Increares `node_count` by the number of visited nodes. - fn is_consistent_subtree( + fn is_consistent_subtree>( &self, node_index: usize, expected_parent_index: usize, - expected_outer_aabb: &AABB, + expected_outer_aabb: &AABB, expected_depth: u32, node_count: &mut usize, shapes: &[Shape], - ) -> bool { + ) -> bool + where + T: Float + ClosedSub, + { *node_count += 1; match self.nodes[node_index] { BVHNode::Node { @@ -513,9 +561,9 @@ impl BVH { let correct_parent_index = expected_parent_index == parent_index; let correct_depth = expected_depth == depth; let left_aabb_in_parent = - expected_outer_aabb.approx_contains_aabb_eps(&child_l_aabb, EPSILON); + expected_outer_aabb.approx_contains_aabb_eps(&child_l_aabb, T::epsilon()); let right_aabb_in_parent = - expected_outer_aabb.approx_contains_aabb_eps(&child_r_aabb, EPSILON); + expected_outer_aabb.approx_contains_aabb_eps(&child_r_aabb, T::epsilon()); let left_subtree_consistent = self.is_consistent_subtree( child_l_index, node_index, @@ -549,7 +597,7 @@ impl BVH { let correct_depth = expected_depth == depth; let shape_aabb = shapes[shape_index].aabb(); let shape_aabb_in_parent = - expected_outer_aabb.approx_contains_aabb_eps(&shape_aabb, EPSILON); + expected_outer_aabb.approx_contains_aabb_eps(&shape_aabb, T::epsilon()); correct_parent_index && correct_depth && shape_aabb_in_parent } @@ -558,12 +606,12 @@ impl BVH { /// Checks if all children of a node have the correct parent index, and that there is no /// detached subtree. Also checks if the `AABB` hierarchy is consistent. - pub fn is_consistent(&self, shapes: &[Shape]) -> bool { + pub fn is_consistent>(&self, shapes: &[Shape]) -> bool + where + T: Float + ClosedSub, + { // The root node of the bvh is not bounded by anything. - let space = AABB { - min: Point3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY), - max: Point3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY), - }; + let space = AABB::infinite(); // The counter for all nodes. let mut node_count = 0; @@ -577,15 +625,17 @@ impl BVH { } /// Assert version of `is_consistent_subtree`. - fn assert_consistent_subtree( + fn assert_consistent_subtree>( &self, node_index: usize, expected_parent_index: usize, - expected_outer_aabb: &AABB, + expected_outer_aabb: &AABB, expected_depth: u32, node_count: &mut usize, shapes: &[Shape], - ) { + ) where + T: Float + ClosedSub + std::fmt::Display, + { *node_count += 1; let node = &self.nodes[node_index]; @@ -611,7 +661,7 @@ impl BVH { .. } => { assert!( - expected_outer_aabb.approx_contains_aabb_eps(&child_l_aabb, EPSILON), + expected_outer_aabb.approx_contains_aabb_eps(&child_l_aabb, T::epsilon()), "Left child lies outside the expected bounds. \tBounds: {} \tLeft child: {}", @@ -619,7 +669,7 @@ impl BVH { child_l_aabb ); assert!( - expected_outer_aabb.approx_contains_aabb_eps(&child_r_aabb, EPSILON), + expected_outer_aabb.approx_contains_aabb_eps(&child_r_aabb, T::epsilon()), "Right child lies outside the expected bounds. \tBounds: {} \tRight child: {}", @@ -646,7 +696,7 @@ impl BVH { BVHNode::Leaf { shape_index, .. } => { let shape_aabb = shapes[shape_index].aabb(); assert!( - expected_outer_aabb.approx_contains_aabb_eps(&shape_aabb, EPSILON), + expected_outer_aabb.approx_contains_aabb_eps(&shape_aabb, T::epsilon()), "Shape's AABB lies outside the expected bounds.\n\tBounds: {}\n\tShape: {}", expected_outer_aabb, shape_aabb @@ -656,12 +706,12 @@ impl BVH { } /// Assert version of `is_consistent`. - pub fn assert_consistent(&self, shapes: &[Shape]) { + pub fn assert_consistent>(&self, shapes: &[Shape]) + where + T: Float + ClosedSub + std::fmt::Display, + { // The root node of the bvh is not bounded by anything. - let space = AABB { - min: Point3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY), - max: Point3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY), - }; + let space = AABB::infinite(); // The counter for all nodes. let mut node_count = 0; @@ -675,7 +725,10 @@ impl BVH { /// Check that the `AABB`s in the `BVH` are tight, which means, that parent `AABB`s are not /// larger than they should be. This function checks, whether the children of node `node_index` /// lie inside `outer_aabb`. - pub fn assert_tight_subtree(&self, node_index: usize, outer_aabb: &AABB) { + pub fn assert_tight_subtree(&self, node_index: usize, outer_aabb: &AABB) + where + T: Float + SimdPartialOrd + ClosedSub + Signed, + { if let BVHNode::Node { child_l_index, child_l_aabb, @@ -685,7 +738,7 @@ impl BVH { } = self.nodes[node_index] { let joint_aabb = child_l_aabb.join(&child_r_aabb); - assert!(joint_aabb.relative_eq(outer_aabb, EPSILON)); + assert!(joint_aabb.relative_eq(outer_aabb, T::epsilon())); self.assert_tight_subtree(child_l_index, &child_l_aabb); self.assert_tight_subtree(child_r_index, &child_r_aabb); } @@ -693,7 +746,10 @@ impl BVH { /// Check that the `AABB`s in the `BVH` are tight, which means, that parent `AABB`s are not /// larger than they should be. - pub fn assert_tight(&self) { + pub fn assert_tight(&self) + where + T: SimdPartialOrd + Float + ClosedSub + Signed, + { // When starting to check whether the `BVH` is tight, we cannot provide a minimum // outer `AABB`, therefore we compute the correct one in this instance. if let BVHNode::Node { @@ -708,12 +764,28 @@ impl BVH { } } -impl BoundingHierarchy for BVH { - fn build(shapes: &mut [Shape]) -> BVH { +impl BoundingHierarchy for BVH +where + T: Scalar + + Copy + + FromPrimitive + + ToPrimitive + + Float + + ClosedSub + + ClosedAdd + + ClosedMul + + SimdPartialOrd + + std::fmt::Display, +{ + fn build>(shapes: &mut [Shape]) -> BVH { BVH::build(shapes) } - fn traverse<'a, Shape: Bounded>(&'a self, ray: &Ray, shapes: &'a [Shape]) -> Vec<&Shape> { + fn traverse<'a, Shape: Bounded>( + &'a self, + ray: &Ray, + shapes: &'a [Shape], + ) -> Vec<&Shape> { self.traverse(ray, shapes) } @@ -724,8 +796,7 @@ impl BoundingHierarchy for BVH { #[cfg(test)] mod tests { - use crate::bvh::{BVHNode, BVH}; - use crate::testbase::{build_some_bh, traverse_some_bh}; + use crate::testbase::{build_some_bh, traverse_some_bh, BVHNode, BVH}; #[test] /// Tests whether the building procedure succeeds in not failing. @@ -770,11 +841,10 @@ mod tests { #[cfg(all(feature = "bench", test))] mod bench { - use crate::bvh::BVH; use crate::testbase::{ build_1200_triangles_bh, build_120k_triangles_bh, build_12k_triangles_bh, intersect_1200_triangles_bh, intersect_120k_triangles_bh, intersect_12k_triangles_bh, - intersect_bh, load_sponza_scene, + intersect_bh, load_sponza_scene, BVH, }; #[bench] diff --git a/src/bvh/iter.rs b/src/bvh/iter.rs index e088dce..42dc20e 100644 --- a/src/bvh/iter.rs +++ b/src/bvh/iter.rs @@ -1,14 +1,18 @@ +use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::Zero; + use crate::aabb::Bounded; use crate::bvh::{BVHNode, BVH}; use crate::ray::Ray; /// Iterator to traverse a [`BVH`] without memory allocations #[allow(clippy::upper_case_acronyms)] -pub struct BVHTraverseIterator<'bvh, 'shape, Shape: Bounded> { +pub struct BVHTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, Shape: Bounded> +{ /// Reference to the BVH to traverse - bvh: &'bvh BVH, + bvh: &'bvh BVH, /// Reference to the input ray - ray: &'bvh Ray, + ray: &'bvh Ray, /// Reference to the input shapes array shapes: &'shape [Shape], /// Traversal stack. 4 billion items seems enough? @@ -21,9 +25,13 @@ pub struct BVHTraverseIterator<'bvh, 'shape, Shape: Bounded> { has_node: bool, } -impl<'bvh, 'shape, Shape: Bounded> BVHTraverseIterator<'bvh, 'shape, Shape> { +impl<'bvh, 'shape, T, const D: usize, Shape: Bounded> + BVHTraverseIterator<'bvh, 'shape, T, D, Shape> +where + T: Scalar + Copy + SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, +{ /// Creates a new `BVHTraverseIterator` - pub fn new(bvh: &'bvh BVH, ray: &'bvh Ray, shapes: &'shape [Shape]) -> Self { + pub fn new(bvh: &'bvh BVH, ray: &'bvh Ray, shapes: &'shape [Shape]) -> Self { BVHTraverseIterator { bvh, ray, @@ -105,7 +113,11 @@ impl<'bvh, 'shape, Shape: Bounded> BVHTraverseIterator<'bvh, 'shape, Shape> { } } -impl<'bvh, 'shape, Shape: Bounded> Iterator for BVHTraverseIterator<'bvh, 'shape, Shape> { +impl<'bvh, 'shape, T, const D: usize, Shape: Bounded> Iterator + for BVHTraverseIterator<'bvh, 'shape, T, D, Shape> +where + T: Scalar + Copy + SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, +{ type Item = &'shape Shape; fn next(&mut self) -> Option<&'shape Shape> { @@ -143,10 +155,8 @@ impl<'bvh, 'shape, Shape: Bounded> Iterator for BVHTraverseIterator<'bvh, 'shape // TODO: Once iterators are part of the BoundingHierarchy trait we can move all this to testbase. #[cfg(test)] mod tests { - use crate::bvh::BVH; use crate::ray::Ray; - use crate::testbase::{generate_aligned_boxes, UnitBox}; - use crate::{Point3, Vector3}; + use crate::testbase::{generate_aligned_boxes, Point3, UnitBox, Vector3, BVH}; use std::collections::HashSet; /// Creates a `BVH` for a fixed scene structure. @@ -254,8 +264,7 @@ mod tests { #[cfg(all(feature = "bench", test))] mod bench { - use crate::bvh::BVH; - use crate::testbase::{create_ray, load_sponza_scene}; + use crate::testbase::{create_ray, load_sponza_scene, BVH}; #[bench] /// Benchmark the traversal of a `BVH` with the Sponza scene with Vec return. diff --git a/src/bvh/optimization.rs b/src/bvh/optimization.rs index 66548ae..0de879e 100644 --- a/src/bvh/optimization.rs +++ b/src/bvh/optimization.rs @@ -11,6 +11,8 @@ use crate::bounding_hierarchy::BHShape; use crate::bvh::*; use log::info; +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::{FromPrimitive, Zero}; use rand::{thread_rng, Rng}; use std::collections::HashSet; @@ -35,14 +37,14 @@ impl OptimizationIndex { } #[derive(Debug, Copy, Clone)] -struct NodeData { +struct NodeData { index: usize, - aabb: AABB, + aabb: AABB, } -impl BVHNode { +impl BVHNode { // Get the grandchildren's NodeData. - fn get_children_node_data(&self) -> Option<(NodeData, NodeData)> { + fn get_children_node_data(&self) -> Option<(NodeData, NodeData)> { match *self { BVHNode::Node { child_l_index, @@ -65,14 +67,26 @@ impl BVHNode { } } -impl BVH { +impl BVH +where + T: Scalar + + Copy + + FromPrimitive + + ClosedSub + + ClosedMul + + ClosedAdd + + Zero + + SimdPartialOrd + + PartialOrd + + std::fmt::Display, +{ /// Optimizes the `BVH` by batch-reorganizing updated nodes. /// Based on /// [`https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs`] /// /// Needs all the scene's shapes, plus the indices of the shapes that were updated. /// - pub fn optimize( + pub fn optimize>( &mut self, refit_shape_indices: &HashSet, shapes: &[Shape], @@ -154,7 +168,7 @@ impl BVH { /// This method is called for each node which has been modified and needs to be updated. /// If the specified node is a grandparent, then try to optimize the `BVH` by rotating its /// children. - fn update( + fn update>( &mut self, node_index: usize, shapes: &[Shape], @@ -219,9 +233,9 @@ impl BVH { fn find_better_rotation( &self, child_l_index: usize, - child_l_aabb: &AABB, + child_l_aabb: &AABB, child_r_index: usize, - child_r_aabb: &AABB, + child_r_aabb: &AABB, ) -> Option<(usize, usize)> { // Get indices and `AABB`s of all grandchildren. let left_children_nodes = self.nodes[child_l_index].get_children_node_data(); @@ -236,7 +250,7 @@ impl BVH { // thus being the favored rotation that will be executed after considering all rotations. let mut best_rotation: Option<(usize, usize)> = None; { - let mut consider_rotation = |new_rotation: (usize, usize), surface_area: f32| { + let mut consider_rotation = |new_rotation: (usize, usize), surface_area: T| { if surface_area < best_surface_area { best_surface_area = surface_area; best_rotation = Some(new_rotation); @@ -286,7 +300,7 @@ impl BVH { /// /// `Some(index_of_node)` if a new node was found that should be used for optimization. /// - fn try_rotate( + fn try_rotate>( &mut self, node_index: usize, shapes: &[Shape], @@ -352,7 +366,11 @@ impl BVH { /// Sets child_l_aabb and child_r_aabb of a BVHNode::Node to match its children, /// right after updating the children themselves. Not recursive. - fn fix_children_and_own_aabbs(&mut self, node_index: usize, shapes: &[Shape]) { + fn fix_children_and_own_aabbs>( + &mut self, + node_index: usize, + shapes: &[Shape], + ) { let (child_l_index, child_r_index) = if let BVHNode::Node { child_l_index, child_r_index, @@ -375,7 +393,7 @@ impl BVH { /// Updates `child_l_aabb` and `child_r_aabb` of the `BVHNode::Node` /// with the index `node_index` from its children. - fn fix_aabbs( + fn fix_aabbs>( &mut self, node_index: usize, shapes: &[Shape], @@ -405,7 +423,7 @@ impl BVH { /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice). /// Also updates the AABBs of the parents. - fn rotate( + fn rotate>( &mut self, node_a_index: usize, node_b_index: usize, @@ -470,7 +488,7 @@ impl BVH { child_l_index == node_index } - fn connect_nodes( + fn connect_nodes>( &mut self, child_index: usize, parent_index: usize, @@ -517,12 +535,10 @@ impl BVH { mod tests { use crate::aabb::Bounded; use crate::bounding_hierarchy::BHShape; - use crate::bvh::{BVHNode, BVH}; use crate::testbase::{ - build_some_bh, create_n_cubes, default_bounds, randomly_transform_scene, UnitBox, + build_some_bh, create_n_cubes, default_bounds, randomly_transform_scene, BVHNode, Point3, + UnitBox, BVH, }; - use crate::Point3; - use crate::EPSILON; use std::collections::HashSet; #[test] @@ -719,16 +735,16 @@ mod tests { assert!(nodes[1] .child_l_aabb() - .relative_eq(&shapes[2].aabb(), EPSILON)); + .relative_eq(&shapes[2].aabb(), f32::EPSILON)); assert!(nodes[1] .child_r_aabb() - .relative_eq(&shapes[1].aabb(), EPSILON)); + .relative_eq(&shapes[1].aabb(), f32::EPSILON)); assert!(nodes[2] .child_l_aabb() - .relative_eq(&shapes[0].aabb(), EPSILON)); + .relative_eq(&shapes[0].aabb(), f32::EPSILON)); assert!(nodes[2] .child_r_aabb() - .relative_eq(&shapes[3].aabb(), EPSILON)); + .relative_eq(&shapes[3].aabb(), f32::EPSILON)); } #[test] @@ -761,16 +777,16 @@ mod tests { assert!(nodes[0] .child_l_aabb() - .relative_eq(&shapes[2].aabb(), EPSILON)); + .relative_eq(&shapes[2].aabb(), f32::EPSILON)); assert!(nodes[2] .child_r_aabb() - .relative_eq(&shapes[3].aabb(), EPSILON)); + .relative_eq(&shapes[3].aabb(), f32::EPSILON)); assert!(nodes[1] .child_l_aabb() - .relative_eq(&shapes[0].aabb(), EPSILON)); + .relative_eq(&shapes[0].aabb(), f32::EPSILON)); assert!(nodes[1] .child_r_aabb() - .relative_eq(&shapes[1].aabb(), EPSILON)); + .relative_eq(&shapes[1].aabb(), f32::EPSILON)); } #[test] @@ -802,16 +818,16 @@ mod tests { assert!(nodes[1] .child_l_aabb() - .relative_eq(&shapes[2].aabb(), EPSILON)); + .relative_eq(&shapes[2].aabb(), f32::EPSILON)); assert!(nodes[1] .child_r_aabb() - .relative_eq(&shapes[1].aabb(), EPSILON)); + .relative_eq(&shapes[1].aabb(), f32::EPSILON)); assert!(nodes[2] .child_l_aabb() - .relative_eq(&shapes[0].aabb(), EPSILON)); + .relative_eq(&shapes[0].aabb(), f32::EPSILON)); assert!(nodes[2] .child_r_aabb() - .relative_eq(&shapes[3].aabb(), EPSILON)); + .relative_eq(&shapes[3].aabb(), f32::EPSILON)); } #[test] @@ -843,16 +859,16 @@ mod tests { assert!(nodes[0] .child_l_aabb() - .relative_eq(&shapes[2].aabb(), EPSILON)); + .relative_eq(&shapes[2].aabb(), f32::EPSILON)); assert!(nodes[2] .child_r_aabb() - .relative_eq(&shapes[3].aabb(), EPSILON)); + .relative_eq(&shapes[3].aabb(), f32::EPSILON)); assert!(nodes[1] .child_l_aabb() - .relative_eq(&shapes[0].aabb(), EPSILON)); + .relative_eq(&shapes[0].aabb(), f32::EPSILON)); assert!(nodes[1] .child_r_aabb() - .relative_eq(&shapes[1].aabb(), EPSILON)); + .relative_eq(&shapes[1].aabb(), f32::EPSILON)); } #[test] @@ -890,24 +906,24 @@ mod tests { assert!(nodes[0] .child_l_aabb() - .relative_eq(&shapes[2].aabb(), EPSILON)); + .relative_eq(&shapes[2].aabb(), f32::EPSILON)); let right_subtree_aabb = &shapes[0] .aabb() .join(&shapes[1].aabb()) .join(&shapes[3].aabb()); assert!(nodes[0] .child_r_aabb() - .relative_eq(right_subtree_aabb, EPSILON)); + .relative_eq(right_subtree_aabb, f32::EPSILON)); assert!(nodes[2] .child_r_aabb() - .relative_eq(&shapes[3].aabb(), EPSILON)); + .relative_eq(&shapes[3].aabb(), f32::EPSILON)); assert!(nodes[1] .child_l_aabb() - .relative_eq(&shapes[0].aabb(), EPSILON)); + .relative_eq(&shapes[0].aabb(), f32::EPSILON)); assert!(nodes[1] .child_r_aabb() - .relative_eq(&shapes[1].aabb(), EPSILON)); + .relative_eq(&shapes[1].aabb(), f32::EPSILON)); } #[test] @@ -938,11 +954,9 @@ mod tests { #[cfg(all(feature = "bench", test))] mod bench { - use crate::aabb::AABB; - use crate::bvh::BVH; use crate::testbase::{ create_n_cubes, default_bounds, intersect_bh, load_sponza_scene, randomly_transform_scene, - Triangle, + Triangle, AABB, BVH, }; #[bench] diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index e42cab7..102b009 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -1,4 +1,6 @@ //! This module exports methods to flatten the `BVH` and traverse it iteratively. +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::{Float, FromPrimitive, ToPrimitive}; use crate::aabb::{Bounded, AABB}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; @@ -10,7 +12,7 @@ use crate::ray::Ray; /// /// [`BVH`]: ../bvh/struct.BVH.html /// -pub struct FlatNode { +pub struct FlatNode { /// The [`AABB`] of the [`BVH`] node. Prior to testing the [`AABB`] bounds, /// the `entry_index` must be checked. In case the entry_index is [`u32::max_value()`], /// the [`AABB`] is undefined. @@ -19,7 +21,7 @@ pub struct FlatNode { /// [`BVH`]: ../bvh/struct.BVH.html /// [`u32::max_value()`]: https://doc.rust-lang.org/std/u32/constant.MAX.html /// - pub aabb: AABB, + pub aabb: AABB, /// The index of the `FlatNode` to jump to, if the [`AABB`] test is positive. /// If this value is [`u32::max_value()`] then the current node is a leaf node. @@ -41,20 +43,20 @@ pub struct FlatNode { pub shape_index: u32, } -impl BVHNode { +impl BVHNode { /// Creates a flat node from a `BVH` inner node and its `AABB`. Returns the next free index. /// TODO: change the algorithm which pushes `FlatNode`s to a vector to not use indices this /// much. Implement an algorithm which writes directly to a writable slice. fn create_flat_branch( &self, - nodes: &[BVHNode], - this_aabb: &AABB, + nodes: &[BVHNode], + this_aabb: &AABB, vec: &mut Vec, next_free: usize, constructor: &F, ) -> usize where - F: Fn(&AABB, u32, u32, u32) -> FNodeType, + F: Fn(&AABB, u32, u32, u32) -> FNodeType, { // Create dummy node. let dummy = constructor(&AABB::empty(), 0, 0, 0); @@ -83,13 +85,13 @@ impl BVHNode { /// pub fn flatten_custom( &self, - nodes: &[BVHNode], + nodes: &[BVHNode], vec: &mut Vec, next_free: usize, constructor: &F, ) -> usize where - F: Fn(&AABB, u32, u32, u32) -> FNodeType, + F: Fn(&AABB, u32, u32, u32) -> FNodeType, { match *self { BVHNode::Node { @@ -139,9 +141,9 @@ impl BVHNode { /// [`FlatBVH`]: struct.FlatBVH.html /// #[allow(clippy::upper_case_acronyms)] -pub type FlatBVH = Vec; +pub type FlatBVH = Vec>; -impl BVH { +impl BVH { /// Flattens the [`BVH`] so that it can be traversed iteratively. /// Constructs the flat nodes using the supplied function. /// This function can be used, when the flat bvh nodes should be of some particular @@ -160,17 +162,17 @@ impl BVH { /// ``` /// use bvh::aabb::{AABB, Bounded}; /// use bvh::bvh::BVH; - /// use bvh::{Point3, Vector3}; + /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; /// # pub struct UnitBox { /// # pub id: i32, - /// # pub pos: Point3, + /// # pub pos: Point3, /// # node_index: usize, /// # } /// # /// # impl UnitBox { - /// # pub fn new(id: i32, pos: Point3) -> UnitBox { + /// # pub fn new(id: i32, pos: Point3) -> UnitBox { /// # UnitBox { /// # id: id, /// # pos: pos, @@ -179,15 +181,15 @@ impl BVH { /// # } /// # } /// # - /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # impl Bounded for UnitBox { + /// # fn aabb(&self) -> AABB { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); /// # AABB::with_bounds(min, max) /// # } /// # } /// # - /// # impl BHShape for UnitBox { + /// # impl BHShape for UnitBox { /// # fn set_bh_node_index(&mut self, index: usize) { /// # self.node_index = index; /// # } @@ -207,13 +209,13 @@ impl BVH { /// # } /// /// struct CustomStruct { - /// aabb: AABB, + /// aabb: AABB, /// entry_index: u32, /// exit_index: u32, /// shape_index: u32, /// } /// - /// let custom_constructor = |aabb: &AABB, entry, exit, shape_index| { + /// let custom_constructor = |aabb: &AABB, entry, exit, shape_index| { /// CustomStruct { /// aabb: *aabb, /// entry_index: entry, @@ -228,7 +230,7 @@ impl BVH { /// ``` pub fn flatten_custom(&self, constructor: &F) -> Vec where - F: Fn(&AABB, u32, u32, u32) -> FNodeType, + F: Fn(&AABB, u32, u32, u32) -> FNodeType, { let mut vec = Vec::new(); self.nodes[0].flatten_custom(&self.nodes, &mut vec, 0, constructor); @@ -244,17 +246,17 @@ impl BVH { /// ``` /// use bvh::aabb::{AABB, Bounded}; /// use bvh::bvh::BVH; - /// use bvh::{Point3, Vector3}; + /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; /// # pub struct UnitBox { /// # pub id: i32, - /// # pub pos: Point3, + /// # pub pos: Point3, /// # node_index: usize, /// # } /// # /// # impl UnitBox { - /// # pub fn new(id: i32, pos: Point3) -> UnitBox { + /// # pub fn new(id: i32, pos: Point3) -> UnitBox { /// # UnitBox { /// # id: id, /// # pos: pos, @@ -263,15 +265,15 @@ impl BVH { /// # } /// # } /// # - /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # impl Bounded for UnitBox { + /// # fn aabb(&self) -> AABB { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); /// # AABB::with_bounds(min, max) /// # } /// # } /// # - /// # impl BHShape for UnitBox { + /// # impl BHShape for UnitBox { /// # fn set_bh_node_index(&mut self, index: usize) { /// # self.node_index = index; /// # } @@ -294,7 +296,7 @@ impl BVH { /// let bvh = BVH::build(&mut shapes); /// let flat_bvh = bvh.flatten(); /// ``` - pub fn flatten(&self) -> FlatBVH { + pub fn flatten(&self) -> FlatBVH { self.flatten_custom(&|aabb, entry, exit, shape| FlatNode { aabb: *aabb, entry_index: entry, @@ -304,13 +306,24 @@ impl BVH { } } -impl BoundingHierarchy for FlatBVH { +impl BoundingHierarchy for FlatBVH +where + T: Scalar + + Copy + + FromPrimitive + + ToPrimitive + + Float + + ClosedSub + + ClosedAdd + + ClosedMul + + SimdPartialOrd, +{ /// A [`FlatBVH`] is built from a regular [`BVH`] using the [`BVH::flatten`] method. /// /// [`FlatBVH`]: struct.FlatBVH.html /// [`BVH`]: ../bvh/struct.BVH.html /// - fn build(shapes: &mut [T]) -> FlatBVH { + fn build>(shapes: &mut [Shape]) -> FlatBVH { let bvh = BVH::build(shapes); bvh.flatten() } @@ -325,17 +338,17 @@ impl BoundingHierarchy for FlatBVH { /// use bvh::aabb::{AABB, Bounded}; /// use bvh::bounding_hierarchy::BoundingHierarchy; /// use bvh::flat_bvh::FlatBVH; - /// use bvh::{Point3, Vector3}; + /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; /// # pub struct UnitBox { /// # pub id: i32, - /// # pub pos: Point3, + /// # pub pos: Point3, /// # node_index: usize, /// # } /// # /// # impl UnitBox { - /// # pub fn new(id: i32, pos: Point3) -> UnitBox { + /// # pub fn new(id: i32, pos: Point3) -> UnitBox { /// # UnitBox { /// # id: id, /// # pos: pos, @@ -344,15 +357,15 @@ impl BoundingHierarchy for FlatBVH { /// # } /// # } /// # - /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # impl Bounded for UnitBox { + /// # fn aabb(&self) -> AABB { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); /// # AABB::with_bounds(min, max) /// # } /// # } /// # - /// # impl BHShape for UnitBox { + /// # impl BHShape for UnitBox { /// # fn set_bh_node_index(&mut self, index: usize) { /// # self.node_index = index; /// # } @@ -378,7 +391,7 @@ impl BoundingHierarchy for FlatBVH { /// let flat_bvh = FlatBVH::build(&mut shapes); /// let hit_shapes = flat_bvh.traverse(&ray, &shapes); /// ``` - fn traverse<'a, T: Bounded>(&'a self, ray: &Ray, shapes: &'a [T]) -> Vec<&T> { + fn traverse<'a, B: Bounded>(&'a self, ray: &Ray, shapes: &'a [B]) -> Vec<&B> { let mut hit_shapes = Vec::new(); let mut index = 0; @@ -428,8 +441,7 @@ impl BoundingHierarchy for FlatBVH { #[cfg(test)] mod tests { - use crate::flat_bvh::FlatBVH; - use crate::testbase::{build_some_bh, traverse_some_bh}; + use crate::testbase::{build_some_bh, traverse_some_bh, FlatBVH}; #[test] /// Tests whether the building procedure succeeds in not failing. @@ -447,13 +459,10 @@ mod tests { #[cfg(all(feature = "bench", test))] mod bench { - use crate::bvh::BVH; - use crate::flat_bvh::FlatBVH; - use crate::testbase::{ build_1200_triangles_bh, build_120k_triangles_bh, build_12k_triangles_bh, create_n_cubes, default_bounds, intersect_1200_triangles_bh, intersect_120k_triangles_bh, - intersect_12k_triangles_bh, + intersect_12k_triangles_bh, BVH, FlatBVH }; #[bench] diff --git a/src/lib.rs b/src/lib.rs index 00fae1d..db8847b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,7 +17,7 @@ //! use bvh::aabb::{AABB, Bounded}; //! use bvh::bounding_hierarchy::BHShape; //! use bvh::bvh::BVH; -//! use bvh::{Point3, Vector3}; +//! use nalgebra::{Point3, Vector3}; //! use bvh::ray::Ray; //! //! let origin = Point3::new(0.0,0.0,0.0); @@ -25,13 +25,13 @@ //! let ray = Ray::new(origin, direction); //! //! struct Sphere { -//! position: Point3, +//! position: Point3, //! radius: f32, //! node_index: usize, //! } //! -//! impl Bounded for Sphere { -//! fn aabb(&self) -> AABB { +//! impl Bounded for Sphere { +//! fn aabb(&self) -> AABB { //! let half_size = Vector3::new(self.radius, self.radius, self.radius); //! let min = self.position - half_size; //! let max = self.position + half_size; @@ -39,7 +39,7 @@ //! } //! } //! -//! impl BHShape for Sphere { +//! impl BHShape for Sphere { //! fn set_bh_node_index(&mut self, index: usize) { //! self.node_index = index; //! } @@ -75,18 +75,7 @@ #[cfg(all(feature = "bench", test))] extern crate test; -/// A minimal floating value used as a lower bound. -/// TODO: replace by/add ULPS/relative float comparison methods. -pub const EPSILON: f32 = 0.00001; - -/// Point math type used by this crate. Type alias for [`glam::Vec3`]. -pub type Point3 = glam::Vec3; - -/// Vector math type used by this crate. Type alias for [`glam::Vec3`]. -pub type Vector3 = glam::Vec3; - pub mod aabb; -pub mod axis; pub mod bounding_hierarchy; pub mod bvh; pub mod flat_bvh; diff --git a/src/ray.rs b/src/ray.rs index 6f0927c..44a6784 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -2,63 +2,43 @@ //! for axis aligned bounding boxes and triangles. use crate::aabb::AABB; -use crate::EPSILON; -use crate::{Point3, Vector3}; -use std::f32::INFINITY; +use nalgebra::{ + ClosedAdd, ClosedMul, ClosedSub, ComplexField, Point, SVector, Scalar, SimdPartialOrd, +}; +use num::{Float, One, Zero}; /// A struct which defines a ray and some of its cached values. -#[derive(Debug)] -pub struct Ray { +#[derive(Debug, Clone, Copy)] +pub struct Ray { /// The ray origin. - pub origin: Point3, + pub origin: Point, /// The ray direction. - pub direction: Vector3, + pub direction: SVector, /// Inverse (1/x) ray direction. Cached for use in [`AABB`] intersections. /// /// [`AABB`]: struct.AABB.html /// - inv_direction: Vector3, - - /// Sign of the X direction. 0 means positive, 1 means negative. - /// Cached for use in [`AABB`] intersections. - /// - /// [`AABB`]: struct.AABB.html - /// - sign_x: usize, - - /// Sign of the Y direction. 0 means positive, 1 means negative. - /// Cached for use in [`AABB`] intersections. - /// - /// [`AABB`]: struct.AABB.html - /// - sign_y: usize, - - /// Sign of the Z direction. 0 means positive, 1 means negative. - /// Cached for use in [`AABB`] intersections. - /// - /// [`AABB`]: struct.AABB.html - /// - sign_z: usize, + inv_direction: SVector, } /// A struct which is returned by the `intersects_triangle` method. -pub struct Intersection { +pub struct Intersection { /// Distance from the ray origin to the intersection point. - pub distance: f32, + pub distance: T, /// U coordinate of the intersection. - pub u: f32, + pub u: T, /// V coordinate of the intersection. - pub v: f32, + pub v: T, } -impl Intersection { +impl Intersection { /// Constructs an `Intersection`. `distance` should be set to positive infinity, /// if the intersection does not occur. - pub fn new(distance: f32, u: f32, v: f32) -> Intersection { + pub fn new(distance: T, u: T, v: T) -> Intersection { Intersection { distance, u, v } } } @@ -79,7 +59,8 @@ impl Intersection { /// min( 1.0, NaN): NaN /// ``` #[inline(always)] -fn min(x: f32, y: f32) -> f32 { +#[allow(dead_code)] +fn min(x: T, y: T) -> T { if x < y { x } else { @@ -103,7 +84,8 @@ fn min(x: f32, y: f32) -> f32 { /// max( 1.0, NaN): NaN /// ``` #[inline(always)] -fn max(x: f32, y: f32) -> f32 { +#[allow(dead_code)] +fn max(x: T, y: T) -> T { if x > y { x } else { @@ -111,14 +93,14 @@ fn max(x: f32, y: f32) -> f32 { } } -impl Ray { +impl Ray { /// Creates a new [`Ray`] from an `origin` and a `direction`. /// `direction` will be normalized. /// /// # Examples /// ``` /// use bvh::ray::Ray; - /// use bvh::{Point3,Vector3}; + /// use nalgebra::{Point3,Vector3}; /// /// let origin = Point3::new(0.0,0.0,0.0); /// let direction = Vector3::new(1.0,0.0,0.0); @@ -130,16 +112,24 @@ impl Ray { /// /// [`Ray`]: struct.Ray.html /// - pub fn new(origin: Point3, direction: Vector3) -> Ray { + pub fn new(origin: Point, direction: SVector) -> Ray + where + T: One + ComplexField, + { let direction = direction.normalize(); Ray { origin, direction, - inv_direction: Vector3::new(1.0 / direction.x, 1.0 / direction.y, 1.0 / direction.z), - sign_x: (direction.x < 0.0) as usize, - sign_y: (direction.y < 0.0) as usize, - sign_z: (direction.z < 0.0) as usize, + inv_direction: direction.map(|x| T::one() / x), } + + // println!("dir: {}, inv_dir: {}", r.direction, r.inv_direction); + // assert_eq!( + // r.direction.component_mul(&r.inv_direction), + // SVector::::from_element(T::one()) + // ); + + // r } /// Tests the intersection of a [`Ray`] with an [`AABB`] using the optimized algorithm @@ -149,7 +139,7 @@ impl Ray { /// ``` /// use bvh::aabb::AABB; /// use bvh::ray::Ray; - /// use bvh::{Point3,Vector3}; + /// use nalgebra::{Point3,Vector3}; /// /// let origin = Point3::new(0.0,0.0,0.0); /// let direction = Vector3::new(1.0,0.0,0.0); @@ -165,118 +155,19 @@ impl Ray { /// [`Ray`]: struct.Ray.html /// [`AABB`]: struct.AABB.html /// - pub fn intersects_aabb(&self, aabb: &AABB) -> bool { - let mut ray_min = (aabb[self.sign_x].x - self.origin.x) * self.inv_direction.x; - let mut ray_max = (aabb[1 - self.sign_x].x - self.origin.x) * self.inv_direction.x; - - let y_min = (aabb[self.sign_y].y - self.origin.y) * self.inv_direction.y; - let y_max = (aabb[1 - self.sign_y].y - self.origin.y) * self.inv_direction.y; - - // Using the following solution significantly decreases the performance - // ray_min = ray_min.max(y_min); - // ray_max = ray_max.min(y_max); - ray_min = max(ray_min, y_min); - ray_max = min(ray_max, y_max); - - let z_min = (aabb[self.sign_z].z - self.origin.z) * self.inv_direction.z; - let z_max = (aabb[1 - self.sign_z].z - self.origin.z) * self.inv_direction.z; - - ray_min = max(ray_min, z_min); - ray_max = min(ray_max, z_max); - - max(ray_min, 0.0) <= ray_max - } - - /// Naive implementation of a [`Ray`]/[`AABB`] intersection algorithm. - /// - /// # Examples - /// ``` - /// use bvh::aabb::AABB; - /// use bvh::ray::Ray; - /// use bvh::{Point3,Vector3}; - /// - /// let origin = Point3::new(0.0,0.0,0.0); - /// let direction = Vector3::new(1.0,0.0,0.0); - /// let ray = Ray::new(origin, direction); - /// - /// let point1 = Point3::new(99.9,-1.0,-1.0); - /// let point2 = Point3::new(100.1,1.0,1.0); - /// let aabb = AABB::with_bounds(point1, point2); - /// - /// assert!(ray.intersects_aabb_naive(&aabb)); - /// ``` - /// - /// [`Ray`]: struct.Ray.html - /// [`AABB`]: struct.AABB.html - /// - pub fn intersects_aabb_naive(&self, aabb: &AABB) -> bool { - let hit_min_x = (aabb.min.x - self.origin.x) * self.inv_direction.x; - let hit_max_x = (aabb.max.x - self.origin.x) * self.inv_direction.x; - - let hit_min_y = (aabb.min.y - self.origin.y) * self.inv_direction.y; - let hit_max_y = (aabb.max.y - self.origin.y) * self.inv_direction.y; - - let hit_min_z = (aabb.min.z - self.origin.z) * self.inv_direction.z; - let hit_max_z = (aabb.max.z - self.origin.z) * self.inv_direction.z; + pub fn intersects_aabb(&self, aabb: &AABB) -> bool + where + T: ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, + { + let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); + let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); - let x_entry = hit_min_x.min(hit_max_x); - let y_entry = hit_min_y.min(hit_max_y); - let z_entry = hit_min_z.min(hit_max_z); - let x_exit = hit_min_x.max(hit_max_x); - let y_exit = hit_min_y.max(hit_max_y); - let z_exit = hit_min_z.max(hit_max_z); + let (inf, sup) = lbr.inf_sup(&rtr); - let latest_entry = x_entry.max(y_entry).max(z_entry); - let earliest_exit = x_exit.min(y_exit).min(z_exit); + let tmin = inf.max(); + let tmax = sup.min(); - latest_entry < earliest_exit && earliest_exit > 0.0 - } - - /// Implementation of the algorithm described [here](https://tavianator.com/2022/ray_box_boundary.html). - /// - /// # Examples - /// ``` - /// use bvh::aabb::AABB; - /// use bvh::ray::Ray; - /// use bvh::{Point3,Vector3}; - /// - /// let origin = Point3::new(0.0,0.0,0.0); - /// let direction = Vector3::new(1.0,0.0,0.0); - /// let ray = Ray::new(origin, direction); - /// - /// let point1 = Point3::new(99.9,-1.0,-1.0); - /// let point2 = Point3::new(100.1,1.0,1.0); - /// let aabb = AABB::with_bounds(point1, point2); - /// - /// assert!(ray.intersects_aabb_branchless(&aabb)); - /// ``` - /// - /// [`Ray`]: struct.Ray.html - /// [`AABB`]: struct.AABB.html - /// - pub fn intersects_aabb_branchless(&self, aabb: &AABB) -> bool { - let mut tmin = 0.0; - let mut tmax = INFINITY; - - let tx1 = (aabb.min.x - self.origin.x) * self.inv_direction.x; - let tx2 = (aabb.max.x - self.origin.x) * self.inv_direction.x; - - tmin = min(max(tx1, tmin), max(tx2, tmin)); - tmax = max(min(tx1, tmax), min(tx2, tmax)); - - let ty1 = (aabb.min.y - self.origin.y) * self.inv_direction.y; - let ty2 = (aabb.max.y - self.origin.y) * self.inv_direction.y; - - tmin = min(max(ty1, tmin), max(ty2, tmin)); - tmax = max(min(ty1, tmax), min(ty2, tmax)); - - let tz1 = (aabb.min.z - self.origin.z) * self.inv_direction.z; - let tz2 = (aabb.max.z - self.origin.z) * self.inv_direction.z; - - tmin = min(max(tz1, tmin), max(tz2, tmin)); - tmax = max(min(tz1, tmax), min(tz2, tmax)); - - tmin <= tmax + tmax > max(tmin, T::zero()) } /// Implementation of the @@ -286,56 +177,64 @@ impl Ray { /// The distance is set to +INFINITY if the ray does not intersect the triangle, or hits /// it from behind. #[allow(clippy::many_single_char_names)] - pub fn intersects_triangle(&self, a: &Point3, b: &Point3, c: &Point3) -> Intersection { + pub fn intersects_triangle( + &self, + a: &Point, + b: &Point, + c: &Point, + ) -> Intersection + where + T: ClosedAdd + ClosedSub + ClosedMul + Zero + One + Float, + { let a_to_b = *b - *a; let a_to_c = *c - *a; // Begin calculating determinant - also used to calculate u parameter // u_vec lies in view plane // length of a_to_c in view_plane = |u_vec| = |a_to_c|*sin(a_to_c, dir) - let u_vec = self.direction.cross(a_to_c); + let u_vec = self.direction.cross(&a_to_c); // If determinant is near zero, ray lies in plane of triangle // The determinant corresponds to the parallelepiped volume: // det = 0 => [dir, a_to_b, a_to_c] not linearly independant - let det = a_to_b.dot(u_vec); + let det = a_to_b.dot(&u_vec); // Only testing positive bound, thus enabling backface culling // If backface culling is not desired write: // det < EPSILON && det > -EPSILON - if det < EPSILON { - return Intersection::new(INFINITY, 0.0, 0.0); + if det < T::epsilon() { + return Intersection::new(T::infinity(), T::zero(), T::zero()); } - let inv_det = 1.0 / det; + let inv_det = T::one() / det; // Vector from point a to ray origin let a_to_origin = self.origin - *a; // Calculate u parameter - let u = a_to_origin.dot(u_vec) * inv_det; + let u = a_to_origin.dot(&u_vec) * inv_det; // Test bounds: u < 0 || u > 1 => outside of triangle - if !(0.0..=1.0).contains(&u) { - return Intersection::new(INFINITY, u, 0.0); + if !(T::zero()..=T::one()).contains(&u) { + return Intersection::new(T::infinity(), u, T::zero()); } // Prepare to test v parameter - let v_vec = a_to_origin.cross(a_to_b); + let v_vec = a_to_origin.cross(&a_to_b); // Calculate v parameter and test bound - let v = self.direction.dot(v_vec) * inv_det; + let v = self.direction.dot(&v_vec) * inv_det; // The intersection lies outside of the triangle - if v < 0.0 || u + v > 1.0 { - return Intersection::new(INFINITY, u, v); + if v < T::zero() || u + v > T::one() { + return Intersection::new(T::infinity(), u, v); } - let dist = a_to_c.dot(v_vec) * inv_det; + let dist = a_to_c.dot(&v_vec) * inv_det; - if dist > EPSILON { + if dist > T::epsilon() { Intersection::new(dist, u, v) } else { - Intersection::new(INFINITY, u, v) + Intersection::new(T::infinity(), u, v) } } } @@ -345,10 +244,7 @@ mod tests { use std::cmp; use std::f32::INFINITY; - use crate::aabb::AABB; - use crate::ray::Ray; - use crate::testbase::{tuple_to_point, tuplevec_small_strategy, TupleVec}; - use crate::EPSILON; + use crate::testbase::{tuple_to_point, tuplevec_small_strategy, Ray, TupleVec, AABB}; use proptest::prelude::*; @@ -381,23 +277,23 @@ mod tests { // Test whether a `Ray` which points at the center of an `AABB` intersects it. // Uses the naive algorithm. - #[test] - fn test_ray_points_at_aabb_center_naive(data in (tuplevec_small_strategy(), - tuplevec_small_strategy(), - tuplevec_small_strategy())) { - let (ray, aabb) = gen_ray_to_aabb(data); - assert!(ray.intersects_aabb_naive(&aabb)); - } - - // Test whether a `Ray` which points at the center of an `AABB` intersects it. - // Uses the branchless algorithm. - #[test] - fn test_ray_points_at_aabb_center_branchless(data in (tuplevec_small_strategy(), - tuplevec_small_strategy(), - tuplevec_small_strategy())) { - let (ray, aabb) = gen_ray_to_aabb(data); - assert!(ray.intersects_aabb_branchless(&aabb)); - } + // #[test] + // fn test_ray_points_at_aabb_center_naive(data in (tuplevec_small_strategy(), + // tuplevec_small_strategy(), + // tuplevec_small_strategy())) { + // let (ray, aabb) = gen_ray_to_aabb(data); + // assert!(ray.intersects_aabb_naive(&aabb)); + // } + + // // Test whether a `Ray` which points at the center of an `AABB` intersects it. + // // Uses the branchless algorithm. + // #[test] + // fn test_ray_points_at_aabb_center_branchless(data in (tuplevec_small_strategy(), + // tuplevec_small_strategy(), + // tuplevec_small_strategy())) { + // let (ray, aabb) = gen_ray_to_aabb(data); + // assert!(ray.intersects_aabb_branchless(&aabb)); + // } // Test whether a `Ray` which points away from the center of an `AABB` // does not intersect it, unless its origin is inside the `AABB`. @@ -417,31 +313,31 @@ mod tests { // Test whether a `Ray` which points away from the center of an `AABB` // does not intersect it, unless its origin is inside the `AABB`. // Uses the naive algorithm. - #[test] - fn test_ray_points_from_aabb_center_naive(data in (tuplevec_small_strategy(), - tuplevec_small_strategy(), - tuplevec_small_strategy())) { - let (mut ray, aabb) = gen_ray_to_aabb(data); - - // Invert the ray direction - ray.direction = -ray.direction; - ray.inv_direction = -ray.inv_direction; - assert!(!ray.intersects_aabb_naive(&aabb) || aabb.contains(&ray.origin)); - } - - // Test whether a `Ray` which points away from the center of an `AABB` - // does not intersect it, unless its origin is inside the `AABB`. - // Uses the branchless algorithm. - #[test] - fn test_ray_points_from_aabb_center_branchless(data in (tuplevec_small_strategy(), - tuplevec_small_strategy(), - tuplevec_small_strategy())) { - let (mut ray, aabb) = gen_ray_to_aabb(data); - // Invert the ray direction - ray.direction = -ray.direction; - ray.inv_direction = -ray.inv_direction; - assert!(!ray.intersects_aabb_branchless(&aabb) || aabb.contains(&ray.origin)); - } + // #[test] + // fn test_ray_points_from_aabb_center_naive(data in (tuplevec_small_strategy(), + // tuplevec_small_strategy(), + // tuplevec_small_strategy())) { + // let (mut ray, aabb) = gen_ray_to_aabb(data); + + // // Invert the ray direction + // ray.direction = -ray.direction; + // ray.inv_direction = -ray.inv_direction; + // assert!(!ray.intersects_aabb_naive(&aabb) || aabb.contains(&ray.origin)); + // } + + // // Test whether a `Ray` which points away from the center of an `AABB` + // // does not intersect it, unless its origin is inside the `AABB`. + // // Uses the branchless algorithm. + // #[test] + // fn test_ray_points_from_aabb_center_branchless(data in (tuplevec_small_strategy(), + // tuplevec_small_strategy(), + // tuplevec_small_strategy())) { + // let (mut ray, aabb) = gen_ray_to_aabb(data); + // // Invert the ray direction + // ray.direction = -ray.direction; + // ray.inv_direction = -ray.inv_direction; + // assert!(!ray.intersects_aabb_branchless(&aabb) || aabb.contains(&ray.origin)); + // } // Test whether a `Ray` which points at the center of a triangle // intersects it, unless it sees the back face, which is culled. @@ -456,7 +352,7 @@ mod tests { let triangle = (tuple_to_point(&a), tuple_to_point(&b), tuple_to_point(&c)); let u_vec = triangle.1 - triangle.0; let v_vec = triangle.2 - triangle.0; - let normal = u_vec.cross(v_vec); + let normal = u_vec.cross(&v_vec); // Get some u and v coordinates such that u+v <= 1 let u = u % 101; @@ -470,7 +366,7 @@ mod tests { // Define a ray which points at the triangle let origin = tuple_to_point(&origin); let ray = Ray::new(origin, point_on_triangle - origin); - let on_back_side = normal.dot(ray.origin - triangle.0) <= 0.0; + let on_back_side = normal.dot(&(ray.origin - triangle.0)) <= 0.0; // Perform the intersection test let intersects = ray.intersects_triangle(&triangle.0, &triangle.1, &triangle.2); @@ -488,8 +384,8 @@ mod tests { // Or the input data was close to the border let close_to_border = - u.abs() < EPSILON || (u - 1.0).abs() < EPSILON || v.abs() < EPSILON || - (v - 1.0).abs() < EPSILON || (u + v - 1.0).abs() < EPSILON; + u.abs() < f32::EPSILON || (u - 1.0).abs() < f32::EPSILON || v.abs() < f32::EPSILON || + (v - 1.0).abs() < f32::EPSILON || (u + v - 1.0).abs() < f32::EPSILON; if !(intersection_inside || close_to_border) { println!("uvsum {}", uv_sum); @@ -510,13 +406,9 @@ mod tests { mod bench { use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; - use test::{black_box, Bencher}; - use crate::aabb::AABB; - use crate::ray::Ray; - - use crate::testbase::{tuple_to_point, tuple_to_vector, TupleVec}; + use crate::testbase::{tuple_to_point, tuple_to_vector, Ray, TupleVec, AABB}; /// Generate a random deterministic `Ray`. fn random_ray(rng: &mut StdRng) -> Ray { @@ -556,27 +448,27 @@ mod bench { }); } - /// Benchmark for the naive intersection algorithm. - #[bench] - fn bench_intersects_aabb_naive(b: &mut Bencher) { - let (ray, boxes) = random_ray_and_boxes(); - - b.iter(|| { - for aabb in &boxes { - black_box(ray.intersects_aabb_naive(aabb)); - } - }); - } - - /// Benchmark for the branchless intersection algorithm. - #[bench] - fn bench_intersects_aabb_branchless(b: &mut Bencher) { - let (ray, boxes) = random_ray_and_boxes(); - - b.iter(|| { - for aabb in &boxes { - black_box(ray.intersects_aabb_branchless(aabb)); - } - }); - } + // /// Benchmark for the naive intersection algorithm. + // #[bench] + // fn bench_intersects_aabb_naive(b: &mut Bencher) { + // let (ray, boxes) = random_ray_and_boxes(); + + // b.iter(|| { + // for aabb in &boxes { + // black_box(ray.intersects_aabb_naive(aabb)); + // } + // }); + // } + + // /// Benchmark for the branchless intersection algorithm. + // #[bench] + // fn bench_intersects_aabb_branchless(b: &mut Bencher) { + // let (ray, boxes) = random_ray_and_boxes(); + + // b.iter(|| { + // for aabb in &boxes { + // black_box(ray.intersects_aabb_branchless(aabb)); + // } + // }); + // } } diff --git a/src/testbase.rs b/src/testbase.rs index f4191b3..1162d68 100644 --- a/src/testbase.rs +++ b/src/testbase.rs @@ -4,7 +4,6 @@ use std::collections::HashSet; use std::f32; -use crate::{Point3, Vector3}; use num::{FromPrimitive, Integer}; use obj::raw::object::Polygon; use obj::*; @@ -13,13 +12,22 @@ use rand::rngs::StdRng; use rand::seq::SliceRandom; use rand::SeedableRng; -use crate::aabb::{Bounded, AABB}; +use crate::aabb::Bounded; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; -use crate::ray::Ray; + +// TODO These all need to be realtyped and bounded /// A vector represented as a tuple pub type TupleVec = (f32, f32, f32); +pub type Ray = crate::ray::Ray; +pub type AABB = crate::aabb::AABB; +pub type BVH = crate::bvh::BVH; +pub type BVHNode = crate::bvh::BVHNode; +pub type Vector3 = nalgebra::SVector; +pub type Point3 = nalgebra::Point; +pub type FlatBVH = crate::flat_bvh::FlatBVH; + /// Generate a `TupleVec` for [`proptest::strategy::Strategy`] from -10e10 to 10e10 /// A small enough range to prevent most fp32 errors from breaking certain tests /// Tests which rely on this strategy should probably be rewritten @@ -69,7 +77,7 @@ impl UnitBox { } /// `UnitBox`'s `AABB`s are unit `AABB`s centered on the box's position. -impl Bounded for UnitBox { +impl Bounded for UnitBox { fn aabb(&self) -> AABB { let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); let max = self.pos + Vector3::new(0.5, 0.5, 0.5); @@ -77,7 +85,7 @@ impl Bounded for UnitBox { } } -impl BHShape for UnitBox { +impl BHShape for UnitBox { fn set_bh_node_index(&mut self, index: usize) { self.node_index = index; } @@ -99,7 +107,7 @@ pub fn generate_aligned_boxes() -> Vec { } /// Creates a `BoundingHierarchy` for a fixed scene structure. -pub fn build_some_bh() -> (Vec, BH) { +pub fn build_some_bh>() -> (Vec, BH) { let mut boxes = generate_aligned_boxes(); let bh = BH::build(&mut boxes); (boxes, bh) @@ -107,7 +115,7 @@ pub fn build_some_bh() -> (Vec, BH) { /// Given a ray, a bounding hierarchy, the complete list of shapes in the scene and a list of /// expected hits, verifies, whether the ray hits only the expected shapes. -fn traverse_and_verify( +fn traverse_and_verify>( ray_origin: Point3, ray_direction: Vector3, all_shapes: &[UnitBox], @@ -124,7 +132,7 @@ fn traverse_and_verify( } /// Perform some fixed intersection tests on BH structures. -pub fn traverse_some_bh() { +pub fn traverse_some_bh>() { let (all_shapes, bh) = build_some_bh::(); { @@ -187,13 +195,13 @@ impl Triangle { } } -impl Bounded for Triangle { +impl Bounded for Triangle { fn aabb(&self) -> AABB { self.aabb } } -impl BHShape for Triangle { +impl BHShape for Triangle { fn set_bh_node_index(&mut self, index: usize) { self.node_index = index; } @@ -426,7 +434,7 @@ pub fn randomly_transform_scene( let aabb = triangles[*index].aabb(); let min_move_bound = bounds.min - aabb.min; let max_move_bound = bounds.max - aabb.max; - let movement_bounds = AABB::with_bounds(min_move_bound, max_move_bound); + let movement_bounds = AABB::with_bounds(min_move_bound.into(), max_move_bound.into()); let mut random_offset = next_point3(seed, &movement_bounds); random_offset.x = max_offset.min((-max_offset).max(random_offset.x)); @@ -436,9 +444,9 @@ pub fn randomly_transform_scene( let triangle = &mut triangles[*index]; let old_index = triangle.bh_node_index(); *triangle = Triangle::new( - triangle.a + random_offset, - triangle.b + random_offset, - triangle.c + random_offset, + (triangle.a.coords + random_offset.coords).into(), + (triangle.b.coords + random_offset.coords).into(), + (triangle.c.coords + random_offset.coords).into(), ); triangle.set_bh_node_index(old_index); } @@ -453,12 +461,12 @@ pub fn randomly_transform_scene( pub fn create_ray(seed: &mut u64, bounds: &AABB) -> Ray { let origin = next_point3(seed, bounds); let direction = next_point3(seed, bounds); - Ray::new(origin, direction) + Ray::new(origin, direction.coords) } /// Benchmark the construction of a `BoundingHierarchy` with `n` triangles. #[cfg(feature = "bench")] -fn build_n_triangles_bh(n: usize, b: &mut ::test::Bencher) { +fn build_n_triangles_bh>(n: usize, b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(n, &bounds); b.iter(|| { @@ -468,19 +476,19 @@ fn build_n_triangles_bh(n: usize, b: &mut ::test::Bencher) /// Benchmark the construction of a `BoundingHierarchy` with 1,200 triangles. #[cfg(feature = "bench")] -pub fn build_1200_triangles_bh(b: &mut ::test::Bencher) { +pub fn build_1200_triangles_bh>(b: &mut ::test::Bencher) { build_n_triangles_bh::(100, b); } /// Benchmark the construction of a `BoundingHierarchy` with 12,000 triangles. #[cfg(feature = "bench")] -pub fn build_12k_triangles_bh(b: &mut ::test::Bencher) { +pub fn build_12k_triangles_bh>(b: &mut ::test::Bencher) { build_n_triangles_bh::(1_000, b); } /// Benchmark the construction of a `BoundingHierarchy` with 120,000 triangles. #[cfg(feature = "bench")] -pub fn build_120k_triangles_bh(b: &mut ::test::Bencher) { +pub fn build_120k_triangles_bh>(b: &mut ::test::Bencher) { build_n_triangles_bh::(10_000, b); } @@ -551,7 +559,7 @@ fn bench_intersect_sponza_list_aabb(b: &mut ::test::Bencher) { } #[cfg(feature = "bench")] -pub fn intersect_bh( +pub fn intersect_bh>( bh: &T, triangles: &[Triangle], bounds: &AABB, @@ -573,7 +581,7 @@ pub fn intersect_bh( /// Benchmark the traversal of a `BoundingHierarchy` with `n` triangles. #[cfg(feature = "bench")] -pub fn intersect_n_triangles(n: usize, b: &mut ::test::Bencher) { +pub fn intersect_n_triangles>(n: usize, b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(n, &bounds); let bh = T::build(&mut triangles); @@ -582,18 +590,18 @@ pub fn intersect_n_triangles(n: usize, b: &mut ::test::Ben /// Benchmark the traversal of a `BoundingHierarchy` with 1,200 triangles. #[cfg(feature = "bench")] -pub fn intersect_1200_triangles_bh(b: &mut ::test::Bencher) { +pub fn intersect_1200_triangles_bh>(b: &mut ::test::Bencher) { intersect_n_triangles::(100, b); } /// Benchmark the traversal of a `BoundingHierarchy` with 12,000 triangles. #[cfg(feature = "bench")] -pub fn intersect_12k_triangles_bh(b: &mut ::test::Bencher) { +pub fn intersect_12k_triangles_bh>(b: &mut ::test::Bencher) { intersect_n_triangles::(1_000, b); } /// Benchmark the traversal of a `BoundingHierarchy` with 120,000 triangles. #[cfg(feature = "bench")] -pub fn intersect_120k_triangles_bh(b: &mut ::test::Bencher) { +pub fn intersect_120k_triangles_bh>(b: &mut ::test::Bencher) { intersect_n_triangles::(10_000, b); } diff --git a/src/utils.rs b/src/utils.rs index 7ca88a0..d783de9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,8 @@ //! Utilities module. +use nalgebra::{Scalar, SimdPartialOrd}; +use num::Float; + use crate::aabb::AABB; use crate::bounding_hierarchy::BHShape; @@ -15,18 +18,18 @@ pub fn concatenate_vectors(vectors: &mut [Vec]) -> Vec { /// Defines a Bucket utility object. Used to store the properties of shape-partitions /// in the BVH build procedure using SAH. -#[derive(Copy, Clone)] -pub struct Bucket { +#[derive(Clone, Copy)] +pub struct Bucket { /// The number of shapes in this `Bucket`. pub size: usize, /// The joint `AABB` of the shapes in this `Bucket`. - pub aabb: AABB, + pub aabb: AABB, } -impl Bucket { +impl Bucket { /// Returns an empty bucket. - pub fn empty() -> Bucket { + pub fn empty() -> Bucket { Bucket { size: 0, aabb: AABB::empty(), @@ -34,13 +37,13 @@ impl Bucket { } /// Extend this `Bucket` by a shape with the given `AABB`. - pub fn add_aabb(&mut self, aabb: &AABB) { + pub fn add_aabb(&mut self, aabb: &AABB) { self.size += 1; self.aabb = self.aabb.join(aabb); } /// Join the contents of two `Bucket`s. - pub fn join_bucket(a: Bucket, b: &Bucket) -> Bucket { + pub fn join_bucket(a: Bucket, b: &Bucket) -> Bucket { Bucket { size: a.size + b.size, aabb: a.aabb.join(&b.aabb), @@ -48,7 +51,14 @@ impl Bucket { } } -pub fn joint_aabb_of_shapes(indices: &[usize], shapes: &[Shape]) -> AABB { +pub fn joint_aabb_of_shapes< + T: Scalar + Copy + Float + SimdPartialOrd, + const D: usize, + Shape: BHShape, +>( + indices: &[usize], + shapes: &[Shape], +) -> AABB { let mut aabb = AABB::empty(); for index in indices { let shape = &shapes[*index]; From b535240d9b766fc62b784435d8d6568e4f0856a7 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Sat, 22 Apr 2023 12:20:36 -0700 Subject: [PATCH 02/20] Initial f32x3 optimization for SIMD --- rust-toolchain.toml | 2 + src/lib.rs | 3 ++ src/ray.rs | 108 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index db8847b..f5969f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,9 @@ #![deny(missing_docs)] #![cfg_attr(feature = "bench", feature(test))] +#![feature(specialization)] +#![feature(portable_simd)] +#![feature(stdsimd)] #[cfg(all(feature = "bench", test))] extern crate test; diff --git a/src/ray.rs b/src/ray.rs index 44a6784..874b2ca 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,6 +1,9 @@ //! This module defines a Ray structure and intersection algorithms //! for axis aligned bounding boxes and triangles. +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + use crate::aabb::AABB; use nalgebra::{ ClosedAdd, ClosedMul, ClosedSub, ComplexField, Point, SVector, Scalar, SimdPartialOrd, @@ -93,6 +96,101 @@ fn max(x: T, y: T) -> T { } } +trait IntersectsAABB +where + T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, +{ + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool; +} + +impl IntersectsAABB, T, D> for Ray +where + T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, +{ + #[inline(always)] + default fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); + let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); + + let (inf, sup) = lbr.inf_sup(&rtr); + + let tmin = inf.max(); + let tmax = sup.min(); + + tmax > max(tmin, T::zero()) + } +} + +#[inline(always)] +#[cfg(target_arch = "x86_64")] +fn vec_to_mm(vec: &SVector) -> __m128 { + unsafe { _mm_set_ps(vec.z, vec.z, vec.y, vec.x) } +} + +#[inline(always)] +#[cfg(target_arch = "x86_64")] +fn max_elem(mm: __m128) -> f32 { + unsafe { + let a = _mm_unpacklo_ps(mm, mm); // x x y y + let b = _mm_unpackhi_ps(mm, mm); // z z w w + let c = _mm_max_ps(a, b); // ..., max(x, z), ..., ... + let res = _mm_max_ps(mm, c); // ..., max(y, max(x, z)), ..., ... + #[allow(invalid_value)] + let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); + _mm_store_ps(data.as_mut_ptr(), res); + return data[1]; + } +} + +#[inline(always)] +#[cfg(target_arch = "x86_64")] +fn min_elem(mm: __m128) -> f32 { + unsafe { + let a = _mm_unpacklo_ps(mm, mm); // x x y y + let b = _mm_unpackhi_ps(mm, mm); // z z w w + let c = _mm_min_ps(a, b); // ..., min(x, z), ..., ... + let res = _mm_min_ps(mm, c); // ..., min(y, min(x, z)), ..., ... + #[allow(invalid_value)] + let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); + _mm_store_ps(data.as_mut_ptr(), res); + return data[1]; + } +} + +#[cfg(target_arch = "x86_64")] +impl IntersectsAABB, f32, 3> for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + use std::arch::x86_64::*; + + unsafe { + let v1 = vec_to_mm(&aabb[0].coords); + let v2 = vec_to_mm(&aabb[1].coords); + + let oc = vec_to_mm(&self.origin.coords); + let v1 = _mm_sub_ps(v1, oc); + let v2 = _mm_sub_ps(v2, oc); + drop(oc); + + let id = vec_to_mm(&self.inv_direction); + let v1 = _mm_mul_ps(v1, id); + let v2 = _mm_mul_ps(v2, id); + drop(id); + + let inf = _mm_min_ps(v1, v2); + let sup = _mm_max_ps(v1, v2); + + drop(v1); + drop(v2); + + let tmin = max_elem(inf); + let tmax = min_elem(sup); + + tmax > max(tmin, 0.0) + } + } +} + impl Ray { /// Creates a new [`Ray`] from an `origin` and a `direction`. /// `direction` will be normalized. @@ -159,15 +257,7 @@ impl Ray { where T: ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, { - let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); - let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); - - let (inf, sup) = lbr.inf_sup(&rtr); - - let tmin = inf.max(); - let tmax = sup.min(); - - tmax > max(tmin, T::zero()) + self.ray_intersects_aabb(aabb) } /// Implementation of the From a5fa1dac11a3a334cb1767f3225abac4fa96d3bf Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Sun, 23 Apr 2023 17:10:27 -0700 Subject: [PATCH 03/20] Try using simba --- Cargo.toml | 3 ++- src/ray.rs | 68 ++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5fee554..77d610e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ rand = "0.8" log = "0.4" serde = { optional = true, version = "1", features = ["derive"] } num = "0.4.0" -nalgebra = { version = "0.32.2", features = ["serde"] } +nalgebra = { version = "0.32.2", features = ["default", "serde"] } +simba = "0.8.1" [dev-dependencies] proptest = "1.0" diff --git a/src/ray.rs b/src/ray.rs index 874b2ca..cdf952e 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -3,12 +3,14 @@ #[cfg(target_arch = "x86_64")] use std::arch::x86_64::*; +use std::simd::f32x4; use crate::aabb::AABB; use nalgebra::{ ClosedAdd, ClosedMul, ClosedSub, ComplexField, Point, SVector, Scalar, SimdPartialOrd, }; use num::{Float, One, Zero}; +use simba::simd::WideF32x4; /// A struct which defines a ray and some of its cached values. #[derive(Debug, Clone, Copy)] @@ -157,34 +159,70 @@ fn min_elem(mm: __m128) -> f32 { } } -#[cfg(target_arch = "x86_64")] +// #[cfg(target_arch = "x86_64")] +// impl IntersectsAABB, f32, 3> for Ray { +// #[inline(always)] +// fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { +// use std::arch::x86_64::*; + +// unsafe { +// let v1 = vec_to_mm(&aabb[0].coords); +// let v2 = vec_to_mm(&aabb[1].coords); + +// let oc = vec_to_mm(&self.origin.coords); +// let v1 = _mm_sub_ps(v1, oc); +// let v2 = _mm_sub_ps(v2, oc); +// drop(oc); + +// let id = vec_to_mm(&self.inv_direction); +// let v1 = _mm_mul_ps(v1, id); +// let v2 = _mm_mul_ps(v2, id); +// drop(id); + +// let inf = _mm_min_ps(v1, v2); +// let sup = _mm_max_ps(v1, v2); + +// drop(v1); +// drop(v2); + +// let tmin = max_elem(inf); +// let tmax = min_elem(sup); + +// tmax > max(tmin, 0.0) +// } +// } +// } + +fn vec_to_wide(vec: &SVector) -> WideF32x4 { + WideF32x4::from([vec.x, vec.y, vec.z, vec.z]) +} + impl IntersectsAABB, f32, 3> for Ray { #[inline(always)] fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { - use std::arch::x86_64::*; unsafe { - let v1 = vec_to_mm(&aabb[0].coords); - let v2 = vec_to_mm(&aabb[1].coords); + let v1 = vec_to_wide(&aabb[0].coords); + let v2 = vec_to_wide(&aabb[1].coords); - let oc = vec_to_mm(&self.origin.coords); - let v1 = _mm_sub_ps(v1, oc); - let v2 = _mm_sub_ps(v2, oc); + let oc = vec_to_wide(&self.origin.coords); + let v1 = v1 - oc; + let v2 = v2 - oc; drop(oc); - let id = vec_to_mm(&self.inv_direction); - let v1 = _mm_mul_ps(v1, id); - let v2 = _mm_mul_ps(v2, id); + let id = vec_to_wide(&self.inv_direction); + let v1 = v1 * id; + let v2 = v2 * id; drop(id); - let inf = _mm_min_ps(v1, v2); - let sup = _mm_max_ps(v1, v2); - + let inf = v1.simd_min(v2); + let sup = v1.simd_max(v2); + drop(v1); drop(v2); - let tmin = max_elem(inf); - let tmax = min_elem(sup); + let tmin = inf.simd_horizontal_max(); + let tmax = sup.simd_horizontal_min(); tmax > max(tmin, 0.0) } From 2006787d2f41bf0c99c1a26419aef5d1e778933f Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Mon, 24 Apr 2023 17:29:41 -0700 Subject: [PATCH 04/20] Added intrinsics for f32/64 2-4 dimensions on x86_65, guarded by nightly and "full_simd" feature --- Cargo.toml | 2 +- rust-toolchain.toml | 2 - src/lib.rs | 5 +- src/ray/intersect_default.rs | 47 ++++++ src/ray/intersect_x86_64.rs | 283 ++++++++++++++++++++++++++++++++ src/ray/mod.rs | 9 + src/{ray.rs => ray/ray_impl.rs} | 273 +----------------------------- src/utils.rs | 50 ++++++ 8 files changed, 395 insertions(+), 276 deletions(-) delete mode 100644 rust-toolchain.toml create mode 100644 src/ray/intersect_default.rs create mode 100644 src/ray/intersect_x86_64.rs create mode 100644 src/ray/mod.rs rename src/{ray.rs => ray/ray_impl.rs} (56%) diff --git a/Cargo.toml b/Cargo.toml index 77d610e..3ebc199 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ log = "0.4" serde = { optional = true, version = "1", features = ["derive"] } num = "0.4.0" nalgebra = { version = "0.32.2", features = ["default", "serde"] } -simba = "0.8.1" [dev-dependencies] proptest = "1.0" @@ -31,6 +30,7 @@ doc-comment = "0.3" [features] bench = [] +full_simd = [] [profile.release] lto = true diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 271800c..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f5969f1..0680b6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,9 +71,8 @@ #![deny(missing_docs)] #![cfg_attr(feature = "bench", feature(test))] -#![feature(specialization)] -#![feature(portable_simd)] -#![feature(stdsimd)] + +#![cfg_attr(feature = "full_simd", feature(min_specialization))] #[cfg(all(feature = "bench", test))] extern crate test; diff --git a/src/ray/intersect_default.rs b/src/ray/intersect_default.rs new file mode 100644 index 0000000..094cd2a --- /dev/null +++ b/src/ray/intersect_default.rs @@ -0,0 +1,47 @@ +use super::Ray; +use crate::{aabb::AABB, utils::fast_max}; +use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::Zero; + +pub trait RayIntersection +where + T: Copy + Scalar, +{ + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool; +} + +#[cfg(not(feature = "full_simd"))] +impl RayIntersection for Ray +where + T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, +{ + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); + let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); + + let (inf, sup) = lbr.inf_sup(&rtr); + + let tmin = inf.max(); + let tmax = sup.min(); + + tmax > fast_max(tmin, T::zero()) + } +} + +#[cfg(all(feature = "full_simd", target_arch = "x86_64"))] +impl RayIntersection for Ray +where + T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, +{ + default fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); + let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); + + let (inf, sup) = lbr.inf_sup(&rtr); + + let tmin = inf.max(); + let tmax = sup.min(); + + tmax > max(tmin, T::zero()) + } +} diff --git a/src/ray/intersect_x86_64.rs b/src/ray/intersect_x86_64.rs new file mode 100644 index 0000000..8522428 --- /dev/null +++ b/src/ray/intersect_x86_64.rs @@ -0,0 +1,283 @@ +use std::arch::x86_64::*; + +use nalgebra::SVector; + +use crate::{aabb::AABB, utils::fast_max}; + +use super::{intersect_default::RayIntersection, Ray}; + +trait ToRegisterType { + type Register; + + fn to_register(&self) -> Self::Register; +} + +impl ToRegisterType for SVector { + type Register = __m128; + + #[inline(always)] + fn to_register(&self) -> Self::Register { + unsafe { _mm_set_ps(self.y, self.y, self.y, self.x) } + } +} + +impl ToRegisterType for SVector { + type Register = __m128; + + #[inline(always)] + fn to_register(&self) -> Self::Register { + unsafe { _mm_set_ps(self.z, self.z, self.y, self.x) } + } +} + +impl ToRegisterType for SVector { + type Register = __m128; + + #[inline(always)] + fn to_register(&self) -> Self::Register { + unsafe { _mm_set_ps(self.w, self.z, self.y, self.x) } + } +} + +#[inline(always)] +fn max_elem_m128(mm: __m128) -> f32 { + unsafe { + let a = _mm_unpacklo_ps(mm, mm); // x x y y + let b = _mm_unpackhi_ps(mm, mm); // z z w w + let c = _mm_max_ps(a, b); // ..., max(x, z), ..., ... + let res = _mm_max_ps(mm, c); // ..., max(y, max(x, z)), ..., ... + #[allow(invalid_value)] + let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); + _mm_store_ps(data.as_mut_ptr(), res); + return data[1]; + } +} + +#[inline(always)] +fn min_elem_m128(mm: __m128) -> f32 { + unsafe { + let a = _mm_unpacklo_ps(mm, mm); // x x y y + let b = _mm_unpackhi_ps(mm, mm); // z z w w + let c = _mm_min_ps(a, b); // ..., min(x, z), ..., ... + let res = _mm_min_ps(mm, c); // ..., min(y, min(x, z)), ..., ... + #[allow(invalid_value)] + let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); + _mm_store_ps(data.as_mut_ptr(), res); + return data[1]; + } +} + +#[inline(always)] +fn ray_intersects_aabb_m128( + ray_origin: __m128, + ray_inv_dir: __m128, + aabb_0: __m128, + aabb_1: __m128, +) -> bool { + unsafe { + let v1 = _mm_mul_ps(_mm_sub_ps(aabb_0, ray_origin), ray_inv_dir); + let v2 = _mm_mul_ps(_mm_sub_ps(aabb_1, ray_origin), ray_inv_dir); + + let inf = _mm_min_ps(v1, v2); + let sup = _mm_max_ps(v1, v2); + + let tmin = max_elem_m128(inf); + let tmax = min_elem_m128(sup); + + tmax > fast_max(tmin, 0.0) + } +} + +impl RayIntersection for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let ro = self.origin.coords.to_register(); + let ri = self.inv_direction.to_register(); + let aabb_0 = aabb[0].coords.to_register(); + let aabb_1 = aabb[1].coords.to_register(); + + ray_intersects_aabb_m128(ro, ri, aabb_0, aabb_1) + } +} + +impl RayIntersection for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let ro = self.origin.coords.to_register(); + let ri = self.inv_direction.to_register(); + let aabb_0 = aabb[0].coords.to_register(); + let aabb_1 = aabb[1].coords.to_register(); + + ray_intersects_aabb_m128(ro, ri, aabb_0, aabb_1) + } +} + +impl RayIntersection for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let ro = self.origin.coords.to_register(); + let ri = self.inv_direction.to_register(); + let aabb_0 = aabb[0].coords.to_register(); + let aabb_1 = aabb[1].coords.to_register(); + + ray_intersects_aabb_m128(ro, ri, aabb_0, aabb_1) + } +} + +impl ToRegisterType for SVector { + type Register = __m128d; + + #[inline(always)] + fn to_register(&self) -> Self::Register { + unsafe { _mm_set_pd(self.y, self.x) } + } +} + +#[inline(always)] +fn max_elem_m128d(mm: __m128d) -> f64 { + unsafe { + let a = _mm_unpacklo_pd(mm, mm); // x x + let b = _mm_max_pd(mm, b); // max(x, x), max(x, y) + #[allow(invalid_value)] + let mut data: [f64; 2] = std::mem::MaybeUninit::uninit().assume_init(); + _mm_store_pd(data.as_mut_ptr(), b); + return data[1]; + } +} + +#[inline(always)] +fn min_elem_m128d(mm: __m128d) -> f64 { + unsafe { + let a = _mm_unpacklo_pd(mm, mm); // x x + let b = _mm_unpackhi_pd(mm, mm); // y y + let c = _mm_min_pd(a, b); // min(x, y), min(x, y) + #[allow(invalid_value)] + let mut data: [f64; 2] = std::mem::MaybeUninit::uninit().assume_init(); + _mm_store_pd(data.as_mut_ptr(), c); + return data[0]; + } +} + +#[inline(always)] +fn ray_intersects_aabb_m128d( + ray_origin: __m128d, + ray_inv_dir: __m128d, + aabb_0: __m128d, + aabb_1: __m128d, +) -> bool { + unsafe { + let v1 = _mm_mul_pd(_mm_sub_pd(aabb_0, ray_origin), ray_inv_dir); + let v2 = _mm_mul_pd(_mm_sub_pd(aabb_1, ray_origin), ray_inv_dir); + + let inf = _mm_min_pd(v1, v2); + let sup = _mm_max_pd(v1, v2); + + let tmin = max_elem_m128d(inf); + let tmax = min_elem_m128d(sup); + + tmax > fast_max(tmin, 0.0) + } +} + +impl RayIntersection for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let ro = self.origin.coords.to_register(); + let ri = self.inv_direction.to_register(); + let aabb_0 = aabb[0].coords.to_register(); + let aabb_1 = aabb[1].coords.to_register(); + + ray_intersects_aabb_m128d(ro, ri, aabb_0, aabb_1) + } +} + +impl ToRegisterType for SVector { + type Register = __m256d; + + #[inline(always)] + fn to_register(&self) -> Self::Register { + unsafe { _mm256_set_pd(self.z, self.z, self.y, self.x) } + } +} + +impl ToRegisterType for SVector { + type Register = __m256d; + + #[inline(always)] + fn to_register(&self) -> Self::Register { + unsafe { _mm256_set_pd(self.w, self.z, self.y, self.x) } + } +} + +#[inline(always)] +fn max_elem_m256d(mm: __m256d) -> f64 { + unsafe { + let a = _mm256_unpacklo_pd(mm, mm); // x x y y + let b = _mm256_unpackhi_pd(mm, mm); // z z w w + let c = _mm256_max_pd(a, b); // ..., max(x, z), ..., ... + let res = _mm256_max_pd(mm, c); // ..., max(y, max(x, z)), ..., ... + #[allow(invalid_value)] + let mut data: [f64; 4] = std::mem::MaybeUninit::uninit().assume_init(); + _mm256_store_pd(data.as_mut_ptr(), res); + return data[1]; + } +} + +#[inline(always)] +fn min_elem_m256d(mm: __m256d) -> f64 { + unsafe { + let a = _mm256_unpacklo_pd(mm, mm); // x x y y + let b = _mm256_unpackhi_pd(mm, mm); // z z w w + let c = _mm256_min_pd(a, b); // ..., min(x, z), ..., ... + let res = _mm256_min_pd(mm, c); // ..., min(y, min(x, z)), ..., ... + #[allow(invalid_value)] + let mut data: [f64; 4] = std::mem::MaybeUninit::uninit().assume_init(); + _mm256_store_pd(data.as_mut_ptr(), res); + return data[1]; + } +} + +#[inline(always)] +fn ray_intersects_aabb_m256d( + ray_origin: __m256d, + ray_inv_dir: __m256d, + aabb_0: __m256d, + aabb_1: __m256d, +) -> bool { + unsafe { + let v1 = _mm256_mul_pd(_mm256_sub_pd(aabb_0, ray_origin), ray_inv_dir); + let v2 = _mm256_mul_pd(_mm256_sub_pd(aabb_1, ray_origin), ray_inv_dir); + + let inf = _mm256_min_pd(v1, v2); + let sup = _mm256_max_pd(v1, v2); + + let tmin = max_elem_m256d(inf); + let tmax = min_elem_m256d(sup); + + tmax > fast_max(tmin, 0.0) + } +} + +impl RayIntersection for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let ro = self.origin.coords.to_register(); + let ri = self.inv_direction.to_register(); + let aabb_0 = aabb[0].coords.to_register(); + let aabb_1 = aabb[1].coords.to_register(); + + ray_intersects_aabb_m256d(ro, ri, aabb_0, aabb_1) + } +} + +impl RayIntersection for Ray { + #[inline(always)] + fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + let ro = self.origin.coords.to_register(); + let ri = self.inv_direction.to_register(); + let aabb_0 = aabb[0].coords.to_register(); + let aabb_1 = aabb[1].coords.to_register(); + + ray_intersects_aabb_m256d(ro, ri, aabb_0, aabb_1) + } +} \ No newline at end of file diff --git a/src/ray/mod.rs b/src/ray/mod.rs new file mode 100644 index 0000000..b58415c --- /dev/null +++ b/src/ray/mod.rs @@ -0,0 +1,9 @@ +//! This module holds the ray definition, and overloads for x86_64 simd operations + +mod ray_impl; +mod intersect_default; + +#[cfg(all(feature = "full_simd", target_arch = "x86_64"))] +mod intersect_x86_64; + +pub use self::ray_impl::*; \ No newline at end of file diff --git a/src/ray.rs b/src/ray/ray_impl.rs similarity index 56% rename from src/ray.rs rename to src/ray/ray_impl.rs index cdf952e..759b5b2 100644 --- a/src/ray.rs +++ b/src/ray/ray_impl.rs @@ -1,16 +1,13 @@ //! This module defines a Ray structure and intersection algorithms //! for axis aligned bounding boxes and triangles. -#[cfg(target_arch = "x86_64")] -use std::arch::x86_64::*; -use std::simd::f32x4; - use crate::aabb::AABB; use nalgebra::{ ClosedAdd, ClosedMul, ClosedSub, ComplexField, Point, SVector, Scalar, SimdPartialOrd, }; use num::{Float, One, Zero}; -use simba::simd::WideF32x4; + +use super::intersect_default::RayIntersection; /// A struct which defines a ray and some of its cached values. #[derive(Debug, Clone, Copy)] @@ -25,7 +22,7 @@ pub struct Ray { /// /// [`AABB`]: struct.AABB.html /// - inv_direction: SVector, + pub inv_direction: SVector, } /// A struct which is returned by the `intersects_triangle` method. @@ -48,187 +45,6 @@ impl Intersection { } } -/// Fast floating point minimum. This function matches the semantics of -/// -/// ```no_compile -/// if x < y { x } else { y } -/// ``` -/// -/// which has efficient instruction sequences on many platforms (1 instruction on x86). For most -/// values, it matches the semantics of `x.min(y)`; the special cases are: -/// -/// ```text -/// min(-0.0, +0.0); +0.0 -/// min(+0.0, -0.0): -0.0 -/// min( NaN, 1.0): 1.0 -/// min( 1.0, NaN): NaN -/// ``` -#[inline(always)] -#[allow(dead_code)] -fn min(x: T, y: T) -> T { - if x < y { - x - } else { - y - } -} - -/// Fast floating point maximum. This function matches the semantics of -/// -/// ```no_compile -/// if x > y { x } else { y } -/// ``` -/// -/// which has efficient instruction sequences on many platforms (1 instruction on x86). For most -/// values, it matches the semantics of `x.max(y)`; the special cases are: -/// -/// ```text -/// max(-0.0, +0.0); +0.0 -/// max(+0.0, -0.0): -0.0 -/// max( NaN, 1.0): 1.0 -/// max( 1.0, NaN): NaN -/// ``` -#[inline(always)] -#[allow(dead_code)] -fn max(x: T, y: T) -> T { - if x > y { - x - } else { - y - } -} - -trait IntersectsAABB -where - T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, -{ - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool; -} - -impl IntersectsAABB, T, D> for Ray -where - T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, -{ - #[inline(always)] - default fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { - let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); - let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); - - let (inf, sup) = lbr.inf_sup(&rtr); - - let tmin = inf.max(); - let tmax = sup.min(); - - tmax > max(tmin, T::zero()) - } -} - -#[inline(always)] -#[cfg(target_arch = "x86_64")] -fn vec_to_mm(vec: &SVector) -> __m128 { - unsafe { _mm_set_ps(vec.z, vec.z, vec.y, vec.x) } -} - -#[inline(always)] -#[cfg(target_arch = "x86_64")] -fn max_elem(mm: __m128) -> f32 { - unsafe { - let a = _mm_unpacklo_ps(mm, mm); // x x y y - let b = _mm_unpackhi_ps(mm, mm); // z z w w - let c = _mm_max_ps(a, b); // ..., max(x, z), ..., ... - let res = _mm_max_ps(mm, c); // ..., max(y, max(x, z)), ..., ... - #[allow(invalid_value)] - let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); - _mm_store_ps(data.as_mut_ptr(), res); - return data[1]; - } -} - -#[inline(always)] -#[cfg(target_arch = "x86_64")] -fn min_elem(mm: __m128) -> f32 { - unsafe { - let a = _mm_unpacklo_ps(mm, mm); // x x y y - let b = _mm_unpackhi_ps(mm, mm); // z z w w - let c = _mm_min_ps(a, b); // ..., min(x, z), ..., ... - let res = _mm_min_ps(mm, c); // ..., min(y, min(x, z)), ..., ... - #[allow(invalid_value)] - let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); - _mm_store_ps(data.as_mut_ptr(), res); - return data[1]; - } -} - -// #[cfg(target_arch = "x86_64")] -// impl IntersectsAABB, f32, 3> for Ray { -// #[inline(always)] -// fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { -// use std::arch::x86_64::*; - -// unsafe { -// let v1 = vec_to_mm(&aabb[0].coords); -// let v2 = vec_to_mm(&aabb[1].coords); - -// let oc = vec_to_mm(&self.origin.coords); -// let v1 = _mm_sub_ps(v1, oc); -// let v2 = _mm_sub_ps(v2, oc); -// drop(oc); - -// let id = vec_to_mm(&self.inv_direction); -// let v1 = _mm_mul_ps(v1, id); -// let v2 = _mm_mul_ps(v2, id); -// drop(id); - -// let inf = _mm_min_ps(v1, v2); -// let sup = _mm_max_ps(v1, v2); - -// drop(v1); -// drop(v2); - -// let tmin = max_elem(inf); -// let tmax = min_elem(sup); - -// tmax > max(tmin, 0.0) -// } -// } -// } - -fn vec_to_wide(vec: &SVector) -> WideF32x4 { - WideF32x4::from([vec.x, vec.y, vec.z, vec.z]) -} - -impl IntersectsAABB, f32, 3> for Ray { - #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { - - unsafe { - let v1 = vec_to_wide(&aabb[0].coords); - let v2 = vec_to_wide(&aabb[1].coords); - - let oc = vec_to_wide(&self.origin.coords); - let v1 = v1 - oc; - let v2 = v2 - oc; - drop(oc); - - let id = vec_to_wide(&self.inv_direction); - let v1 = v1 * id; - let v2 = v2 * id; - drop(id); - - let inf = v1.simd_min(v2); - let sup = v1.simd_max(v2); - - drop(v1); - drop(v2); - - let tmin = inf.simd_horizontal_max(); - let tmax = sup.simd_horizontal_min(); - - tmax > max(tmin, 0.0) - } - } -} - impl Ray { /// Creates a new [`Ray`] from an `origin` and a `direction`. /// `direction` will be normalized. @@ -258,14 +74,6 @@ impl Ray { direction, inv_direction: direction.map(|x| T::one() / x), } - - // println!("dir: {}, inv_dir: {}", r.direction, r.inv_direction); - // assert_eq!( - // r.direction.component_mul(&r.inv_direction), - // SVector::::from_element(T::one()) - // ); - - // r } /// Tests the intersection of a [`Ray`] with an [`AABB`] using the optimized algorithm @@ -394,7 +202,6 @@ mod tests { proptest! { // Test whether a `Ray` which points at the center of an `AABB` intersects it. - // Uses the optimized algorithm. #[test] fn test_ray_points_at_aabb_center(data in (tuplevec_small_strategy(), tuplevec_small_strategy(), @@ -403,29 +210,8 @@ mod tests { assert!(ray.intersects_aabb(&aabb)); } - // Test whether a `Ray` which points at the center of an `AABB` intersects it. - // Uses the naive algorithm. - // #[test] - // fn test_ray_points_at_aabb_center_naive(data in (tuplevec_small_strategy(), - // tuplevec_small_strategy(), - // tuplevec_small_strategy())) { - // let (ray, aabb) = gen_ray_to_aabb(data); - // assert!(ray.intersects_aabb_naive(&aabb)); - // } - - // // Test whether a `Ray` which points at the center of an `AABB` intersects it. - // // Uses the branchless algorithm. - // #[test] - // fn test_ray_points_at_aabb_center_branchless(data in (tuplevec_small_strategy(), - // tuplevec_small_strategy(), - // tuplevec_small_strategy())) { - // let (ray, aabb) = gen_ray_to_aabb(data); - // assert!(ray.intersects_aabb_branchless(&aabb)); - // } - // Test whether a `Ray` which points away from the center of an `AABB` // does not intersect it, unless its origin is inside the `AABB`. - // Uses the optimized algorithm. #[test] fn test_ray_points_from_aabb_center(data in (tuplevec_small_strategy(), tuplevec_small_strategy(), @@ -438,35 +224,6 @@ mod tests { assert!(!ray.intersects_aabb(&aabb) || aabb.contains(&ray.origin)); } - // Test whether a `Ray` which points away from the center of an `AABB` - // does not intersect it, unless its origin is inside the `AABB`. - // Uses the naive algorithm. - // #[test] - // fn test_ray_points_from_aabb_center_naive(data in (tuplevec_small_strategy(), - // tuplevec_small_strategy(), - // tuplevec_small_strategy())) { - // let (mut ray, aabb) = gen_ray_to_aabb(data); - - // // Invert the ray direction - // ray.direction = -ray.direction; - // ray.inv_direction = -ray.inv_direction; - // assert!(!ray.intersects_aabb_naive(&aabb) || aabb.contains(&ray.origin)); - // } - - // // Test whether a `Ray` which points away from the center of an `AABB` - // // does not intersect it, unless its origin is inside the `AABB`. - // // Uses the branchless algorithm. - // #[test] - // fn test_ray_points_from_aabb_center_branchless(data in (tuplevec_small_strategy(), - // tuplevec_small_strategy(), - // tuplevec_small_strategy())) { - // let (mut ray, aabb) = gen_ray_to_aabb(data); - // // Invert the ray direction - // ray.direction = -ray.direction; - // ray.inv_direction = -ray.inv_direction; - // assert!(!ray.intersects_aabb_branchless(&aabb) || aabb.contains(&ray.origin)); - // } - // Test whether a `Ray` which points at the center of a triangle // intersects it, unless it sees the back face, which is culled. #[test] @@ -575,28 +332,4 @@ mod bench { } }); } - - // /// Benchmark for the naive intersection algorithm. - // #[bench] - // fn bench_intersects_aabb_naive(b: &mut Bencher) { - // let (ray, boxes) = random_ray_and_boxes(); - - // b.iter(|| { - // for aabb in &boxes { - // black_box(ray.intersects_aabb_naive(aabb)); - // } - // }); - // } - - // /// Benchmark for the branchless intersection algorithm. - // #[bench] - // fn bench_intersects_aabb_branchless(b: &mut Bencher) { - // let (ray, boxes) = random_ray_and_boxes(); - - // b.iter(|| { - // for aabb in &boxes { - // black_box(ray.intersects_aabb_branchless(aabb)); - // } - // }); - // } } diff --git a/src/utils.rs b/src/utils.rs index d783de9..90916f7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -6,6 +6,56 @@ use num::Float; use crate::aabb::AABB; use crate::bounding_hierarchy::BHShape; +/// Fast floating point minimum. This function matches the semantics of +/// +/// ```no_compile +/// if x < y { x } else { y } +/// ``` +/// +/// which has efficient instruction sequences on many platforms (1 instruction on x86). For most +/// values, it matches the semantics of `x.min(y)`; the special cases are: +/// +/// ```text +/// min(-0.0, +0.0); +0.0 +/// min(+0.0, -0.0): -0.0 +/// min( NaN, 1.0): 1.0 +/// min( 1.0, NaN): NaN +/// ``` +#[inline(always)] +#[allow(dead_code)] +pub fn fast_min(x: T, y: T) -> T { + if x < y { + x + } else { + y + } +} + +/// Fast floating point maximum. This function matches the semantics of +/// +/// ```no_compile +/// if x > y { x } else { y } +/// ``` +/// +/// which has efficient instruction sequences on many platforms (1 instruction on x86). For most +/// values, it matches the semantics of `x.max(y)`; the special cases are: +/// +/// ```text +/// max(-0.0, +0.0); +0.0 +/// max(+0.0, -0.0): -0.0 +/// max( NaN, 1.0): 1.0 +/// max( 1.0, NaN): NaN +/// ``` +#[inline(always)] +#[allow(dead_code)] +pub fn fast_max(x: T, y: T) -> T { + if x > y { + x + } else { + y + } +} + /// Concatenates the list of vectors into a single vector. /// Drains the elements from the source `vectors`. pub fn concatenate_vectors(vectors: &mut [Vec]) -> Vec { From 28fdd22e7e295721a6cfaf39c0973186cadecc0c Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Mon, 24 Apr 2023 21:30:27 -0700 Subject: [PATCH 05/20] Update CI configurations and rename full_simd to simd --- .github/workflows/ci.yml | 77 +++++++++++++++++++++++++++--------- Cargo.toml | 2 +- src/flat_bvh.rs | 2 +- src/lib.rs | 3 +- src/ray/intersect_default.rs | 4 +- src/ray/intersect_x86_64.rs | 2 +- src/ray/mod.rs | 6 +-- 7 files changed, 68 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea367a8..6315cba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,37 @@ name: CI on: [push, pull_request] jobs: - ci: - name: CI with ${{ matrix.rust }} on ${{ matrix.os }} + check-fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + components: rustfmt + - name: Check formatting + run: cargo fmt --all -- --check + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + override: true + components: clippy + - name: Run clippy + with: + command: clippy + args: --workspace --all-targets --all-features -- -D warnings + + build-and-test-no-features: + name: CI with ${{ matrix.rust }} on ${{ matrix.os }} [no-features] runs-on: ${{ matrix.os }} strategy: matrix: @@ -21,7 +50,6 @@ jobs: profile: minimal toolchain: ${{ matrix.rust }} override: true - components: rustfmt, clippy - name: cargo build uses: actions-rs/cargo@v1 @@ -34,23 +62,36 @@ jobs: with: command: test - - name: cargo fmt - uses: actions-rs/cargo@v1 + - name: Upload coverage report to codecov.io + uses: codecov/codecov-action@v1 + if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable' + + build-and-test-simd: + name: CI with nightly on ${{ matrix.os }} [simd] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 with: - command: fmt - args: --all -- --check + profile: minimal + toolchain: nightly + override: true - - name: cargo clippy + - name: cargo build uses: actions-rs/cargo@v1 with: - command: clippy - args: --workspace --all-targets --all-features -- -D warnings - if: matrix.rust == 'nightly' - - # - name: Run cargo-tarpaulin - # uses: actions-rs/tarpaulin@v0.1 - # if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable' + command: build + args: --workspace --features simd - - name: Upload coverage report to codecov.io - uses: codecov/codecov-action@v1 - if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable' + - name: cargo test + uses: actions-rs/cargo@v1 + with: + command: test + args: --features simd diff --git a/Cargo.toml b/Cargo.toml index 3ebc199..30c8f19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ doc-comment = "0.3" [features] bench = [] -full_simd = [] +simd = [] [profile.release] lto = true diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index 102b009..4848448 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -462,7 +462,7 @@ mod bench { use crate::testbase::{ build_1200_triangles_bh, build_120k_triangles_bh, build_12k_triangles_bh, create_n_cubes, default_bounds, intersect_1200_triangles_bh, intersect_120k_triangles_bh, - intersect_12k_triangles_bh, BVH, FlatBVH + intersect_12k_triangles_bh, FlatBVH, BVH, }; #[bench] diff --git a/src/lib.rs b/src/lib.rs index 0680b6b..be67b07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,8 +71,7 @@ #![deny(missing_docs)] #![cfg_attr(feature = "bench", feature(test))] - -#![cfg_attr(feature = "full_simd", feature(min_specialization))] +#![cfg_attr(feature = "simd", feature(min_specialization))] #[cfg(all(feature = "bench", test))] extern crate test; diff --git a/src/ray/intersect_default.rs b/src/ray/intersect_default.rs index 094cd2a..8180bc2 100644 --- a/src/ray/intersect_default.rs +++ b/src/ray/intersect_default.rs @@ -10,7 +10,7 @@ where fn ray_intersects_aabb(&self, aabb: &AABB) -> bool; } -#[cfg(not(feature = "full_simd"))] +#[cfg(not(feature = "simd"))] impl RayIntersection for Ray where T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, @@ -28,7 +28,7 @@ where } } -#[cfg(all(feature = "full_simd", target_arch = "x86_64"))] +#[cfg(all(feature = "simd", target_arch = "x86_64"))] impl RayIntersection for Ray where T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, diff --git a/src/ray/intersect_x86_64.rs b/src/ray/intersect_x86_64.rs index 8522428..ca10dac 100644 --- a/src/ray/intersect_x86_64.rs +++ b/src/ray/intersect_x86_64.rs @@ -280,4 +280,4 @@ impl RayIntersection for Ray { ray_intersects_aabb_m256d(ro, ri, aabb_0, aabb_1) } -} \ No newline at end of file +} diff --git a/src/ray/mod.rs b/src/ray/mod.rs index b58415c..44ccdd3 100644 --- a/src/ray/mod.rs +++ b/src/ray/mod.rs @@ -1,9 +1,9 @@ //! This module holds the ray definition, and overloads for x86_64 simd operations -mod ray_impl; mod intersect_default; +mod ray_impl; -#[cfg(all(feature = "full_simd", target_arch = "x86_64"))] +#[cfg(all(feature = "simd", target_arch = "x86_64"))] mod intersect_x86_64; -pub use self::ray_impl::*; \ No newline at end of file +pub use self::ray_impl::*; From 6ddbaf97c87c0b6efd967d824fe5bb4b6f6f63fc Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Mon, 24 Apr 2023 21:41:51 -0700 Subject: [PATCH 06/20] Update README --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index bac2311..be1c17f 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,18 @@ let bvh = BVH::build(&mut spheres); let hit_sphere_aabbs = bvh.traverse(&ray, &spheres); ``` +## Explicit SIMD + +This crate features some manually written SIMD instructions, currently only for the `x86_64` architecture. +While nalgebra provides us with generic SIMD optimization (and it does a great job for the most part) - +some important functions, such as ray-aabb-intersection have been optimized by hand. + +The currently optimized intersections for ray-aabb are: +Type: f32, Dimension: 2,3,4 +Type: f64, Dimension: 2,3,4 + +To enable these optimziations, you must build with the `nightly` toolchain and enable the `simd` flag. + ## Optimization This crate provides BVH updating, which is also called optimization. With BVH optimization From ccfdc2891005bf447be215a07e2faff253baa235 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Mon, 24 Apr 2023 21:45:15 -0700 Subject: [PATCH 07/20] Fix CI builds --- .github/workflows/ci.yml | 54 ++++++++---------------------------- src/ray/intersect_default.rs | 2 +- src/ray/intersect_x86_64.rs | 2 +- 3 files changed, 14 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6315cba..0ff1233 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,30 +7,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Setup Rust toolchain - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: nightly - override: true components: rustfmt - name: Check formatting run: cargo fmt --all -- --check + clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Setup Rust toolchain - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: nightly - override: true components: clippy - name: Run clippy - with: - command: clippy - args: --workspace --all-targets --all-features -- -D warnings + run: cargo clippy --workspace --all-targets --all-features -- -D warnings build-and-test-no-features: name: CI with ${{ matrix.rust }} on ${{ matrix.os }} [no-features] @@ -41,26 +34,16 @@ jobs: rust: [stable, nightly] steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Rust toolchain - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} - override: true - name: cargo build - uses: actions-rs/cargo@v1 - with: - command: build - args: --workspace + run: cargo build --workspace - name: cargo test - uses: actions-rs/cargo@v1 - with: - command: test + run: cargo test - name: Upload coverage report to codecov.io uses: codecov/codecov-action@v1 @@ -74,24 +57,11 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup Rust toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly - name: cargo build - uses: actions-rs/cargo@v1 - with: - command: build - args: --workspace --features simd + run: cargo build --workspace --features simd - name: cargo test - uses: actions-rs/cargo@v1 - with: - command: test - args: --features simd + run: cargo test --features simd diff --git a/src/ray/intersect_default.rs b/src/ray/intersect_default.rs index 8180bc2..6b5a0bd 100644 --- a/src/ray/intersect_default.rs +++ b/src/ray/intersect_default.rs @@ -42,6 +42,6 @@ where let tmin = inf.max(); let tmax = sup.min(); - tmax > max(tmin, T::zero()) + tmax > fast_max(tmin, T::zero()) } } diff --git a/src/ray/intersect_x86_64.rs b/src/ray/intersect_x86_64.rs index ca10dac..dfb5697 100644 --- a/src/ray/intersect_x86_64.rs +++ b/src/ray/intersect_x86_64.rs @@ -137,7 +137,7 @@ impl ToRegisterType for SVector { fn max_elem_m128d(mm: __m128d) -> f64 { unsafe { let a = _mm_unpacklo_pd(mm, mm); // x x - let b = _mm_max_pd(mm, b); // max(x, x), max(x, y) + let b = _mm_max_pd(mm, a); // max(x, x), max(x, y) #[allow(invalid_value)] let mut data: [f64; 2] = std::mem::MaybeUninit::uninit().assume_init(); _mm_store_pd(data.as_mut_ptr(), b); From 2ddfac47b990622e71b1ccb0a79d46488a56d303 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Wed, 26 Apr 2023 00:34:13 -0700 Subject: [PATCH 08/20] Fix Serde --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 30c8f19..199e3aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ rand = "0.8" log = "0.4" serde = { optional = true, version = "1", features = ["derive"] } num = "0.4.0" -nalgebra = { version = "0.32.2", features = ["default", "serde"] } +nalgebra = { version = "0.32.2", features = ["default", "serde-serialize"] } [dev-dependencies] proptest = "1.0" From 7ea3a1b9dc3d8b9f696fad6e7e9e58c062cc18cc Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Wed, 26 Apr 2023 01:04:09 -0700 Subject: [PATCH 09/20] Fix Clippy errors, including renaming of all uppercase names (Bvh, BvhNode) --- Cargo.toml | 2 +- README.md | 54 +++--- examples/simple.rs | 10 +- src/aabb.rs | 330 +++++++++++++++++------------------ src/bounding_hierarchy.rs | 34 ++-- src/bvh/bvh_impl.rs | 298 +++++++++++++++---------------- src/bvh/iter.rs | 94 +++++----- src/bvh/mod.rs | 4 +- src/bvh/optimization.rs | 220 +++++++++++------------ src/flat_bvh.rs | 176 +++++++++---------- src/lib.rs | 18 +- src/ray/intersect_default.rs | 8 +- src/ray/intersect_x86_64.rs | 62 +++---- src/ray/ray_impl.rs | 50 +++--- src/testbase.rs | 136 +++++++-------- src/utils.rs | 18 +- 16 files changed, 757 insertions(+), 757 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 199e3aa..5fa7c41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bvh" -description = "A fast BVH using SAH" +description = "A fast Bvh using SAH" version = "0.7.2" edition = "2018" authors = [ diff --git a/README.md b/README.md index be1c17f..08be538 100644 --- a/README.md +++ b/README.md @@ -12,19 +12,19 @@ volume hierarchies.** ## About This crate can be used for applications which contain intersection computations of rays -with primitives. For this purpose a binary tree BVH (Bounding Volume Hierarchy) is of great -use if the scene which the ray traverses contains a huge number of primitives. With a BVH the +with primitives. For this purpose a binary tree Bvh (Bounding Volume Hierarchy) is of great +use if the scene which the ray traverses contains a huge number of primitives. With a Bvh the intersection test complexity is reduced from O(n) to O(log2(n)) at the cost of building -the BVH once in advance. This technique is especially useful in ray/path tracers. For +the Bvh once in advance. This technique is especially useful in ray/path tracers. For use in a shader this module also exports a flattening procedure, which allows for -iterative traversal of the BVH. +iterative traversal of the Bvh. ## Example ```rust -use bvh::aabb::{AABB, Bounded}; +use bvh::aabb::{Aabb, Bounded}; use bvh::bounding_hierarchy::BHShape; -use bvh::bvh::BVH; +use bvh::bvh::Bvh; use bvh::ray::Ray; use nalgebra::{Point3, Vector3}; @@ -39,11 +39,11 @@ struct Sphere { } impl Bounded for Sphere { - fn aabb(&self) -> AABB { + fn aabb(&self) -> Aabb { let half_size = Vector3::new(self.radius, self.radius, self.radius); let min = self.position - half_size; let max = self.position + half_size; - AABB::with_bounds(min, max) + Aabb::with_bounds(min, max) } } @@ -67,7 +67,7 @@ for i in 0..1000u32 { }); } -let bvh = BVH::build(&mut spheres); +let bvh = Bvh::build(&mut spheres); let hit_sphere_aabbs = bvh.traverse(&ray, &spheres); ``` @@ -85,8 +85,8 @@ To enable these optimziations, you must build with the `nightly` toolchain and e ## Optimization -This crate provides BVH updating, which is also called optimization. With BVH optimization -you can mutate the shapes on which the BVH is built and update the tree accordingly without rebuilding it completely. +This crate provides Bvh updating, which is also called optimization. With Bvh optimization +you can mutate the shapes on which the Bvh is built and update the tree accordingly without rebuilding it completely. This method is very useful when there are only very few changes to a huge scene. When the major part of the scene is static, it is faster to update the tree, instead of rebuilding it from scratch. @@ -95,11 +95,11 @@ it is faster to update the tree, instead of rebuilding it from scratch. First of all, optimizing is not helpful if more than half of the scene is not static. This is due to how optimizing takes place: Given a set of indices of all shapes which have changed, the optimize procedure tries to rotate fixed constellations -in search for a better surface area heuristic (SAH) value. This is done recursively from bottom to top while fixing the AABBs -in the inner nodes of the BVH. Which is why it is inefficient to update the BVH in comparison to rebuilding, when a lot +in search for a better surface area heuristic (SAH) value. This is done recursively from bottom to top while fixing the Aabbs +in the inner nodes of the Bvh. Which is why it is inefficient to update the Bvh in comparison to rebuilding, when a lot of shapes have moved. -Another problem with updated BVHs is, that the resulting BVH is not optimal. Assume that the scene is composed of two major +Another problem with updated Bvhs is, that the resulting Bvh is not optimal. Assume that the scene is composed of two major groups separated by a large gap. When one shape moves from one group to another, the updating procedure will not be able to find a sequence of bottom-up rotations which inserts the shape deeply into the other branch. @@ -116,17 +116,17 @@ test testbase::bench_intersect_sponza_list ... ben This is the most naive approach to intersecting a scene with a ray. It defines the baseline. -### Intersection via traversal of the list of triangles with AABB checks +### Intersection via traversal of the list of triangles with Aabb checks ```C test testbase::bench_intersect_120k_triangles_list_aabb ... bench: 229,088 ns/iter (+/- 6,727) test testbase::bench_intersect_sponza_list_aabb ... bench: 107,514 ns/iter (+/- 1,511) ``` -AABB checks are cheap, compared to triangle-intersection algorithms. Therefore, preceeding AABB checks +Aabb checks are cheap, compared to triangle-intersection algorithms. Therefore, preceeding Aabb checks increase intersection speed by filtering negative results a lot faster. -### Build of a BVH from scratch +### Build of a Bvh from scratch ```C test flat_bvh::bench::bench_build_1200_triangles_flat_bvh ... bench: 538,474 ns/iter (+/- 4,001) @@ -138,17 +138,17 @@ test bvh::bvh::bench::bench_build_120k_triangles_bvh ... ben test bvh::bvh::bench::bench_build_sponza_bvh ... bench: 46,802,305 ns/iter (+/- 184,644) ``` -### Flatten a BVH +### Flatten a Bvh ```C test flat_bvh::bench::bench_flatten_120k_triangles_bvh ... bench: 3,891,505 ns/iter (+/- 42,360) ``` -As you can see, building a BVH takes a long time. Building a BVH is only useful if the number of intersections performed on the +As you can see, building a Bvh takes a long time. Building a Bvh is only useful if the number of intersections performed on the scene exceeds the build duration. This is the case in applications such as ray and path tracing, where the minimum number of intersections is `1280 * 720` for an HD image. -### Intersection via BVH traversal +### Intersection via Bvh traversal ```C test flat_bvh::bench::bench_intersect_1200_triangles_flat_bvh ... bench: 168 ns/iter (+/- 2) @@ -163,7 +163,7 @@ test ray::bench::bench_intersects_aabb_branchless ... ben test ray::bench::bench_intersects_aabb_naive ... bench: 34,958 ns/iter (+/- 259) ``` -These performance measurements show that traversing a BVH is much faster than traversing a list. +These performance measurements show that traversing a Bvh is much faster than traversing a list. ### Optimization @@ -180,13 +180,13 @@ test bvh::optimization::bench::bench_randomize_120k_50p ... ben This is the place where you have to differentiate between rebuilding the tree from scratch or trying to optimize the old one. These tests show the impact of moving around a particular percentage of shapes (`10p` => `10%`). It is important to note that the randomization process here moves triangles around indiscriminately. -This will also lead to cases where the BVH would have to be restructured completely. +This will also lead to cases where the Bvh would have to be restructured completely. ### Intersection after the optimization -These intersection tests are grouped by dataset and by the BVH generation method. -* `_after_optimize` uses a BVH which was kept up to date with calls to `optimize`, while -* `_with_rebuild` uses the same triangle data as `_after_optimize`, but constructs a BVH from scratch. +These intersection tests are grouped by dataset and by the Bvh generation method. +* `_after_optimize` uses a Bvh which was kept up to date with calls to `optimize`, while +* `_with_rebuild` uses the same triangle data as `_after_optimize`, but constructs a Bvh from scratch. *120K Triangles* ```C @@ -218,9 +218,9 @@ This set of tests shows the impact of randomly moving triangles around and produ The *120K Triangles* dataset has been updated randomly. The *Sponza* scene was updated using a method which has a maximum offset distance for shapes. This simulates a more realistic scenario. -We also see that the *Sponza* scene by itself contains some structures which can be tightly wrapped in a BVH. +We also see that the *Sponza* scene by itself contains some structures which can be tightly wrapped in a Bvh. By mowing those structures around we destroy the locality of the triangle groups which leads to more branches in the -BVH requiring a check, thus leading to a higher intersection duration. +Bvh requiring a check, thus leading to a higher intersection duration. ### Running the benchmark suite diff --git a/examples/simple.rs b/examples/simple.rs index d1e9af2..68f5142 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,6 +1,6 @@ -use bvh::aabb::{Bounded, AABB}; +use bvh::aabb::{Bounded, Aabb}; use bvh::bounding_hierarchy::BHShape; -use bvh::bvh::BVH; +use bvh::bvh::Bvh; use bvh::ray::Ray; use nalgebra::{Point, SVector}; @@ -12,11 +12,11 @@ struct Sphere { } impl Bounded for Sphere { - fn aabb(&self) -> AABB { + fn aabb(&self) -> Aabb { let half_size = SVector::::new(self.radius, self.radius, self.radius); let min = self.position - half_size; let max = self.position + half_size; - AABB::with_bounds(min, max) + Aabb::with_bounds(min, max) } } @@ -41,7 +41,7 @@ pub fn main() { node_index: 0, }); } - let bvh = BVH::build(&mut spheres); + let bvh = Bvh::build(&mut spheres); let origin = Point::::new(0.0, 0.0, 0.0); let direction = SVector::::new(1.0, 0.0, 0.0); diff --git a/src/aabb.rs b/src/aabb.rs index b93bd22..b9bd1b1 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -16,11 +16,11 @@ use num::One; use num::Signed; use num::Zero; -/// AABB struct. +/// Aabb struct. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(clippy::upper_case_acronyms)] -pub struct AABB { +pub struct Aabb { /// Minimum coordinates pub min: Point, @@ -28,31 +28,31 @@ pub struct AABB { pub max: Point, } -impl fmt::Display for AABB { +impl fmt::Display for Aabb { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Min bound: {}; Max bound: {}", self.min, self.max) } } -/// A trait implemented by things which can be bounded by an [`AABB`]. +/// A trait implemented by things which can be bounded by an [`Aabb`]. /// -/// [`AABB`]: struct.AABB.html +/// [`Aabb`]: struct.Aabb.html /// pub trait Bounded { - /// Returns the geometric bounds of this object in the form of an [`AABB`]. + /// Returns the geometric bounds of this object in the form of an [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::{AABB, Bounded}; + /// use bvh::aabb::{Aabb, Bounded}; /// use nalgebra::Point3; /// /// struct Something; /// /// impl Bounded for Something { - /// fn aabb(&self) -> AABB { + /// fn aabb(&self) -> Aabb { /// let point1 = Point3::new(0.0,0.0,0.0); /// let point2 = Point3::new(1.0,1.0,1.0); - /// AABB::with_bounds(point1, point2) + /// Aabb::with_bounds(point1, point2) /// } /// } /// @@ -63,56 +63,56 @@ pub trait Bounded { /// assert!(aabb.contains(&Point3::new(1.0,1.0,1.0))); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// - fn aabb(&self) -> AABB; + fn aabb(&self) -> Aabb; } impl> Bounded for &B { - fn aabb(&self) -> AABB { + fn aabb(&self) -> Aabb { B::aabb(self) } } impl> Bounded for &mut B { - fn aabb(&self) -> AABB { + fn aabb(&self) -> Aabb { B::aabb(self) } } impl> Bounded for Box { - fn aabb(&self) -> AABB { + fn aabb(&self) -> Aabb { B::aabb(self) } } -impl AABB { - /// Creates a new [`AABB`] with the given bounds. +impl Aabb { + /// Creates a new [`Aabb`] with the given bounds. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb = AABB::with_bounds(Point3::new(-1.0,-1.0,-1.0), Point3::new(1.0,1.0,1.0)); + /// let aabb = Aabb::with_bounds(Point3::new(-1.0,-1.0,-1.0), Point3::new(1.0,1.0,1.0)); /// assert_eq!(aabb.min.x, -1.0); /// assert_eq!(aabb.max.z, 1.0); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn with_bounds(min: Point, max: Point) -> Self { - AABB { min, max } + Aabb { min, max } } - /// Creates a new empty [`AABB`]. + /// Creates a new empty [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// /// # fn main() { - /// let aabb = AABB::::empty(); + /// let aabb = Aabb::::empty(); /// let min = &aabb.min; /// let max = &aabb.max; /// @@ -121,13 +121,13 @@ impl AABB { /// let y = rand::random(); /// let z = rand::random(); /// - /// // An empty AABB should not contain it + /// // An empty Aabb should not contain it /// assert!(x < min.x && y < min.y && z < min.z); /// assert!(max.x < x && max.y < y && max.z < z); /// # } /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn empty() -> Self where @@ -139,14 +139,14 @@ impl AABB { } } - /// Creates a new infinite [`AABB`]. + /// Creates a new infinite [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// /// # fn main() { - /// let aabb :AABB = AABB::infinite(); + /// let aabb :Aabb = Aabb::infinite(); /// let min = &aabb.min; /// let max = &aabb.max; /// @@ -155,13 +155,13 @@ impl AABB { /// let y = rand::random(); /// let z = rand::random(); /// - /// // An infintie AABB should contain it + /// // An infintie Aabb should contain it /// assert!(x > min.x && y > min.y && z > min.z); /// assert!(max.x > x && max.y > y && max.z > z); /// # } /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn infinite() -> Self where @@ -173,14 +173,14 @@ impl AABB { } } - /// Returns true if the [`Point3`] is inside the [`AABB`]. + /// Returns true if the [`Point3`] is inside the [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); + /// let aabb = Aabb::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_inside = Point3::new(0.125, -0.25, 0.5); /// let point_outside = Point3::new(1.0, -2.0, 4.0); /// @@ -188,7 +188,7 @@ impl AABB { /// assert!(!aabb.contains(&point_outside)); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// [`Point3`]: nalgebra::Vec3 /// pub fn contains(&self, p: &Point) -> bool @@ -198,15 +198,15 @@ impl AABB { p >= &self.min && p <= &self.max } - /// Returns true if the [`Point3`] is approximately inside the [`AABB`] + /// Returns true if the [`Point3`] is approximately inside the [`Aabb`] /// with respect to some `epsilon`. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); + /// let aabb = Aabb::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_barely_outside = Point3::new(1.000_000_1, -1.000_000_1, 1.000_000_001); /// let point_outside = Point3::new(1.0, -2.0, 4.0); /// @@ -214,7 +214,7 @@ impl AABB { /// assert!(!aabb.approx_contains_eps(&point_outside, 0.00001)); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// [`Point3`]: nalgebra::Point3 /// pub fn approx_contains_eps(&self, p: &Point, epsilon: T) -> bool @@ -226,24 +226,24 @@ impl AABB { && (p - self.max) < SVector::from_element(epsilon) } - /// Returns true if the `other` [`AABB`] is approximately inside this [`AABB`] + /// Returns true if the `other` [`Aabb`] is approximately inside this [`Aabb`] /// with respect to some `epsilon`. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); + /// let aabb = Aabb::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_barely_outside = Point3::new(1.000_000_1, 1.000_000_1, 1.000_000_1); /// let center = aabb.center(); - /// let inner_aabb = AABB::with_bounds(center, point_barely_outside); + /// let inner_aabb = Aabb::with_bounds(center, point_barely_outside); /// /// assert!(aabb.approx_contains_aabb_eps(&inner_aabb, 0.00001)); /// ``` /// - /// [`AABB`]: struct.AABB.html - pub fn approx_contains_aabb_eps(&self, other: &AABB, epsilon: T) -> bool + /// [`Aabb`]: struct.Aabb.html + pub fn approx_contains_aabb_eps(&self, other: &Aabb, epsilon: T) -> bool where T: ClosedSub + PartialOrd + Float, { @@ -251,24 +251,24 @@ impl AABB { && self.approx_contains_eps(&other.max, epsilon) } - /// Returns true if the `other` [`AABB`] is approximately equal to this [`AABB`] + /// Returns true if the `other` [`Aabb`] is approximately equal to this [`Aabb`] /// with respect to some `epsilon`. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb = AABB::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); + /// let aabb = Aabb::with_bounds(Point3::new(-1.0, -1.0, -1.0), Point3::new(1.0, 1.0, 1.0)); /// let point_barely_outside_min = Point3::new(-1.000_000_1, -1.000_000_1, -1.000_000_1); /// let point_barely_outside_max = Point3::new(1.000_000_1, 1.000_000_1, 1.000_000_1); - /// let other = AABB::with_bounds(point_barely_outside_min, point_barely_outside_max); + /// let other = Aabb::with_bounds(point_barely_outside_min, point_barely_outside_max); /// /// assert!(aabb.relative_eq(&other, 0.00001)); /// ``` /// - /// [`AABB`]: struct.AABB.html - pub fn relative_eq(&self, other: &AABB, epsilon: T) -> bool + /// [`Aabb`]: struct.Aabb.html + pub fn relative_eq(&self, other: &Aabb, epsilon: T) -> bool where T: ClosedSub + PartialOrd + Signed, { @@ -276,16 +276,16 @@ impl AABB { (self.min - other.min).abs() < ep_vec && (self.max - other.max).abs() < ep_vec } - /// Returns a new minimal [`AABB`] which contains both this [`AABB`] and `other`. - /// The result is the convex hull of the both [`AABB`]s. + /// Returns a new minimal [`Aabb`] which contains both this [`Aabb`] and `other`. + /// The result is the convex hull of the both [`Aabb`]s. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb1 = AABB::with_bounds(Point3::new(-101.0, 0.0, 0.0), Point3::new(-100.0, 1.0, 1.0)); - /// let aabb2 = AABB::with_bounds(Point3::new(100.0, 0.0, 0.0), Point3::new(101.0, 1.0, 1.0)); + /// let aabb1 = Aabb::with_bounds(Point3::new(-101.0, 0.0, 0.0), Point3::new(-100.0, 1.0, 1.0)); + /// let aabb2 = Aabb::with_bounds(Point3::new(100.0, 0.0, 0.0), Point3::new(101.0, 1.0, 1.0)); /// let joint = aabb1.join(&aabb2); /// /// let point_inside_aabb1 = Point3::new(-100.5, 0.5, 0.5); @@ -305,31 +305,31 @@ impl AABB { /// assert!(joint.contains(&point_inside_joint)); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// - pub fn join(&self, other: &AABB) -> AABB + pub fn join(&self, other: &Aabb) -> Aabb where T: SimdPartialOrd, { - AABB::with_bounds( + Aabb::with_bounds( self.min.coords.inf(&other.min.coords).into(), self.max.coords.sup(&other.max.coords).into(), ) } - /// Mutable version of [`AABB::join`]. + /// Mutable version of [`Aabb::join`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::{Point3, Vector3}; /// /// let size = Vector3::new(1.0, 1.0, 1.0); /// let aabb_pos = Point3::new(-101.0, 0.0, 0.0); - /// let mut aabb = AABB::with_bounds(aabb_pos, aabb_pos + size); + /// let mut aabb = Aabb::with_bounds(aabb_pos, aabb_pos + size); /// /// let other_pos = Point3::new(100.0, 0.0, 0.0); - /// let other = AABB::with_bounds(other_pos, other_pos + size); + /// let other = Aabb::with_bounds(other_pos, other_pos + size); /// /// let point_inside_aabb = aabb_pos + size / 2.0; /// let point_inside_other = other_pos + size / 2.0; @@ -350,28 +350,28 @@ impl AABB { /// assert!(aabb.contains(&point_inside_joint)); /// ``` /// - /// [`AABB::join`]: struct.AABB.html + /// [`Aabb::join`]: struct.Aabb.html /// - pub fn join_mut(&mut self, other: &AABB) + pub fn join_mut(&mut self, other: &Aabb) where T: SimdPartialOrd, { *self = self.join(other); } - /// Returns a new minimal [`AABB`] which contains both - /// this [`AABB`] and the [`Point3`] `other`. + /// Returns a new minimal [`Aabb`] which contains both + /// this [`Aabb`] and the [`Point3`] `other`. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let point1 = Point3::new(0.0, 0.0, 0.0); /// let point2 = Point3::new(1.0, 1.0, 1.0); /// let point3 = Point3::new(2.0, 2.0, 2.0); /// - /// let aabb = AABB::empty(); + /// let aabb = Aabb::empty(); /// assert!(!aabb.contains(&point1)); /// /// let aabb1 = aabb.grow(&point1); @@ -382,31 +382,31 @@ impl AABB { /// assert!(!aabb2.contains(&point3)); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// [`Point3`]: nalgebra::Point3 /// - pub fn grow(&self, other: &Point) -> AABB + pub fn grow(&self, other: &Point) -> Aabb where T: SimdPartialOrd, { - AABB::with_bounds( + Aabb::with_bounds( self.min.coords.inf(&other.coords).into(), self.max.coords.sup(&other.coords).into(), ) } - /// Mutable version of [`AABB::grow`]. + /// Mutable version of [`Aabb::grow`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let point1 = Point3::new(0.0, 0.0, 0.0); /// let point2 = Point3::new(1.0, 1.0, 1.0); /// let point3 = Point3::new(2.0, 2.0, 2.0); /// - /// let mut aabb = AABB::empty(); + /// let mut aabb = Aabb::empty(); /// assert!(!aabb.contains(&point1)); /// /// aabb.grow_mut(&point1); @@ -418,7 +418,7 @@ impl AABB { /// assert!(!aabb.contains(&point3)); /// ``` /// - /// [`AABB::grow`]: struct.AABB.html + /// [`Aabb::grow`]: struct.Aabb.html /// [`Point3`]: nalgebra::Point3 /// pub fn grow_mut(&mut self, other: &Point) @@ -428,25 +428,25 @@ impl AABB { *self = self.grow(other); } - /// Returns a new minimal [`AABB`] which contains both this [`AABB`] and the [`Bounded`] + /// Returns a new minimal [`Aabb`] which contains both this [`Aabb`] and the [`Bounded`] /// `other`. /// /// # Examples /// ``` - /// use bvh::aabb::{AABB, Bounded}; + /// use bvh::aabb::{Aabb, Bounded}; /// use nalgebra::Point3; /// /// struct Something; /// /// impl Bounded for Something { - /// fn aabb(&self) -> AABB { + /// fn aabb(&self) -> Aabb { /// let point1 = Point3::new(0.0,0.0,0.0); /// let point2 = Point3::new(1.0,1.0,1.0); - /// AABB::with_bounds(point1, point2) + /// Aabb::with_bounds(point1, point2) /// } /// } /// - /// let aabb = AABB::empty(); + /// let aabb = Aabb::empty(); /// let something = Something; /// let aabb1 = aabb.join_bounded(&something); /// @@ -454,29 +454,29 @@ impl AABB { /// assert!(aabb1.contains(¢er)); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// [`Bounded`]: trait.Bounded.html /// - pub fn join_bounded>(&self, other: &B) -> AABB + pub fn join_bounded>(&self, other: &B) -> Aabb where T: SimdPartialOrd, { self.join(&other.aabb()) } - /// Returns the size of this [`AABB`] in all three dimensions. + /// Returns the size of this [`Aabb`] in all three dimensions. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let aabb = AABB::with_bounds(Point3::new(-1.0,-1.0,-1.0), Point3::new(1.0,1.0,1.0)); + /// let aabb = Aabb::with_bounds(Point3::new(-1.0,-1.0,-1.0), Point3::new(1.0,1.0,1.0)); /// let size = aabb.size(); /// assert!(size.x == 2.0 && size.y == 2.0 && size.z == 2.0); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn size(&self) -> SVector where @@ -485,22 +485,22 @@ impl AABB { self.max - self.min } - /// Returns the center [`Point3`] of the [`AABB`]. + /// Returns the center [`Point3`] of the [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); /// - /// let aabb = AABB::with_bounds(min, max); + /// let aabb = Aabb::with_bounds(min, max); /// let center = aabb.center(); /// assert!(center.x == 42.0 && center.y == 42.0 && center.z == 42.0); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// [`Point3`]: nalgebra::Point3 /// pub fn center(&self) -> Point @@ -510,25 +510,25 @@ impl AABB { (self.min.coords + (self.size() * T::from_f32(0.5).unwrap())).into() } - /// An empty [`AABB`] is an [`AABB`] where the lower bound is greater than + /// An empty [`Aabb`] is an [`Aabb`] where the lower bound is greater than /// the upper bound in at least one component /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// - /// let empty_aabb: AABB = AABB::empty(); + /// let empty_aabb: Aabb = Aabb::empty(); /// assert!(empty_aabb.is_empty()); /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); /// - /// let aabb = AABB::with_bounds(min, max); + /// let aabb = Aabb::with_bounds(min, max); /// assert!(!aabb.is_empty()); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn is_empty(&self) -> bool where @@ -541,22 +541,22 @@ impl AABB { self.min.coords.sup(&self.max.coords) != self.max.coords } - /// Returns the total surface area of this [`AABB`]. + /// Returns the total surface area of this [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); /// - /// let aabb = AABB::with_bounds(min, max); + /// let aabb = Aabb::with_bounds(min, max); /// let surface_area = aabb.surface_area(); /// assert!(surface_area == 24.0); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn surface_area(&self) -> T where @@ -566,22 +566,22 @@ impl AABB { T::from_f32(2.0).unwrap() * size.dot(&size) } - /// Returns the volume of this [`AABB`]. + /// Returns the volume of this [`Aabb`]. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let min = Point3::new(41.0,41.0,41.0); /// let max = Point3::new(43.0,43.0,43.0); /// - /// let aabb = AABB::with_bounds(min, max); + /// let aabb = Aabb::with_bounds(min, max); /// let volume = aabb.volume(); /// assert!(volume == 8.0); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn volume(&self) -> T where @@ -590,22 +590,22 @@ impl AABB { self.size().product() } - /// Returns the axis along which the [`AABB`] is stretched the most. + /// Returns the axis along which the [`Aabb`] is stretched the most. /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let min = Point3::new(-100.0,0.0,0.0); /// let max = Point3::new(100.0,0.0,0.0); /// - /// let aabb = AABB::with_bounds(min, max); + /// let aabb = Aabb::with_bounds(min, max); /// let axis = aabb.largest_axis(); /// assert!(axis == 0); /// ``` /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub fn largest_axis(&self) -> usize where @@ -615,36 +615,36 @@ impl AABB { } } -/// Default instance for [`AABB`]s. Returns an [`AABB`] which is [`empty()`]. +/// Default instance for [`Aabb`]s. Returns an [`Aabb`] which is [`empty()`]. /// -/// [`AABB`]: struct.AABB.html +/// [`Aabb`]: struct.Aabb.html /// [`empty()`]: #method.empty /// -impl Default for AABB { - fn default() -> AABB { - AABB::empty() +impl Default for Aabb { + fn default() -> Aabb { + Aabb::empty() } } -/// Make [`AABB`]s indexable. `aabb[0]` gives a reference to the minimum bound. +/// Make [`Aabb`]s indexable. `aabb[0]` gives a reference to the minimum bound. /// All other indices return a reference to the maximum bound. /// /// # Examples /// ``` -/// use bvh::aabb::AABB; +/// use bvh::aabb::Aabb; /// use nalgebra::Point3; /// /// let min = Point3::new(3.0,4.0,5.0); /// let max = Point3::new(123.0,123.0,123.0); /// -/// let aabb = AABB::with_bounds(min, max); +/// let aabb = Aabb::with_bounds(min, max); /// assert_eq!(aabb[0], min); /// assert_eq!(aabb[1], max); /// ``` /// -/// [`AABB`]: struct.AABB.html +/// [`Aabb`]: struct.Aabb.html /// -impl Index for AABB { +impl Index for Aabb { type Output = Point; fn index(&self, index: usize) -> &Point { @@ -652,16 +652,16 @@ impl Index for AABB { } } -/// Implementation of [`Bounded`] for [`AABB`]. +/// Implementation of [`Bounded`] for [`Aabb`]. /// /// # Examples /// ``` -/// use bvh::aabb::{AABB, Bounded}; +/// use bvh::aabb::{Aabb, Bounded}; /// use nalgebra::Point3; /// /// let point_a = Point3::new(3.0,4.0,5.0); /// let point_b = Point3::new(17.0,18.0,19.0); -/// let aabb = AABB::empty().grow(&point_a).grow(&point_b); +/// let aabb = Aabb::empty().grow(&point_a).grow(&point_b); /// /// let aabb_aabb = aabb.aabb(); /// @@ -669,11 +669,11 @@ impl Index for AABB { /// assert_eq!(aabb_aabb.max, aabb.max); /// ``` /// -/// [`AABB`]: struct.AABB.html +/// [`Aabb`]: struct.Aabb.html /// [`Bounded`]: trait.Bounded.html /// -impl Bounded for AABB { - fn aabb(&self) -> AABB { +impl Bounded for Aabb { + fn aabb(&self) -> Aabb { *self } } @@ -682,7 +682,7 @@ impl Bounded for AABB { /// /// # Examples /// ``` -/// use bvh::aabb::{AABB, Bounded}; +/// use bvh::aabb::{Aabb, Bounded}; /// use nalgebra::Point3; /// /// let point = Point3::new(3.0,4.0,5.0); @@ -695,8 +695,8 @@ impl Bounded for AABB { /// [`Point3`]: nalgebra::Point3 /// impl Bounded for Point { - fn aabb(&self) -> AABB { - AABB::with_bounds(*self, *self) + fn aabb(&self) -> Aabb { + Aabb::with_bounds(*self, *self) } } @@ -704,50 +704,50 @@ impl Bounded for Point { mod tests { use crate::aabb::Bounded; use crate::testbase::{ - tuple_to_point, tuple_to_vector, tuplevec_large_strategy, Point3, TupleVec, Vector3, AABB, + tuple_to_point, tuple_to_vector, tuplevec_large_strategy, TPoint3, TupleVec, TVector3, TAabb3, }; use float_eq::assert_float_eq; use proptest::prelude::*; proptest! { - // Test whether an empty `AABB` does not contains anything. + // Test whether an empty `Aabb` does not contains anything. #[test] fn test_empty_contains_nothing(tpl: TupleVec) { // Define a random Point let p = tuple_to_point(&tpl); - // Create an empty AABB - let aabb = AABB::empty(); + // Create an empty Aabb + let aabb = TAabb3::empty(); // It should not contain anything assert!(!aabb.contains(&p)); } - // Test whether a default `AABB` is empty. + // Test whether a default `Aabb` is empty. #[test] fn test_default_is_empty(tpl: TupleVec) { // Define a random Point let p = tuple_to_point(&tpl); - // Create a default AABB - let aabb: AABB = Default::default(); + // Create a default Aabb + let aabb: TAabb3 = Default::default(); // It should not contain anything assert!(!aabb.contains(&p)); } - // Test whether an `AABB` always contains its center. + // Test whether an `Aabb` always contains its center. #[test] fn test_aabb_contains_center(a: TupleVec, b: TupleVec) { - // Define two points which will be the corners of the `AABB` + // Define two points which will be the corners of the `Aabb` let p1 = tuple_to_point(&a); let p2 = tuple_to_point(&b); - // Span the `AABB` - let aabb = AABB::empty().grow(&p1).join_bounded(&p2); + // Span the `Aabb` + let aabb = TAabb3::empty().grow(&p1).join_bounded(&p2); - // Its center should be inside the `AABB` + // Its center should be inside the `Aabb` assert!(aabb.contains(&aabb.center())); } @@ -760,14 +760,14 @@ mod tests { let points = [a.0, a.1, a.2, a.3, a.4, b.0, b.1, b.2, b.3, b.4]; // Convert these points to `Point3` - let points = points.iter().map(tuple_to_point).collect::>(); + let points = points.iter().map(tuple_to_point).collect::>(); - // Create two `AABB`s. One spanned the first five points, + // Create two `Aabb`s. One spanned the first five points, // the other by the last five points - let aabb1 = points.iter().take(5).fold(AABB::empty(), |aabb, point| aabb.grow(point)); - let aabb2 = points.iter().skip(5).fold(AABB::empty(), |aabb, point| aabb.grow(point)); + let aabb1 = points.iter().take(5).fold(TAabb3::empty(), |aabb, point| aabb.grow(point)); + let aabb2 = points.iter().skip(5).fold(TAabb3::empty(), |aabb, point| aabb.grow(point)); - // The `AABB`s should contain the points by which they are spanned + // The `Aabb`s should contain the points by which they are spanned let aabb1_contains_init_five = points.iter() .take(5) .all(|point| aabb1.contains(point)); @@ -775,7 +775,7 @@ mod tests { .skip(5) .all(|point| aabb2.contains(point)); - // Build the joint of the two `AABB`s + // Build the joint of the two `Aabb`s let aabbu = aabb1.join(&aabb2); // The joint should contain all points @@ -786,12 +786,12 @@ mod tests { assert!(aabb1_contains_init_five && aabb2_contains_last_five && aabbu_contains_all); } - // Test whether some points relative to the center of an AABB are classified correctly. + // Test whether some points relative to the center of an Aabb are classified correctly. // Currently doesn't test `approx_contains_eps` or `contains` very well due to scaling by 0.9 and 1.1. #[test] fn test_points_relative_to_center_and_size(a in tuplevec_large_strategy(), b in tuplevec_large_strategy()) { - // Generate some nonempty AABB - let aabb = AABB::empty() + // Generate some nonempty Aabb + let aabb = TAabb3::empty() .grow(&tuple_to_point(&a)) .grow(&tuple_to_point(&b)); @@ -800,11 +800,11 @@ mod tests { let size_half = size / 2.0; let center = aabb.center(); - // Compute the min and the max corners of the AABB by hand + // Compute the min and the max corners of the Aabb by hand let inside_ppp = center + size_half * 0.9; let inside_mmm = center - size_half * 0.9; - // Generate two points which are outside the AABB + // Generate two points which are outside the Aabb let outside_ppp = inside_ppp + size_half * 1.1; let outside_mmm = inside_mmm - size_half * 1.1; @@ -814,22 +814,22 @@ mod tests { assert!(!aabb.contains(&outside_mmm)); } - // Test whether the surface of a nonempty AABB is always positive. + // Test whether the surface of a nonempty Aabb is always positive. #[test] fn test_surface_always_positive(a: TupleVec, b: TupleVec) { - let aabb = AABB::empty() + let aabb = TAabb3::empty() .grow(&tuple_to_point(&a)) .grow(&tuple_to_point(&b)); assert!(aabb.surface_area() >= 0.0); } - // Compute and compare the surface area of an AABB by hand. + // Compute and compare the surface area of an Aabb by hand. #[test] fn test_surface_area_cube(pos: TupleVec, size in f32::EPSILON..10e30_f32) { - // Generate some non-empty AABB + // Generate some non-empty Aabb let pos = tuple_to_point(&pos); - let size_vec = Vector3::new(size, size, size); - let aabb = AABB::with_bounds(pos, pos + size_vec); + let size_vec = TVector3::new(size, size, size); + let aabb = TAabb3::with_bounds(pos, pos + size_vec); // Check its surface area let area_a = aabb.surface_area(); @@ -837,19 +837,19 @@ mod tests { assert_float_eq!(area_a, area_b, rmax <= f32::EPSILON); } - // Test whether the volume of a nonempty AABB is always positive. + // Test whether the volume of a nonempty Aabb is always positive. #[test] fn test_volume_always_positive(a in tuplevec_large_strategy(), b in tuplevec_large_strategy()) { - let aabb = AABB::empty() + let aabb = TAabb3::empty() .grow(&tuple_to_point(&a)) .grow(&tuple_to_point(&b)); assert!(aabb.volume() >= 0.0); } - // Compute and compare the volume of an AABB by hand. + // Compute and compare the volume of an Aabb by hand. #[test] fn test_volume_by_hand(pos in tuplevec_large_strategy(), size in tuplevec_large_strategy()) { - // Generate some non-empty AABB + // Generate some non-empty Aabb let pos = tuple_to_point(&pos); let size = tuple_to_vector(&size); let aabb = pos.aabb().grow(&(pos + size)); @@ -860,21 +860,21 @@ mod tests { assert_float_eq!(volume_a, volume_b, rmax <= f32::EPSILON); } - // Test whether generating an `AABB` from the min and max bounds yields the same `AABB`. + // Test whether generating an `Aabb` from the min and max bounds yields the same `Aabb`. #[test] fn test_create_aabb_from_indexable(a: TupleVec, b: TupleVec, p: TupleVec) { // Create a random point let point = tuple_to_point(&p); - // Create a random AABB - let aabb = AABB::empty() + // Create a random Aabb + let aabb = TAabb3::empty() .grow(&tuple_to_point(&a)) .grow(&tuple_to_point(&b)); - // Create an AABB by using the index-access method - let aabb_by_index = AABB::with_bounds(aabb[0], aabb[1]); + // Create an Aabb by using the index-access method + let aabb_by_index = TAabb3::with_bounds(aabb[0], aabb[1]); - // The AABBs should be the same + // The Aabbs should be the same assert!(aabb.contains(&point) == aabb_by_index.contains(&point)); } } diff --git a/src/bounding_hierarchy.rs b/src/bounding_hierarchy.rs index e23f059..7356406 100644 --- a/src/bounding_hierarchy.rs +++ b/src/bounding_hierarchy.rs @@ -53,7 +53,7 @@ pub trait BoundingHierarchy { /// # Examples /// /// ``` - /// use bvh::aabb::{AABB, Bounded}; + /// use bvh::aabb::{Aabb, Bounded}; /// use bvh::bounding_hierarchy::BoundingHierarchy; /// use nalgebra::{Point3, Vector3}; /// # use bvh::bounding_hierarchy::BHShape; @@ -74,10 +74,10 @@ pub trait BoundingHierarchy { /// # } /// # /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # fn aabb(&self) -> Aabb { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); - /// # AABB::with_bounds(min, max) + /// # Aabb::with_bounds(min, max) /// # } /// # } /// # @@ -101,16 +101,16 @@ pub trait BoundingHierarchy { /// # } /// /// let mut shapes = create_bhshapes(); - /// // Construct a normal `BVH`. + /// // Construct a normal `Bvh`. /// { - /// use bvh::bvh::BVH; - /// let bvh = BVH::build(&mut shapes); + /// use bvh::bvh::Bvh; + /// let bvh = Bvh::build(&mut shapes); /// } /// - /// // Or construct a `FlatBVH`. + /// // Or construct a `FlatBvh`. /// { - /// use bvh::flat_bvh::FlatBVH; - /// let bvh = FlatBVH::build(&mut shapes); + /// use bvh::flat_bvh::FlatBvh; + /// let bvh = FlatBvh::build(&mut shapes); /// } /// ``` /// @@ -119,14 +119,14 @@ pub trait BoundingHierarchy { fn build>(shapes: &mut [Shape]) -> Self; /// Traverses the [`BoundingHierarchy`]. - /// Returns a subset of `shapes`, in which the [`AABB`]s of the elements were hit by `ray`. + /// Returns a subset of `shapes`, in which the [`Aabb`]s of the elements were hit by `ray`. /// /// # Examples /// /// ``` - /// use bvh::aabb::{AABB, Bounded}; + /// use bvh::aabb::{Aabb, Bounded}; /// use bvh::bounding_hierarchy::BoundingHierarchy; - /// use bvh::bvh::BVH; + /// use bvh::bvh::Bvh; /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; @@ -147,10 +147,10 @@ pub trait BoundingHierarchy { /// # } /// # /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # fn aabb(&self) -> Aabb { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); - /// # AABB::with_bounds(min, max) + /// # Aabb::with_bounds(min, max) /// # } /// # } /// # @@ -164,13 +164,13 @@ pub trait BoundingHierarchy { /// # } /// # } /// # - /// # fn create_bvh() -> (BVH, Vec) { + /// # fn create_bvh() -> (Bvh, Vec) { /// # let mut shapes = Vec::new(); /// # for i in 0..1000 { /// # let position = Point3::new(i as f32, i as f32, i as f32); /// # shapes.push(UnitBox::new(i, position)); /// # } - /// # let bvh = BVH::build(&mut shapes); + /// # let bvh = Bvh::build(&mut shapes); /// # (bvh, shapes) /// # } /// @@ -183,7 +183,7 @@ pub trait BoundingHierarchy { /// ``` /// /// [`BoundingHierarchy`]: trait.BoundingHierarchy.html - /// [`AABB`]: ../aabb/struct.AABB.html + /// [`Aabb`]: ../aabb/struct.Aabb.html /// fn traverse<'a, Shape: BHShape>( &'a self, diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index 9f61535..e9bcdb7 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -1,32 +1,32 @@ -//! This module defines [`BVH`] and [`BVHNode`] and functions for building and traversing it. +//! This module defines [`Bvh`] and [`BvhNode`] and functions for building and traversing it. //! -//! [`BVH`]: struct.BVH.html -//! [`BVHNode`]: struct.BVHNode.html +//! [`Bvh`]: struct.Bvh.html +//! [`BvhNode`]: struct.BvhNode.html //! use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::{Float, FromPrimitive, Signed, ToPrimitive, Zero}; -use crate::aabb::{Bounded, AABB}; +use crate::aabb::{Bounded, Aabb}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; //use crate::bounds::ScalarType; -use crate::bvh::iter::BVHTraverseIterator; +use crate::bvh::iter::BvhTraverseIterator; use crate::ray::Ray; use crate::utils::{concatenate_vectors, joint_aabb_of_shapes, Bucket}; -/// The [`BVHNode`] enum that describes a node in a [`BVH`]. +/// The [`BvhNode`] enum that describes a node in a [`Bvh`]. /// It's either a leaf node and references a shape (by holding its index) /// or a regular node that has two child nodes. -/// The non-leaf node stores the [`AABB`]s of its children. +/// The non-leaf node stores the [`Aabb`]s of its children. /// -/// [`AABB`]: ../aabb/struct.AABB.html -/// [`BVH`]: struct.BVH.html -/// [`BVH`]: struct.BVHNode.html +/// [`Aabb`]: ../aabb/struct.Aabb.html +/// [`Bvh`]: struct.Bvh.html +/// [`Bvh`]: struct.BvhNode.html /// #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[allow(clippy::upper_case_acronyms)] -pub enum BVHNode { +pub enum BvhNode { /// Leaf node. Leaf { /// The node's parent. @@ -49,30 +49,30 @@ pub enum BVHNode { /// Index of the left subtree's root node. child_l_index: usize, - /// The convex hull of the shapes' `AABB`s in child_l. - child_l_aabb: AABB, + /// The convex hull of the shapes' `Aabb`s in child_l. + child_l_aabb: Aabb, /// Index of the right subtree's root node. child_r_index: usize, - /// The convex hull of the shapes' `AABB`s in child_r. - child_r_aabb: AABB, + /// The convex hull of the shapes' `Aabb`s in child_r. + child_r_aabb: Aabb, }, } -impl PartialEq for BVHNode { - // TODO Consider also comparing AABBs - fn eq(&self, other: &BVHNode) -> bool { +impl PartialEq for BvhNode { + // TODO Consider also comparing Aabbs + fn eq(&self, other: &BvhNode) -> bool { match (self, other) { ( - &BVHNode::Node { + &BvhNode::Node { parent_index: self_parent_index, depth: self_depth, child_l_index: self_child_l_index, child_r_index: self_child_r_index, .. }, - &BVHNode::Node { + &BvhNode::Node { parent_index: other_parent_index, depth: other_depth, child_l_index: other_child_l_index, @@ -86,12 +86,12 @@ impl PartialEq for BVHNode { && self_child_r_index == other_child_r_index } ( - &BVHNode::Leaf { + &BvhNode::Leaf { parent_index: self_parent_index, depth: self_depth, shape_index: self_shape_index, }, - &BVHNode::Leaf { + &BvhNode::Leaf { parent_index: other_parent_index, depth: other_depth, shape_index: other_shape_index, @@ -106,22 +106,22 @@ impl PartialEq for BVHNode { } } -impl BVHNode { +impl BvhNode { /// Returns the index of the parent node. pub fn parent(&self) -> usize { match *self { - BVHNode::Node { parent_index, .. } | BVHNode::Leaf { parent_index, .. } => parent_index, + BvhNode::Node { parent_index, .. } | BvhNode::Leaf { parent_index, .. } => parent_index, } } /// Returns a mutable reference to the parent node index. pub fn parent_mut(&mut self) -> &mut usize { match *self { - BVHNode::Node { + BvhNode::Node { ref mut parent_index, .. } - | BVHNode::Leaf { + | BvhNode::Leaf { ref mut parent_index, .. } => parent_index, @@ -131,78 +131,78 @@ impl BVHNode { /// Returns the index of the left child node. pub fn child_l(&self) -> usize { match *self { - BVHNode::Node { child_l_index, .. } => child_l_index, + BvhNode::Node { child_l_index, .. } => child_l_index, _ => panic!("Tried to get the left child of a leaf node."), } } - /// Returns the `AABB` of the right child node. - pub fn child_l_aabb(&self) -> AABB { + /// Returns the `Aabb` of the right child node. + pub fn child_l_aabb(&self) -> Aabb { match *self { - BVHNode::Node { child_l_aabb, .. } => child_l_aabb, + BvhNode::Node { child_l_aabb, .. } => child_l_aabb, _ => panic!(), } } - /// Returns a mutable reference to the `AABB` of the left child node. - pub fn child_l_aabb_mut(&mut self) -> &mut AABB { + /// Returns a mutable reference to the `Aabb` of the left child node. + pub fn child_l_aabb_mut(&mut self) -> &mut Aabb { match *self { - BVHNode::Node { + BvhNode::Node { ref mut child_l_aabb, .. } => child_l_aabb, - _ => panic!("Tried to get the left child's `AABB` of a leaf node."), + _ => panic!("Tried to get the left child's `Aabb` of a leaf node."), } } /// Returns the index of the right child node. pub fn child_r(&self) -> usize { match *self { - BVHNode::Node { child_r_index, .. } => child_r_index, + BvhNode::Node { child_r_index, .. } => child_r_index, _ => panic!("Tried to get the right child of a leaf node."), } } - /// Returns the `AABB` of the right child node. - pub fn child_r_aabb(&self) -> AABB { + /// Returns the `Aabb` of the right child node. + pub fn child_r_aabb(&self) -> Aabb { match *self { - BVHNode::Node { child_r_aabb, .. } => child_r_aabb, + BvhNode::Node { child_r_aabb, .. } => child_r_aabb, _ => panic!(), } } - /// Returns a mutable reference to the `AABB` of the right child node. - pub fn child_r_aabb_mut(&mut self) -> &mut AABB { + /// Returns a mutable reference to the `Aabb` of the right child node. + pub fn child_r_aabb_mut(&mut self) -> &mut Aabb { match *self { - BVHNode::Node { + BvhNode::Node { ref mut child_r_aabb, .. } => child_r_aabb, - _ => panic!("Tried to get the right child's `AABB` of a leaf node."), + _ => panic!("Tried to get the right child's `Aabb` of a leaf node."), } } /// Returns the depth of the node. The root node has depth `0`. pub fn depth(&self) -> u32 { match *self { - BVHNode::Node { depth, .. } | BVHNode::Leaf { depth, .. } => depth, + BvhNode::Node { depth, .. } | BvhNode::Leaf { depth, .. } => depth, } } - /// Gets the `AABB` for a `BVHNode`. - /// Returns the shape's `AABB` for leaves, and the joined `AABB` of - /// the two children's `AABB`s for non-leaves. - pub fn get_node_aabb>(&self, shapes: &[Shape]) -> AABB + /// Gets the `Aabb` for a `BvhNode`. + /// Returns the shape's `Aabb` for leaves, and the joined `Aabb` of + /// the two children's `Aabb`s for non-leaves. + pub fn get_node_aabb>(&self, shapes: &[Shape]) -> Aabb where T: SimdPartialOrd, { match *self { - BVHNode::Node { + BvhNode::Node { child_l_aabb, child_r_aabb, .. } => child_l_aabb.join(&child_r_aabb), - BVHNode::Leaf { shape_index, .. } => shapes[shape_index].aabb(), + BvhNode::Leaf { shape_index, .. } => shapes[shape_index].aabb(), } } @@ -210,37 +210,37 @@ impl BVHNode { /// or `None` if it is an interior node. pub fn shape_index(&self) -> Option { match *self { - BVHNode::Leaf { shape_index, .. } => Some(shape_index), + BvhNode::Leaf { shape_index, .. } => Some(shape_index), _ => None, } } /// The build function sometimes needs to add nodes while their data is not available yet. /// A dummy cerated by this function serves the purpose of being changed later on. - fn create_dummy() -> BVHNode { - BVHNode::Leaf { + fn create_dummy() -> BvhNode { + BvhNode::Leaf { parent_index: 0, depth: 0, shape_index: 0, } } - /// Builds a [`BVHNode`] recursively using SAH partitioning. + /// Builds a [`BvhNode`] recursively using SAH partitioning. /// Returns the index of the new node in the nodes vector. /// - /// [`BVHNode`]: enum.BVHNode.html + /// [`BvhNode`]: enum.BvhNode.html /// pub fn build>( shapes: &mut [S], indices: &[usize], - nodes: &mut Vec>, + nodes: &mut Vec>, parent_index: usize, depth: u32, ) -> usize where T: FromPrimitive + ClosedSub + ClosedAdd + SimdPartialOrd + ClosedMul + Float, { - // Helper function to accumulate the AABB joint and the centroids AABB + // Helper function to accumulate the Aabb joint and the centroids Aabb fn grow_convex_hull< T: Scalar + Copy @@ -252,9 +252,9 @@ impl BVHNode { + ToPrimitive, const D: usize, >( - convex_hull: (AABB, AABB), - shape_aabb: &AABB, - ) -> (AABB, AABB) { + convex_hull: (Aabb, Aabb), + shape_aabb: &Aabb, + ) -> (Aabb, Aabb) { let center = &shape_aabb.center(); let convex_hull_aabbs = &convex_hull.0; let convex_hull_centroids = &convex_hull.1; @@ -274,7 +274,7 @@ impl BVHNode { if indices.len() == 1 { let shape_index = indices[0]; let node_index = nodes.len(); - nodes.push(BVHNode::Leaf { + nodes.push(BvhNode::Leaf { parent_index, depth, shape_index, @@ -287,13 +287,13 @@ impl BVHNode { // From here on we handle the recursive case. This dummy is required, because the children // must know their parent, and it's easier to update one parent node than the child nodes. let node_index = nodes.len(); - nodes.push(BVHNode::create_dummy()); + nodes.push(BvhNode::create_dummy()); // Find the axis along which the shapes are spread the most. let split_axis = centroid_bounds.largest_axis(); let split_axis_size = centroid_bounds.max[split_axis] - centroid_bounds.min[split_axis]; - // The following `if` partitions `indices` for recursively calling `BVH::build`. + // The following `if` partitions `indices` for recursively calling `Bvh::build`. let (child_l_index, child_l_aabb, child_r_index, child_r_aabb) = if split_axis_size < T::epsilon() { @@ -305,9 +305,9 @@ impl BVHNode { // Proceed recursively. let child_l_index = - BVHNode::build(shapes, child_l_indices, nodes, node_index, depth + 1); + BvhNode::build(shapes, child_l_indices, nodes, node_index, depth + 1); let child_r_index = - BVHNode::build(shapes, child_r_indices, nodes, node_index, depth + 1); + BvhNode::build(shapes, child_r_indices, nodes, node_index, depth + 1); (child_l_index, child_l_aabb, child_r_index, child_r_aabb) } else { // Create six `Bucket`s, and six index assignment vector. @@ -341,8 +341,8 @@ impl BVHNode { // Compute the costs for each configuration and select the best configuration. let mut min_bucket = 0; let mut min_cost = T::infinity(); - let mut child_l_aabb = AABB::empty(); - let mut child_r_aabb = AABB::empty(); + let mut child_l_aabb = Aabb::empty(); + let mut child_r_aabb = Aabb::empty(); for i in 0..(NUM_BUCKETS - 1) { let (l_buckets, r_buckets) = buckets.split_at(i + 1); let child_l = l_buckets.iter().fold(Bucket::empty(), Bucket::join_bucket); @@ -366,16 +366,16 @@ impl BVHNode { // Proceed recursively. let child_l_index = - BVHNode::build(shapes, &child_l_indices, nodes, node_index, depth + 1); + BvhNode::build(shapes, &child_l_indices, nodes, node_index, depth + 1); let child_r_index = - BVHNode::build(shapes, &child_r_indices, nodes, node_index, depth + 1); + BvhNode::build(shapes, &child_r_indices, nodes, node_index, depth + 1); (child_l_index, child_l_aabb, child_r_index, child_r_aabb) }; // Construct the actual data structure and replace the dummy node. assert!(!child_l_aabb.is_empty()); assert!(!child_r_aabb.is_empty()); - nodes[node_index] = BVHNode::Node { + nodes[node_index] = BvhNode::Node { parent_index, depth, child_l_aabb, @@ -387,15 +387,15 @@ impl BVHNode { node_index } - /// Traverses the [`BVH`] recursively and returns all shapes whose [`AABB`] is + /// Traverses the [`Bvh`] recursively and returns all shapes whose [`Aabb`] is /// intersected by the given [`Ray`]. /// - /// [`AABB`]: ../aabb/struct.AABB.html - /// [`BVH`]: struct.BVH.html + /// [`Aabb`]: ../aabb/struct.Aabb.html + /// [`Bvh`]: struct.Bvh.html /// [`Ray`]: ../ray/struct.Ray.html /// pub fn traverse_recursive( - nodes: &[BVHNode], + nodes: &[BvhNode], node_index: usize, ray: &Ray, indices: &mut Vec, @@ -403,7 +403,7 @@ impl BVHNode { T: PartialOrd + Zero + ClosedSub + ClosedMul + SimdPartialOrd, { match nodes[node_index] { - BVHNode::Node { + BvhNode::Node { ref child_l_aabb, child_l_index, ref child_r_aabb, @@ -411,55 +411,55 @@ impl BVHNode { .. } => { if ray.intersects_aabb(child_l_aabb) { - BVHNode::traverse_recursive(nodes, child_l_index, ray, indices); + BvhNode::traverse_recursive(nodes, child_l_index, ray, indices); } if ray.intersects_aabb(child_r_aabb) { - BVHNode::traverse_recursive(nodes, child_r_index, ray, indices); + BvhNode::traverse_recursive(nodes, child_r_index, ray, indices); } } - BVHNode::Leaf { shape_index, .. } => { + BvhNode::Leaf { shape_index, .. } => { indices.push(shape_index); } } } } -/// The [`BVH`] data structure. Contains the list of [`BVHNode`]s. +/// The [`Bvh`] data structure. Contains the list of [`BvhNode`]s. /// -/// [`BVH`]: struct.BVH.html +/// [`Bvh`]: struct.Bvh.html /// #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BVH { - /// The list of nodes of the [`BVH`]. +pub struct Bvh { + /// The list of nodes of the [`Bvh`]. /// - /// [`BVH`]: struct.BVH.html + /// [`Bvh`]: struct.Bvh.html /// - pub nodes: Vec>, + pub nodes: Vec>, } -impl BVH { - /// Creates a new [`BVH`] from the `shapes` slice. +impl Bvh { + /// Creates a new [`Bvh`] from the `shapes` slice. /// - /// [`BVH`]: struct.BVH.html + /// [`Bvh`]: struct.Bvh.html /// - pub fn build>(shapes: &mut [Shape]) -> BVH + pub fn build>(shapes: &mut [Shape]) -> Bvh where T: FromPrimitive + ToPrimitive + Float + ClosedSub + ClosedAdd + ClosedMul + SimdPartialOrd, { let indices = (0..shapes.len()).collect::>(); let expected_node_count = shapes.len() * 2; let mut nodes = Vec::with_capacity(expected_node_count); - BVHNode::build(shapes, &indices, &mut nodes, 0, 0); - BVH { nodes } + BvhNode::build(shapes, &indices, &mut nodes, 0, 0); + Bvh { nodes } } - /// Traverses the [`BVH`]. - /// Returns a subset of `shapes`, in which the [`AABB`]s of the elements were hit by `ray`. + /// Traverses the [`Bvh`]. + /// Returns a subset of `shapes`, in which the [`Aabb`]s of the elements were hit by `ray`. /// - /// [`BVH`]: struct.BVH.html - /// [`AABB`]: ../aabb/struct.AABB.html + /// [`Bvh`]: struct.Bvh.html + /// [`Aabb`]: ../aabb/struct.Aabb.html /// pub fn traverse<'a, Shape: Bounded>( &'a self, @@ -470,45 +470,45 @@ impl BVH { T: PartialOrd + Zero + ClosedSub + ClosedMul + SimdPartialOrd, { let mut indices = Vec::new(); - BVHNode::traverse_recursive(&self.nodes, 0, ray, &mut indices); + BvhNode::traverse_recursive(&self.nodes, 0, ray, &mut indices); indices .iter() .map(|index| &shapes[*index]) .collect::>() } - /// Creates a [`BVHTraverseIterator`] to traverse the [`BVH`]. - /// Returns a subset of `shapes`, in which the [`AABB`]s of the elements were hit by `ray`. + /// Creates a [`BvhTraverseIterator`] to traverse the [`Bvh`]. + /// Returns a subset of `shapes`, in which the [`Aabb`]s of the elements were hit by `ray`. /// - /// [`BVH`]: struct.BVH.html - /// [`AABB`]: ../aabb/struct.AABB.html + /// [`Bvh`]: struct.Bvh.html + /// [`Aabb`]: ../aabb/struct.Aabb.html /// pub fn traverse_iterator<'bvh, 'shape, Shape: Bounded>( &'bvh self, ray: &'bvh Ray, shapes: &'shape [Shape], - ) -> BVHTraverseIterator<'bvh, 'shape, T, D, Shape> + ) -> BvhTraverseIterator<'bvh, 'shape, T, D, Shape> where T: SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, { - BVHTraverseIterator::new(self, ray, shapes) + BvhTraverseIterator::new(self, ray, shapes) } - /// Prints the [`BVH`] in a tree-like visualization. + /// Prints the [`Bvh`] in a tree-like visualization. /// - /// [`BVH`]: struct.BVH.html + /// [`Bvh`]: struct.Bvh.html /// pub fn pretty_print(&self) where T: std::fmt::Display, { let nodes = &self.nodes; - fn print_node(nodes: &[BVHNode], node_index: usize) + fn print_node(nodes: &[BvhNode], node_index: usize) where T: std::fmt::Display, { match nodes[node_index] { - BVHNode::Node { + BvhNode::Node { child_l_index, child_r_index, depth, @@ -522,7 +522,7 @@ impl BVH { println!("{}child_r {}", padding, child_r_aabb); print_node(nodes, child_r_index); } - BVHNode::Leaf { + BvhNode::Leaf { shape_index, depth, .. } => { let padding: String = " ".repeat(depth as usize); @@ -540,7 +540,7 @@ impl BVH { &self, node_index: usize, expected_parent_index: usize, - expected_outer_aabb: &AABB, + expected_outer_aabb: &Aabb, expected_depth: u32, node_count: &mut usize, shapes: &[Shape], @@ -550,7 +550,7 @@ impl BVH { { *node_count += 1; match self.nodes[node_index] { - BVHNode::Node { + BvhNode::Node { parent_index, depth, child_l_index, @@ -588,7 +588,7 @@ impl BVH { && left_subtree_consistent && right_subtree_consistent } - BVHNode::Leaf { + BvhNode::Leaf { parent_index, depth, shape_index, @@ -605,13 +605,13 @@ impl BVH { } /// Checks if all children of a node have the correct parent index, and that there is no - /// detached subtree. Also checks if the `AABB` hierarchy is consistent. + /// detached subtree. Also checks if the `Aabb` hierarchy is consistent. pub fn is_consistent>(&self, shapes: &[Shape]) -> bool where T: Float + ClosedSub, { // The root node of the bvh is not bounded by anything. - let space = AABB::infinite(); + let space = Aabb::infinite(); // The counter for all nodes. let mut node_count = 0; @@ -629,7 +629,7 @@ impl BVH { &self, node_index: usize, expected_parent_index: usize, - expected_outer_aabb: &AABB, + expected_outer_aabb: &Aabb, expected_depth: u32, node_count: &mut usize, shapes: &[Shape], @@ -653,7 +653,7 @@ impl BVH { ); match *node { - BVHNode::Node { + BvhNode::Node { child_l_index, child_l_aabb, child_r_index, @@ -693,11 +693,11 @@ impl BVH { shapes, ); } - BVHNode::Leaf { shape_index, .. } => { + BvhNode::Leaf { shape_index, .. } => { let shape_aabb = shapes[shape_index].aabb(); assert!( expected_outer_aabb.approx_contains_aabb_eps(&shape_aabb, T::epsilon()), - "Shape's AABB lies outside the expected bounds.\n\tBounds: {}\n\tShape: {}", + "Shape's Aabb lies outside the expected bounds.\n\tBounds: {}\n\tShape: {}", expected_outer_aabb, shape_aabb ); @@ -711,7 +711,7 @@ impl BVH { T: Float + ClosedSub + std::fmt::Display, { // The root node of the bvh is not bounded by anything. - let space = AABB::infinite(); + let space = Aabb::infinite(); // The counter for all nodes. let mut node_count = 0; @@ -722,14 +722,14 @@ impl BVH { assert_eq!(node_count, self.nodes.len(), "Detached subtree"); } - /// Check that the `AABB`s in the `BVH` are tight, which means, that parent `AABB`s are not + /// Check that the `Aabb`s in the `Bvh` are tight, which means, that parent `Aabb`s are not /// larger than they should be. This function checks, whether the children of node `node_index` /// lie inside `outer_aabb`. - pub fn assert_tight_subtree(&self, node_index: usize, outer_aabb: &AABB) + pub fn assert_tight_subtree(&self, node_index: usize, outer_aabb: &Aabb) where T: Float + SimdPartialOrd + ClosedSub + Signed, { - if let BVHNode::Node { + if let BvhNode::Node { child_l_index, child_l_aabb, child_r_index, @@ -744,15 +744,15 @@ impl BVH { } } - /// Check that the `AABB`s in the `BVH` are tight, which means, that parent `AABB`s are not + /// Check that the `Aabb`s in the `Bvh` are tight, which means, that parent `Aabb`s are not /// larger than they should be. pub fn assert_tight(&self) where T: SimdPartialOrd + Float + ClosedSub + Signed, { - // When starting to check whether the `BVH` is tight, we cannot provide a minimum - // outer `AABB`, therefore we compute the correct one in this instance. - if let BVHNode::Node { + // When starting to check whether the `Bvh` is tight, we cannot provide a minimum + // outer `Aabb`, therefore we compute the correct one in this instance. + if let BvhNode::Node { child_l_aabb, child_r_aabb, .. @@ -764,7 +764,7 @@ impl BVH { } } -impl BoundingHierarchy for BVH +impl BoundingHierarchy for Bvh where T: Scalar + Copy @@ -777,8 +777,8 @@ where + SimdPartialOrd + std::fmt::Display, { - fn build>(shapes: &mut [Shape]) -> BVH { - BVH::build(shapes) + fn build>(shapes: &mut [Shape]) -> Bvh { + Bvh::build(shapes) } fn traverse<'a, Shape: Bounded>( @@ -796,18 +796,18 @@ where #[cfg(test)] mod tests { - use crate::testbase::{build_some_bh, traverse_some_bh, BVHNode, BVH}; + use crate::testbase::{build_some_bh, traverse_some_bh, TBvhNode3, TBvh3}; #[test] /// Tests whether the building procedure succeeds in not failing. fn test_build_bvh() { - build_some_bh::(); + build_some_bh::(); } #[test] - /// Runs some primitive tests for intersections of a ray with a fixed scene given as a BVH. + /// Runs some primitive tests for intersections of a ray with a fixed scene given as a Bvh. fn test_traverse_bvh() { - traverse_some_bh::(); + traverse_some_bh::(); } #[test] @@ -815,7 +815,7 @@ mod tests { fn test_bvh_shape_indices() { use std::collections::HashSet; - let (all_shapes, bh) = build_some_bh::(); + let (all_shapes, bh) = build_some_bh::(); // It should find all shape indices. let expected_shapes: HashSet<_> = (0..all_shapes.len()).collect(); @@ -823,10 +823,10 @@ mod tests { for node in bh.nodes.iter() { match *node { - BVHNode::Node { .. } => { + TBvhNode3::Node { .. } => { assert_eq!(node.shape_index(), None); } - BVHNode::Leaf { .. } => { + TBvhNode3::Leaf { .. } => { found_shapes.insert( node.shape_index() .expect("getting a shape index from a leaf node"), @@ -844,59 +844,59 @@ mod bench { use crate::testbase::{ build_1200_triangles_bh, build_120k_triangles_bh, build_12k_triangles_bh, intersect_1200_triangles_bh, intersect_120k_triangles_bh, intersect_12k_triangles_bh, - intersect_bh, load_sponza_scene, BVH, + intersect_bh, load_sponza_scene, TBvh3, }; #[bench] - /// Benchmark the construction of a `BVH` with 1,200 triangles. + /// Benchmark the construction of a `Bvh` with 1,200 triangles. fn bench_build_1200_triangles_bvh(b: &mut ::test::Bencher) { - build_1200_triangles_bh::(b); + build_1200_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `BVH` with 12,000 triangles. + /// Benchmark the construction of a `Bvh` with 12,000 triangles. fn bench_build_12k_triangles_bvh(b: &mut ::test::Bencher) { - build_12k_triangles_bh::(b); + build_12k_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `BVH` with 120,000 triangles. + /// Benchmark the construction of a `Bvh` with 120,000 triangles. fn bench_build_120k_triangles_bvh(b: &mut ::test::Bencher) { - build_120k_triangles_bh::(b); + build_120k_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `BVH` for the Sponza scene. + /// Benchmark the construction of a `Bvh` for the Sponza scene. fn bench_build_sponza_bvh(b: &mut ::test::Bencher) { let (mut triangles, _) = load_sponza_scene(); b.iter(|| { - BVH::build(&mut triangles); + TBvh3::build(&mut triangles); }); } #[bench] - /// Benchmark intersecting 1,200 triangles using the recursive `BVH`. + /// Benchmark intersecting 1,200 triangles using the recursive `Bvh`. fn bench_intersect_1200_triangles_bvh(b: &mut ::test::Bencher) { - intersect_1200_triangles_bh::(b); + intersect_1200_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 12,000 triangles using the recursive `BVH`. + /// Benchmark intersecting 12,000 triangles using the recursive `Bvh`. fn bench_intersect_12k_triangles_bvh(b: &mut ::test::Bencher) { - intersect_12k_triangles_bh::(b); + intersect_12k_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 120,000 triangles using the recursive `BVH`. + /// Benchmark intersecting 120,000 triangles using the recursive `Bvh`. fn bench_intersect_120k_triangles_bvh(b: &mut ::test::Bencher) { - intersect_120k_triangles_bh::(b); + intersect_120k_triangles_bh::(b); } #[bench] - /// Benchmark the traversal of a `BVH` with the Sponza scene. + /// Benchmark the traversal of a `Bvh` with the Sponza scene. fn bench_intersect_sponza_bvh(b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); - let bvh = BVH::build(&mut triangles); + let bvh = TBvh3::build(&mut triangles); intersect_bh(&bvh, &triangles, &bounds, b) } } diff --git a/src/bvh/iter.rs b/src/bvh/iter.rs index 42dc20e..6ef31a5 100644 --- a/src/bvh/iter.rs +++ b/src/bvh/iter.rs @@ -2,15 +2,15 @@ use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::Zero; use crate::aabb::Bounded; -use crate::bvh::{BVHNode, BVH}; +use crate::bvh::{BvhNode, Bvh}; use crate::ray::Ray; -/// Iterator to traverse a [`BVH`] without memory allocations +/// Iterator to traverse a [`Bvh`] without memory allocations #[allow(clippy::upper_case_acronyms)] -pub struct BVHTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, Shape: Bounded> +pub struct BvhTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, Shape: Bounded> { - /// Reference to the BVH to traverse - bvh: &'bvh BVH, + /// Reference to the Bvh to traverse + bvh: &'bvh Bvh, /// Reference to the input ray ray: &'bvh Ray, /// Reference to the input shapes array @@ -26,13 +26,13 @@ pub struct BVHTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, S } impl<'bvh, 'shape, T, const D: usize, Shape: Bounded> - BVHTraverseIterator<'bvh, 'shape, T, D, Shape> + BvhTraverseIterator<'bvh, 'shape, T, D, Shape> where T: Scalar + Copy + SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, { - /// Creates a new `BVHTraverseIterator` - pub fn new(bvh: &'bvh BVH, ray: &'bvh Ray, shapes: &'shape [Shape]) -> Self { - BVHTraverseIterator { + /// Creates a new `BvhTraverseIterator` + pub fn new(bvh: &'bvh Bvh, ray: &'bvh Ray, shapes: &'shape [Shape]) -> Self { + BvhTraverseIterator { bvh, ray, shapes, @@ -69,10 +69,10 @@ where } /// Attempt to move to the left node child of the current node. - /// If it is a leaf, or the ray does not intersect the node `AABB`, `has_node` will become false. + /// If it is a leaf, or the ray does not intersect the node `Aabb`, `has_node` will become false. fn move_left(&mut self) { match self.bvh.nodes[self.node_index] { - BVHNode::Node { + BvhNode::Node { child_l_index, ref child_l_aabb, .. @@ -84,17 +84,17 @@ where self.has_node = false; } } - BVHNode::Leaf { .. } => { + BvhNode::Leaf { .. } => { self.has_node = false; } } } /// Attempt to move to the right node child of the current node. - /// If it is a leaf, or the ray does not intersect the node `AABB`, `has_node` will become false. + /// If it is a leaf, or the ray does not intersect the node `Aabb`, `has_node` will become false. fn move_right(&mut self) { match self.bvh.nodes[self.node_index] { - BVHNode::Node { + BvhNode::Node { child_r_index, ref child_r_aabb, .. @@ -106,7 +106,7 @@ where self.has_node = false; } } - BVHNode::Leaf { .. } => { + BvhNode::Leaf { .. } => { self.has_node = false; } } @@ -114,7 +114,7 @@ where } impl<'bvh, 'shape, T, const D: usize, Shape: Bounded> Iterator - for BVHTraverseIterator<'bvh, 'shape, T, D, Shape> + for BvhTraverseIterator<'bvh, 'shape, T, D, Shape> where T: Scalar + Copy + SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, { @@ -134,11 +134,11 @@ where // Go back up the stack and see if a node or leaf was pushed. self.node_index = self.stack_pop(); match self.bvh.nodes[self.node_index] { - BVHNode::Node { .. } => { + BvhNode::Node { .. } => { // If a node was pushed, now attempt to move to its right child. self.move_right(); } - BVHNode::Leaf { shape_index, .. } => { + BvhNode::Leaf { shape_index, .. } => { // We previously pushed a leaf node. This is the "visit" of the in-order traverse. // Next time we call `next()` we try to pop the stack again. self.has_node = false; @@ -156,23 +156,23 @@ where #[cfg(test)] mod tests { use crate::ray::Ray; - use crate::testbase::{generate_aligned_boxes, Point3, UnitBox, Vector3, BVH}; + use crate::testbase::{generate_aligned_boxes, TPoint3, UnitBox, TVector3, TBvh3}; use std::collections::HashSet; - /// Creates a `BVH` for a fixed scene structure. - pub fn build_some_bvh() -> (Vec, BVH) { + /// Creates a `Bvh` for a fixed scene structure. + pub fn build_some_bvh() -> (Vec, TBvh3) { let mut boxes = generate_aligned_boxes(); - let bvh = BVH::build(&mut boxes); + let bvh = TBvh3::build(&mut boxes); (boxes, bvh) } /// Given a ray, a bounding hierarchy, the complete list of shapes in the scene and a list of /// expected hits, verifies, whether the ray hits only the expected shapes. fn traverse_and_verify_vec( - ray_origin: Point3, - ray_direction: Vector3, + ray_origin: TPoint3, + ray_direction: TVector3, all_shapes: &[UnitBox], - bvh: &BVH, + bvh: &TBvh3, expected_shapes: &HashSet, ) { let ray = Ray::new(ray_origin, ray_direction); @@ -185,10 +185,10 @@ mod tests { } fn traverse_and_verify_iterator( - ray_origin: Point3, - ray_direction: Vector3, + ray_origin: TPoint3, + ray_direction: TVector3, all_shapes: &[UnitBox], - bvh: &BVH, + bvh: &TBvh3, expected_shapes: &HashSet, ) { let ray = Ray::new(ray_origin, ray_direction); @@ -203,10 +203,10 @@ mod tests { } fn traverse_and_verify_base( - ray_origin: Point3, - ray_direction: Vector3, + ray_origin: TPoint3, + ray_direction: TVector3, all_shapes: &[UnitBox], - bvh: &BVH, + bvh: &TBvh3, expected_shapes: &HashSet, ) { traverse_and_verify_vec(ray_origin, ray_direction, all_shapes, bvh, expected_shapes); @@ -219,8 +219,8 @@ mod tests { { // Define a ray which traverses the x-axis from afar. - let origin = Point3::new(-1000.0, 0.0, 0.0); - let direction = Vector3::new(1.0, 0.0, 0.0); + let origin = TPoint3::new(-1000.0, 0.0, 0.0); + let direction = TVector3::new(1.0, 0.0, 0.0); let mut expected_shapes = HashSet::new(); // It should hit everything. @@ -232,8 +232,8 @@ mod tests { { // Define a ray which traverses the y-axis from afar. - let origin = Point3::new(0.0, -1000.0, 0.0); - let direction = Vector3::new(0.0, 1.0, 0.0); + let origin = TPoint3::new(0.0, -1000.0, 0.0); + let direction = TVector3::new(0.0, 1.0, 0.0); // It should hit only one box. let mut expected_shapes = HashSet::new(); @@ -243,8 +243,8 @@ mod tests { { // Define a ray which intersects the x-axis diagonally. - let origin = Point3::new(6.0, 0.5, 0.0); - let direction = Vector3::new(-2.0, -1.0, 0.0); + let origin = TPoint3::new(6.0, 0.5, 0.0); + let direction = TVector3::new(-2.0, -1.0, 0.0); // It should hit exactly three boxes. let mut expected_shapes = HashSet::new(); @@ -256,7 +256,7 @@ mod tests { } #[test] - /// Runs some primitive tests for intersections of a ray with a fixed scene given as a BVH. + /// Runs some primitive tests for intersections of a ray with a fixed scene given as a Bvh. fn test_traverse_bvh() { traverse_some_bvh(); } @@ -264,23 +264,23 @@ mod tests { #[cfg(all(feature = "bench", test))] mod bench { - use crate::testbase::{create_ray, load_sponza_scene, BVH}; + use crate::testbase::{create_ray, load_sponza_scene, TBvh3}; #[bench] - /// Benchmark the traversal of a `BVH` with the Sponza scene with Vec return. + /// Benchmark the traversal of a `Bvh` with the Sponza scene with Vec return. fn bench_intersect_128rays_sponza_vec(b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); - let bvh = BVH::build(&mut triangles); + let bvh = TBvh3::build(&mut triangles); let mut seed = 0; b.iter(|| { for _ in 0..128 { let ray = create_ray(&mut seed, &bounds); - // Traverse the `BVH` recursively. + // Traverse the `Bvh` recursively. let hits = bvh.traverse(&ray, &triangles); - // Traverse the resulting list of positive `AABB` tests + // Traverse the resulting list of positive `Aabb` tests for triangle in &hits { ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c); } @@ -289,20 +289,20 @@ mod bench { } #[bench] - /// Benchmark the traversal of a `BVH` with the Sponza scene with `BVHTraverseIterator`. + /// Benchmark the traversal of a `Bvh` with the Sponza scene with `BvhTraverseIterator`. fn bench_intersect_128rays_sponza_iter(b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); - let bvh = BVH::build(&mut triangles); + let bvh = TBvh3::build(&mut triangles); let mut seed = 0; b.iter(|| { for _ in 0..128 { let ray = create_ray(&mut seed, &bounds); - // Traverse the `BVH` recursively. + // Traverse the `Bvh` recursively. let hits = bvh.traverse_iterator(&ray, &triangles); - // Traverse the resulting list of positive `AABB` tests + // Traverse the resulting list of positive `Aabb` tests for triangle in hits { ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c); } diff --git a/src/bvh/mod.rs b/src/bvh/mod.rs index c3e56fa..214deb8 100644 --- a/src/bvh/mod.rs +++ b/src/bvh/mod.rs @@ -1,6 +1,6 @@ -//! This module defines a [`BVH`]. +//! This module defines a [`Bvh`]. //! -//! [`BVH`]: struct.BVH.html +//! [`Bvh`]: struct.Bvh.html //! mod bvh_impl; diff --git a/src/bvh/optimization.rs b/src/bvh/optimization.rs index 0de879e..49a9bce 100644 --- a/src/bvh/optimization.rs +++ b/src/bvh/optimization.rs @@ -1,12 +1,12 @@ -//! This module defines the optimization function for the [`BVH`]. +//! This module defines the optimization function for the [`Bvh`]. //! By passing the indices of shapes that have changed, the function determines possible -//! tree rotations and optimizes the BVH using a SAH. +//! tree rotations and optimizes the Bvh using a SAH. //! Based on [`http://www.cs.utah.edu/~thiago/papers/rotations.pdf`] //! -//! [`BVH`]: struct.BVH.html +//! [`Bvh`]: struct.Bvh.html //! -use crate::aabb::AABB; +use crate::aabb::Aabb; use crate::bounding_hierarchy::BHShape; use crate::bvh::*; @@ -16,22 +16,22 @@ use num::{FromPrimitive, Zero}; use rand::{thread_rng, Rng}; use std::collections::HashSet; -// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an AABB +// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an Aabb // that is updated from the outside, perhaps by passing not only the indices of the changed -// shapes, but also their new AABBs into optimize(). -// TODO Consider: Stop updating AABBs upwards the tree once an AABB didn't get changed. +// shapes, but also their new Aabbs into optimize(). +// TODO Consider: Stop updating Aabbs upwards the tree once an Aabb didn't get changed. #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] #[allow(clippy::upper_case_acronyms)] enum OptimizationIndex { Refit(usize), - FixAABBs(usize), + FixAabbs(usize), } impl OptimizationIndex { fn index(&self) -> usize { match *self { - OptimizationIndex::Refit(index) | OptimizationIndex::FixAABBs(index) => index, + OptimizationIndex::Refit(index) | OptimizationIndex::FixAabbs(index) => index, } } } @@ -39,14 +39,14 @@ impl OptimizationIndex { #[derive(Debug, Copy, Clone)] struct NodeData { index: usize, - aabb: AABB, + aabb: Aabb, } -impl BVHNode { +impl BvhNode { // Get the grandchildren's NodeData. fn get_children_node_data(&self) -> Option<(NodeData, NodeData)> { match *self { - BVHNode::Node { + BvhNode::Node { child_l_index, child_l_aabb, child_r_index, @@ -62,12 +62,12 @@ impl BVHNode { aabb: child_r_aabb, }, )), - BVHNode::Leaf { .. } => None, + BvhNode::Leaf { .. } => None, } } } -impl BVH +impl Bvh where T: Scalar + Copy @@ -80,9 +80,9 @@ where + PartialOrd + std::fmt::Display, { - /// Optimizes the `BVH` by batch-reorganizing updated nodes. + /// Optimizes the `Bvh` by batch-reorganizing updated nodes. /// Based on - /// [`https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs`] + /// [`https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBvh/ssBvh.cs`] /// /// Needs all the scene's shapes, plus the indices of the shapes that were updated. /// @@ -149,7 +149,7 @@ where // in a quick, sequential loop after all threads finished their work. let new_refit_node_index = match sweep_node_index { OptimizationIndex::Refit(index) => self.update(index, shapes), - OptimizationIndex::FixAABBs(index) => self.fix_aabbs(index, shapes), + OptimizationIndex::FixAabbs(index) => self.fix_aabbs(index, shapes), }; // Instead of finding a useful tree rotation, we found another node @@ -166,7 +166,7 @@ where } /// This method is called for each node which has been modified and needs to be updated. - /// If the specified node is a grandparent, then try to optimize the `BVH` by rotating its + /// If the specified node is a grandparent, then try to optimize the `Bvh` by rotating its /// children. fn update>( &mut self, @@ -176,7 +176,7 @@ where info!(" [{}]\t", node_index); match self.nodes[node_index] { - BVHNode::Leaf { + BvhNode::Leaf { parent_index, shape_index, .. @@ -189,7 +189,7 @@ where ); Some(OptimizationIndex::Refit(parent_index)) } - BVHNode::Node { + BvhNode::Node { parent_index, child_l_index, child_r_index, @@ -197,19 +197,19 @@ where } => { // The current node is a parent. if let ( - &BVHNode::Leaf { + &BvhNode::Leaf { shape_index: shape_l_index, .. }, - &BVHNode::Leaf { + &BvhNode::Leaf { shape_index: shape_r_index, .. }, ) = (&self.nodes[child_l_index], &self.nodes[child_r_index]) { - // The current node is a final parent. Update its `AABB`s, because at least + // The current node is a final parent. Update its `Aabb`s, because at least // one of its children was updated and queue its parent for refitting. - if let BVHNode::Node { + if let BvhNode::Node { ref mut child_l_aabb, ref mut child_r_aabb, .. @@ -233,11 +233,11 @@ where fn find_better_rotation( &self, child_l_index: usize, - child_l_aabb: &AABB, + child_l_aabb: &Aabb, child_r_index: usize, - child_r_aabb: &AABB, + child_r_aabb: &Aabb, ) -> Option<(usize, usize)> { - // Get indices and `AABB`s of all grandchildren. + // Get indices and `Aabb`s of all grandchildren. let left_children_nodes = self.nodes[child_l_index].get_children_node_data(); let right_children_nodes = self.nodes[child_r_index].get_children_node_data(); @@ -289,12 +289,12 @@ where } /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of - /// the given node (specified by `node_index`) that would improve the `BVH`. + /// the given node (specified by `node_index`) that would improve the `Bvh`. /// If there is, the best rotation found is performed. /// /// # Preconditions /// - /// This function requires that the subtree at `node_index` has correct `AABB`s. + /// This function requires that the subtree at `node_index` has correct `Aabb`s. /// /// # Returns /// @@ -305,7 +305,7 @@ where node_index: usize, shapes: &[Shape], ) -> Option { - let (parent_index, child_l_index, child_r_index) = if let BVHNode::Node { + let (parent_index, child_l_index, child_r_index) = if let BvhNode::Node { parent_index, child_l_index, child_r_index, @@ -317,8 +317,8 @@ where unreachable!() }; - // Recalculate `AABB`s for the children since at least one of them changed. Don't update - // the `AABB`s in the node yet because they're still subject to change during potential + // Recalculate `Aabb`s for the children since at least one of them changed. Don't update + // the `Aabb`s in the node yet because they're still subject to change during potential // upcoming rotations. let child_l_aabb = self.nodes[child_l_index].get_node_aabb(shapes); let child_r_aabb = self.nodes[child_r_index].get_node_aabb(shapes); @@ -329,11 +329,11 @@ where if let Some((rotation_node_a, rotation_node_b)) = best_rotation { self.rotate(rotation_node_a, rotation_node_b, shapes); - // Update the children's children `AABB`s and the children `AABB`s of node. + // Update the children's children `Aabb`s and the children `Aabb`s of node. self.fix_children_and_own_aabbs(node_index, shapes); // Return parent node's index for upcoming refitting, - // since this node just changed its `AABB`. + // since this node just changed its `Aabb`. if node_index != 0 { Some(OptimizationIndex::Refit(parent_index)) } else { @@ -341,7 +341,7 @@ where } } else { info!(" No useful rotation."); - // Set the correct children `AABB`s, which have been computed earlier. + // Set the correct children `Aabb`s, which have been computed earlier. *self.nodes[node_index].child_l_aabb_mut() = child_l_aabb; *self.nodes[node_index].child_r_aabb_mut() = child_r_aabb; @@ -349,14 +349,14 @@ where if node_index != 0 { // Even with no rotation being useful for this node, a parent node's rotation // could be beneficial, so queue the parent *sometimes*. For reference see: - // https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307 + // https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBvh/ssBvh_Node.cs#L307 // TODO Evaluate whether this is a smart thing to do. let mut rng = thread_rng(); if rng.gen_bool(0.01) { Some(OptimizationIndex::Refit(parent_index)) } else { - // Otherwise, we still have to fix the parent's AABBs - Some(OptimizationIndex::FixAABBs(parent_index)) + // Otherwise, we still have to fix the parent's Aabbs + Some(OptimizationIndex::FixAabbs(parent_index)) } } else { None @@ -364,14 +364,14 @@ where } } - /// Sets child_l_aabb and child_r_aabb of a BVHNode::Node to match its children, + /// Sets child_l_aabb and child_r_aabb of a BvhNode::Node to match its children, /// right after updating the children themselves. Not recursive. fn fix_children_and_own_aabbs>( &mut self, node_index: usize, shapes: &[Shape], ) { - let (child_l_index, child_r_index) = if let BVHNode::Node { + let (child_l_index, child_r_index) = if let BvhNode::Node { child_l_index, child_r_index, .. @@ -391,7 +391,7 @@ where self.nodes[child_r_index].get_node_aabb(shapes); } - /// Updates `child_l_aabb` and `child_r_aabb` of the `BVHNode::Node` + /// Updates `child_l_aabb` and `child_r_aabb` of the `BvhNode::Node` /// with the index `node_index` from its children. fn fix_aabbs>( &mut self, @@ -399,7 +399,7 @@ where shapes: &[Shape], ) -> Option { match self.nodes[node_index] { - BVHNode::Node { + BvhNode::Node { parent_index, child_l_index, child_r_index, @@ -411,7 +411,7 @@ where self.nodes[child_r_index].get_node_aabb(shapes); if node_index > 0 { - Some(OptimizationIndex::FixAABBs(parent_index)) + Some(OptimizationIndex::FixAabbs(parent_index)) } else { None } @@ -422,7 +422,7 @@ where } /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice). - /// Also updates the AABBs of the parents. + /// Also updates the Aabbs of the parents. fn rotate>( &mut self, node_a_index: usize, @@ -459,7 +459,7 @@ where let children = { let node = &mut self.nodes[node_index]; match *node { - BVHNode::Node { + BvhNode::Node { ref mut depth, child_l_index, child_r_index, @@ -468,7 +468,7 @@ where *depth = new_depth; Some((child_l_index, child_r_index)) } - BVHNode::Leaf { ref mut depth, .. } => { + BvhNode::Leaf { ref mut depth, .. } => { *depth = new_depth; None } @@ -500,7 +500,7 @@ where // Set parent's child and child_aabb; and get its depth. let parent_depth = { match self.nodes[parent_index] { - BVHNode::Node { + BvhNode::Node { ref mut child_l_index, ref mut child_r_index, ref mut child_l_aabb, @@ -518,7 +518,7 @@ where info!("\t {}'s new {}", parent_index, child_aabb); depth } - // Assuming that our BVH is correct, the parent cannot be a leaf. + // Assuming that our Bvh is correct, the parent cannot be a leaf. _ => unreachable!(), } }; @@ -536,15 +536,15 @@ mod tests { use crate::aabb::Bounded; use crate::bounding_hierarchy::BHShape; use crate::testbase::{ - build_some_bh, create_n_cubes, default_bounds, randomly_transform_scene, BVHNode, Point3, - UnitBox, BVH, + build_some_bh, create_n_cubes, default_bounds, randomly_transform_scene, TBvhNode3, TPoint3, + UnitBox, TBvh3, }; use std::collections::HashSet; #[test] - /// Tests if `optimize` does not modify a fresh `BVH`. + /// Tests if `optimize` does not modify a fresh `Bvh`. fn test_optimizing_new_bvh() { - let (shapes, mut bvh) = build_some_bh::(); + let (shapes, mut bvh) = build_some_bh::(); let original_nodes = bvh.nodes.clone(); // Query an update for all nodes. @@ -558,15 +558,15 @@ mod tests { } #[test] - /// Tests whether a BVH is still consistent after a few optimization calls. + /// Tests whether a Bvh is still consistent after a few optimization calls. fn test_consistent_after_optimize() { - let (mut shapes, mut bvh) = build_some_bh::(); - shapes[0].pos = Point3::new(10.0, 1.0, 2.0); - shapes[1].pos = Point3::new(-10.0, -10.0, 10.0); - shapes[2].pos = Point3::new(-10.0, 10.0, 10.0); - shapes[3].pos = Point3::new(-10.0, 10.0, -10.0); - shapes[4].pos = Point3::new(11.0, 1.0, 2.0); - shapes[5].pos = Point3::new(11.0, 2.0, 2.0); + let (mut shapes, mut bvh) = build_some_bh::(); + shapes[0].pos = TPoint3::new(10.0, 1.0, 2.0); + shapes[1].pos = TPoint3::new(-10.0, -10.0, 10.0); + shapes[2].pos = TPoint3::new(-10.0, 10.0, 10.0); + shapes[3].pos = TPoint3::new(-10.0, 10.0, -10.0); + shapes[4].pos = TPoint3::new(11.0, 1.0, 2.0); + shapes[5].pos = TPoint3::new(11.0, 2.0, 2.0); let refit_shape_indices = (0..6).collect(); bvh.optimize(&refit_shape_indices, &shapes); @@ -574,15 +574,15 @@ mod tests { } #[test] - /// Test whether a simple update on a simple BVH yields the expected optimization result. + /// Test whether a simple update on a simple Bvh yields the expected optimization result. fn test_optimize_simple_update() { let mut shapes = vec![ - UnitBox::new(0, Point3::new(-50.0, 0.0, 0.0)), - UnitBox::new(1, Point3::new(-40.0, 0.0, 0.0)), - UnitBox::new(2, Point3::new(50.0, 0.0, 0.0)), + UnitBox::new(0, TPoint3::new(-50.0, 0.0, 0.0)), + UnitBox::new(1, TPoint3::new(-40.0, 0.0, 0.0)), + UnitBox::new(2, TPoint3::new(50.0, 0.0, 0.0)), ]; - let mut bvh = BVH::build(&mut shapes); + let mut bvh = TBvh3::build(&mut shapes); bvh.pretty_print(); // Assert that SAH joined shapes #0 and #1. @@ -595,11 +595,11 @@ mod tests { &bvh.nodes[moving.bh_node_index()], ) { ( - &BVHNode::Leaf { + &TBvhNode3::Leaf { parent_index: left_parent_index, .. }, - &BVHNode::Leaf { + &TBvhNode3::Leaf { parent_index: moving_parent_index, .. }, @@ -611,7 +611,7 @@ mod tests { } // Move the first shape so that it is closer to shape #2. - shapes[1].pos = Point3::new(40.0, 0.0, 0.0); + shapes[1].pos = TPoint3::new(40.0, 0.0, 0.0); let refit_shape_indices: HashSet = (1..2).collect(); bvh.optimize(&refit_shape_indices, &shapes); bvh.pretty_print(); @@ -627,11 +627,11 @@ mod tests { &bvh.nodes[moving.bh_node_index()], ) { ( - &BVHNode::Leaf { + &TBvhNode3::Leaf { parent_index: right_parent_index, .. }, - &BVHNode::Leaf { + &TBvhNode3::Leaf { parent_index: moving_parent_index, .. }, @@ -643,18 +643,18 @@ mod tests { } } - /// Creates a small `BVH` with 4 shapes and 7 nodes. - fn create_predictable_bvh() -> (Vec, BVH) { + /// Creates a small `Bvh` with 4 shapes and 7 nodes. + fn create_predictable_bvh() -> (Vec, TBvh3) { let shapes = vec![ - UnitBox::new(0, Point3::new(0.0, 0.0, 0.0)), - UnitBox::new(1, Point3::new(2.0, 0.0, 0.0)), - UnitBox::new(2, Point3::new(4.0, 0.0, 0.0)), - UnitBox::new(3, Point3::new(6.0, 0.0, 0.0)), + UnitBox::new(0, TPoint3::new(0.0, 0.0, 0.0)), + UnitBox::new(1, TPoint3::new(2.0, 0.0, 0.0)), + UnitBox::new(2, TPoint3::new(4.0, 0.0, 0.0)), + UnitBox::new(3, TPoint3::new(6.0, 0.0, 0.0)), ]; let nodes = vec![ // Root node. - BVHNode::Node { + TBvhNode3::Node { parent_index: 0, depth: 0, child_l_aabb: shapes[0].aabb().join(&shapes[1].aabb()), @@ -663,7 +663,7 @@ mod tests { child_r_index: 2, }, // Depth 1 nodes. - BVHNode::Node { + TBvhNode3::Node { parent_index: 0, depth: 1, child_l_aabb: shapes[0].aabb(), @@ -671,7 +671,7 @@ mod tests { child_r_aabb: shapes[1].aabb(), child_r_index: 4, }, - BVHNode::Node { + TBvhNode3::Node { parent_index: 0, depth: 1, child_l_aabb: shapes[2].aabb(), @@ -680,29 +680,29 @@ mod tests { child_r_index: 6, }, // Depth 2 nodes (leaves). - BVHNode::Leaf { + TBvhNode3::Leaf { parent_index: 1, depth: 2, shape_index: 0, }, - BVHNode::Leaf { + TBvhNode3::Leaf { parent_index: 1, depth: 2, shape_index: 1, }, - BVHNode::Leaf { + TBvhNode3::Leaf { parent_index: 2, depth: 2, shape_index: 2, }, - BVHNode::Leaf { + TBvhNode3::Leaf { parent_index: 2, depth: 2, shape_index: 3, }, ]; - (shapes, BVH { nodes }) + (shapes, TBvh3 { nodes }) } #[test] @@ -714,7 +714,7 @@ mod tests { bvh.connect_nodes(5, 1, true, &shapes); // Check if the resulting tree is as expected. - let BVH { nodes } = bvh; + let TBvh3 { nodes } = bvh; assert_eq!(nodes[0].parent(), 0); assert_eq!(nodes[0].child_l(), 1); @@ -756,7 +756,7 @@ mod tests { bvh.connect_nodes(5, 0, true, &shapes); // Check if the resulting tree is as expected. - let BVH { nodes } = bvh; + let TBvh3 { nodes } = bvh; assert_eq!(nodes[0].parent(), 0); assert_eq!(nodes[0].child_l(), 5); @@ -797,7 +797,7 @@ mod tests { bvh.rotate(3, 5, &shapes); // Check if the resulting tree is as expected. - let BVH { nodes } = bvh; + let TBvh3 { nodes } = bvh; assert_eq!(nodes[0].parent(), 0); assert_eq!(nodes[0].child_l(), 1); @@ -838,7 +838,7 @@ mod tests { bvh.rotate(1, 5, &shapes); // Check if the resulting tree is as expected. - let BVH { nodes } = bvh; + let TBvh3 { nodes } = bvh; assert_eq!(nodes[0].parent(), 0); assert_eq!(nodes[0].child_l(), 5); @@ -876,7 +876,7 @@ mod tests { let (mut shapes, mut bvh) = create_predictable_bvh(); // Move the second shape. - shapes[2].pos = Point3::new(-40.0, 0.0, 0.0); + shapes[2].pos = TPoint3::new(-40.0, 0.0, 0.0); // Try to rotate node 2 because node 5 changed. bvh.try_rotate(2, &shapes); @@ -885,7 +885,7 @@ mod tests { bvh.try_rotate(0, &shapes); // Check if the resulting tree is as expected. - let BVH { nodes } = bvh; + let TBvh3 { nodes } = bvh; assert_eq!(nodes[0].parent(), 0); assert_eq!(nodes[0].child_l(), 5); @@ -927,25 +927,25 @@ mod tests { } #[test] - /// Test optimizing `BVH` after randomizing 50% of the shapes. + /// Test optimizing `Bvh` after randomizing 50% of the shapes. fn test_optimize_bvh_12k_75p() { let bounds = default_bounds(); let mut triangles = create_n_cubes(1_000, &bounds); - let mut bvh = BVH::build(&mut triangles); + let mut bvh = TBvh3::build(&mut triangles); - // The initial BVH should be consistent. + // The initial Bvh should be consistent. bvh.assert_consistent(&triangles); bvh.assert_tight(); - // After moving triangles, the BVH should be inconsistent, because the shape `AABB`s do not + // After moving triangles, the Bvh should be inconsistent, because the shape `Aabb`s do not // match the tree entries. let mut seed = 0; let updated = randomly_transform_scene(&mut triangles, 9_000, &bounds, None, &mut seed); - assert!(!bvh.is_consistent(&triangles), "BVH is consistent."); + assert!(!bvh.is_consistent(&triangles), "Bvh is consistent."); - // After fixing the `AABB` consistency should be restored. + // After fixing the `Aabb` consistency should be restored. bvh.optimize(&updated, &triangles); bvh.assert_consistent(&triangles); bvh.assert_tight(); @@ -956,11 +956,11 @@ mod tests { mod bench { use crate::testbase::{ create_n_cubes, default_bounds, intersect_bh, load_sponza_scene, randomly_transform_scene, - Triangle, AABB, BVH, + Triangle, TAabb3, TBvh3, }; #[bench] - /// Benchmark randomizing 50% of the shapes in a `BVH`. + /// Benchmark randomizing 50% of the shapes in a `Bvh`. fn bench_randomize_120k_50p(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); @@ -971,12 +971,12 @@ mod bench { }); } - /// Benchmark optimizing a `BVH` with 120,000 `Triangle`s, where `percent` + /// Benchmark optimizing a `Bvh` with 120,000 `Triangle`s, where `percent` /// `Triangles` have been randomly moved. fn optimize_bvh_120k(percent: f32, b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); - let mut bvh = BVH::build(&mut triangles); + let mut bvh = TBvh3::build(&mut triangles); let num_move = (triangles.len() as f32 * percent) as usize; let mut seed = 0; @@ -1008,17 +1008,17 @@ mod bench { } /// Move `percent` `Triangle`s in the scene given by `triangles` and optimize the - /// `BVH`. Iterate this procedure `iterations` times. Afterwards benchmark the performance - /// of intersecting this scene/`BVH`. + /// `Bvh`. Iterate this procedure `iterations` times. Afterwards benchmark the performance + /// of intersecting this scene/`Bvh`. fn intersect_scene_after_optimize( triangles: &mut Vec, - bounds: &AABB, + bounds: &TAabb3, percent: f32, max_offset: Option, iterations: usize, b: &mut ::test::Bencher, ) { - let mut bvh = BVH::build(triangles); + let mut bvh = TBvh3::build(triangles); let num_move = (triangles.len() as f32 * percent) as usize; let mut seed = 0; @@ -1060,12 +1060,12 @@ mod bench { } /// Move `percent` `Triangle`s in the scene given by `triangles` `iterations` times. - /// Afterwards optimize the `BVH` and benchmark the performance of intersecting this - /// scene/`BVH`. Used to compare optimizing with rebuilding. For reference see + /// Afterwards optimize the `Bvh` and benchmark the performance of intersecting this + /// scene/`Bvh`. Used to compare optimizing with rebuilding. For reference see /// `intersect_scene_after_optimize`. fn intersect_scene_with_rebuild( triangles: &mut Vec, - bounds: &AABB, + bounds: &TAabb3, percent: f32, max_offset: Option, iterations: usize, @@ -1077,7 +1077,7 @@ mod bench { randomly_transform_scene(triangles, num_move, bounds, max_offset, &mut seed); } - let bvh = BVH::build(triangles); + let bvh = TBvh3::build(triangles); intersect_bh(&bvh, triangles, bounds, b); } @@ -1109,7 +1109,7 @@ mod bench { intersect_scene_with_rebuild(&mut triangles, &bounds, 0.5, None, 10, b); } - /// Benchmark intersecting a `BVH` for Sponza after randomly moving one `Triangle` and + /// Benchmark intersecting a `Bvh` for Sponza after randomly moving one `Triangle` and /// optimizing. fn intersect_sponza_after_optimize(percent: f32, b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); @@ -1136,7 +1136,7 @@ mod bench { intersect_sponza_after_optimize(0.5, b); } - /// Benchmark intersecting a `BVH` for Sponza after rebuilding. Used to compare optimizing + /// Benchmark intersecting a `Bvh` for Sponza after rebuilding. Used to compare optimizing /// with rebuilding. For reference see `intersect_sponza_after_optimize`. fn intersect_sponza_with_rebuild(percent: f32, b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index 4848448..c6ea88f 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -1,41 +1,41 @@ -//! This module exports methods to flatten the `BVH` and traverse it iteratively. +//! This module exports methods to flatten the `Bvh` and traverse it iteratively. use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::{Float, FromPrimitive, ToPrimitive}; -use crate::aabb::{Bounded, AABB}; +use crate::aabb::{Bounded, Aabb}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; -use crate::bvh::{BVHNode, BVH}; +use crate::bvh::{BvhNode, Bvh}; use crate::ray::Ray; -/// A structure of a node of a flat [`BVH`]. The structure of the nodes allows for an +/// A structure of a node of a flat [`Bvh`]. The structure of the nodes allows for an /// iterative traversal approach without the necessity to maintain a stack or queue. /// -/// [`BVH`]: ../bvh/struct.BVH.html +/// [`Bvh`]: ../bvh/struct.Bvh.html /// pub struct FlatNode { - /// The [`AABB`] of the [`BVH`] node. Prior to testing the [`AABB`] bounds, + /// The [`Aabb`] of the [`Bvh`] node. Prior to testing the [`Aabb`] bounds, /// the `entry_index` must be checked. In case the entry_index is [`u32::max_value()`], - /// the [`AABB`] is undefined. + /// the [`Aabb`] is undefined. /// - /// [`AABB`]: ../aabb/struct.AABB.html - /// [`BVH`]: ../bvh/struct.BVH.html + /// [`Aabb`]: ../aabb/struct.Aabb.html + /// [`Bvh`]: ../bvh/struct.Bvh.html /// [`u32::max_value()`]: https://doc.rust-lang.org/std/u32/constant.MAX.html /// - pub aabb: AABB, + pub aabb: Aabb, - /// The index of the `FlatNode` to jump to, if the [`AABB`] test is positive. + /// The index of the `FlatNode` to jump to, if the [`Aabb`] test is positive. /// If this value is [`u32::max_value()`] then the current node is a leaf node. /// Leaf nodes contain a shape index and an exit index. In leaf nodes the - /// [`AABB`] is undefined. + /// [`Aabb`] is undefined. /// - /// [`AABB`]: ../aabb/struct.AABB.html + /// [`Aabb`]: ../aabb/struct.Aabb.html /// [`u32::max_value()`]: https://doc.rust-lang.org/std/u32/constant.MAX.html /// pub entry_index: u32, - /// The index of the `FlatNode` to jump to, if the [`AABB`] test is negative. + /// The index of the `FlatNode` to jump to, if the [`Aabb`] test is negative. /// - /// [`AABB`]: ../aabb/struct.AABB.html + /// [`Aabb`]: ../aabb/struct.Aabb.html /// pub exit_index: u32, @@ -43,23 +43,23 @@ pub struct FlatNode { pub shape_index: u32, } -impl BVHNode { - /// Creates a flat node from a `BVH` inner node and its `AABB`. Returns the next free index. +impl BvhNode { + /// Creates a flat node from a `Bvh` inner node and its `Aabb`. Returns the next free index. /// TODO: change the algorithm which pushes `FlatNode`s to a vector to not use indices this /// much. Implement an algorithm which writes directly to a writable slice. fn create_flat_branch( &self, - nodes: &[BVHNode], - this_aabb: &AABB, + nodes: &[BvhNode], + this_aabb: &Aabb, vec: &mut Vec, next_free: usize, constructor: &F, ) -> usize where - F: Fn(&AABB, u32, u32, u32) -> FNodeType, + F: Fn(&Aabb, u32, u32, u32) -> FNodeType, { // Create dummy node. - let dummy = constructor(&AABB::empty(), 0, 0, 0); + let dummy = constructor(&Aabb::empty(), 0, 0, 0); vec.push(dummy); assert_eq!(vec.len() - 1, next_free); @@ -78,23 +78,23 @@ impl BVHNode { index_after_subtree } - /// Flattens the [`BVH`], so that it can be traversed in an iterative manner. + /// Flattens the [`Bvh`], so that it can be traversed in an iterative manner. /// This method constructs custom flat nodes using the `constructor`. /// - /// [`BVH`]: ../bvh/struct.BVH.html + /// [`Bvh`]: ../bvh/struct.Bvh.html /// pub fn flatten_custom( &self, - nodes: &[BVHNode], + nodes: &[BvhNode], vec: &mut Vec, next_free: usize, constructor: &F, ) -> usize where - F: Fn(&AABB, u32, u32, u32) -> FNodeType, + F: Fn(&Aabb, u32, u32, u32) -> FNodeType, { match *self { - BVHNode::Node { + BvhNode::Node { ref child_l_aabb, child_l_index, ref child_r_aabb, @@ -116,11 +116,11 @@ impl BVHNode { constructor, ) } - BVHNode::Leaf { shape_index, .. } => { + BvhNode::Leaf { shape_index, .. } => { let mut next_shape = next_free; next_shape += 1; let leaf_node = constructor( - &AABB::empty(), + &Aabb::empty(), u32::max_value(), next_shape as u32, shape_index as u32, @@ -133,35 +133,35 @@ impl BVHNode { } } -/// A flat [`BVH`]. Represented by a vector of [`FlatNode`]s. The [`FlatBVH`] is designed for use +/// A flat [`Bvh`]. Represented by a vector of [`FlatNode`]s. The [`FlatBvh`] is designed for use /// where a recursive traversal of a data structure is not possible, for example shader programs. /// -/// [`BVH`]: ../bvh/struct.BVH.html +/// [`Bvh`]: ../bvh/struct.Bvh.html /// [`FlatNode`]: struct.FlatNode.html -/// [`FlatBVH`]: struct.FlatBVH.html +/// [`FlatBvh`]: struct.FlatBvh.html /// #[allow(clippy::upper_case_acronyms)] -pub type FlatBVH = Vec>; +pub type FlatBvh = Vec>; -impl BVH { - /// Flattens the [`BVH`] so that it can be traversed iteratively. +impl Bvh { + /// Flattens the [`Bvh`] so that it can be traversed iteratively. /// Constructs the flat nodes using the supplied function. /// This function can be used, when the flat bvh nodes should be of some particular /// non-default structure. /// The `constructor` is fed the following arguments in this order: /// - /// 1 - &AABB: The enclosing `AABB` + /// 1 - &Aabb: The enclosing `Aabb` /// 2 - u32: The index of the nested node /// 3 - u32: The exit index /// 4 - u32: The shape index /// - /// [`BVH`]: ../bvh/struct.BVH.html + /// [`Bvh`]: ../bvh/struct.Bvh.html /// /// # Example /// /// ``` - /// use bvh::aabb::{AABB, Bounded}; - /// use bvh::bvh::BVH; + /// use bvh::aabb::{Aabb, Bounded}; + /// use bvh::bvh::Bvh; /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; @@ -182,10 +182,10 @@ impl BVH { /// # } /// # /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # fn aabb(&self) -> Aabb { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); - /// # AABB::with_bounds(min, max) + /// # Aabb::with_bounds(min, max) /// # } /// # } /// # @@ -209,13 +209,13 @@ impl BVH { /// # } /// /// struct CustomStruct { - /// aabb: AABB, + /// aabb: Aabb, /// entry_index: u32, /// exit_index: u32, /// shape_index: u32, /// } /// - /// let custom_constructor = |aabb: &AABB, entry, exit, shape_index| { + /// let custom_constructor = |aabb: &Aabb, entry, exit, shape_index| { /// CustomStruct { /// aabb: *aabb, /// entry_index: entry, @@ -225,27 +225,27 @@ impl BVH { /// }; /// /// let mut shapes = create_bhshapes(); - /// let bvh = BVH::build(&mut shapes); + /// let bvh = Bvh::build(&mut shapes); /// let custom_flat_bvh = bvh.flatten_custom(&custom_constructor); /// ``` pub fn flatten_custom(&self, constructor: &F) -> Vec where - F: Fn(&AABB, u32, u32, u32) -> FNodeType, + F: Fn(&Aabb, u32, u32, u32) -> FNodeType, { let mut vec = Vec::new(); self.nodes[0].flatten_custom(&self.nodes, &mut vec, 0, constructor); vec } - /// Flattens the [`BVH`] so that it can be traversed iteratively. + /// Flattens the [`Bvh`] so that it can be traversed iteratively. /// - /// [`BVH`]: ../bvh/struct.BVH.html + /// [`Bvh`]: ../bvh/struct.Bvh.html /// /// # Example /// /// ``` - /// use bvh::aabb::{AABB, Bounded}; - /// use bvh::bvh::BVH; + /// use bvh::aabb::{Aabb, Bounded}; + /// use bvh::bvh::Bvh; /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; @@ -266,10 +266,10 @@ impl BVH { /// # } /// # /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # fn aabb(&self) -> Aabb { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); - /// # AABB::with_bounds(min, max) + /// # Aabb::with_bounds(min, max) /// # } /// # } /// # @@ -293,10 +293,10 @@ impl BVH { /// # } /// /// let mut shapes = create_bhshapes(); - /// let bvh = BVH::build(&mut shapes); + /// let bvh = Bvh::build(&mut shapes); /// let flat_bvh = bvh.flatten(); /// ``` - pub fn flatten(&self) -> FlatBVH { + pub fn flatten(&self) -> FlatBvh { self.flatten_custom(&|aabb, entry, exit, shape| FlatNode { aabb: *aabb, entry_index: entry, @@ -306,7 +306,7 @@ impl BVH { } } -impl BoundingHierarchy for FlatBVH +impl BoundingHierarchy for FlatBvh where T: Scalar + Copy @@ -318,26 +318,26 @@ where + ClosedMul + SimdPartialOrd, { - /// A [`FlatBVH`] is built from a regular [`BVH`] using the [`BVH::flatten`] method. + /// A [`FlatBvh`] is built from a regular [`Bvh`] using the [`Bvh::flatten`] method. /// - /// [`FlatBVH`]: struct.FlatBVH.html - /// [`BVH`]: ../bvh/struct.BVH.html + /// [`FlatBvh`]: struct.FlatBvh.html + /// [`Bvh`]: ../bvh/struct.Bvh.html /// - fn build>(shapes: &mut [Shape]) -> FlatBVH { - let bvh = BVH::build(shapes); + fn build>(shapes: &mut [Shape]) -> FlatBvh { + let bvh = Bvh::build(shapes); bvh.flatten() } - /// Traverses a [`FlatBVH`] structure iteratively. + /// Traverses a [`FlatBvh`] structure iteratively. /// - /// [`FlatBVH`]: struct.FlatBVH.html + /// [`FlatBvh`]: struct.FlatBvh.html /// /// # Examples /// /// ``` - /// use bvh::aabb::{AABB, Bounded}; + /// use bvh::aabb::{Aabb, Bounded}; /// use bvh::bounding_hierarchy::BoundingHierarchy; - /// use bvh::flat_bvh::FlatBVH; + /// use bvh::flat_bvh::FlatBvh; /// use nalgebra::{Point3, Vector3}; /// use bvh::ray::Ray; /// # use bvh::bounding_hierarchy::BHShape; @@ -358,10 +358,10 @@ where /// # } /// # /// # impl Bounded for UnitBox { - /// # fn aabb(&self) -> AABB { + /// # fn aabb(&self) -> Aabb { /// # let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); /// # let max = self.pos + Vector3::new(0.5, 0.5, 0.5); - /// # AABB::with_bounds(min, max) + /// # Aabb::with_bounds(min, max) /// # } /// # } /// # @@ -388,7 +388,7 @@ where /// let direction = Vector3::new(1.0,0.0,0.0); /// let ray = Ray::new(origin, direction); /// let mut shapes = create_bhshapes(); - /// let flat_bvh = FlatBVH::build(&mut shapes); + /// let flat_bvh = FlatBvh::build(&mut shapes); /// let hit_shapes = flat_bvh.traverse(&ray, &shapes); /// ``` fn traverse<'a, B: Bounded>(&'a self, ray: &Ray, shapes: &'a [B]) -> Vec<&B> { @@ -412,11 +412,11 @@ where // Exit the current node. index = node.exit_index as usize; } else if ray.intersects_aabb(&node.aabb) { - // If entry_index is not MAX_UINT32 and the AABB test passes, then + // If entry_index is not MAX_UINT32 and the Aabb test passes, then // proceed to the node in entry_index (which goes down the bvh branch). index = node.entry_index as usize; } else { - // If entry_index is not MAX_UINT32 and the AABB test fails, then + // If entry_index is not MAX_UINT32 and the Aabb test fails, then // proceed to the node in exit_index (which defines the next untested partition). index = node.exit_index as usize; } @@ -425,9 +425,9 @@ where hit_shapes } - /// Prints a textual representation of a [`FlatBVH`]. + /// Prints a textual representation of a [`FlatBvh`]. /// - /// [`FlatBVH`]: struct.FlatBVH.html + /// [`FlatBvh`]: struct.FlatBvh.html /// fn pretty_print(&self) { for (i, node) in self.iter().enumerate() { @@ -441,19 +441,19 @@ where #[cfg(test)] mod tests { - use crate::testbase::{build_some_bh, traverse_some_bh, FlatBVH}; + use crate::testbase::{build_some_bh, traverse_some_bh, TFlatBvh3}; #[test] /// Tests whether the building procedure succeeds in not failing. fn test_build_flat_bvh() { - build_some_bh::(); + build_some_bh::(); } #[test] /// Runs some primitive tests for intersections of a ray with a fixed scene given - /// as a `FlatBVH`. + /// as a `FlatBvh`. fn test_traverse_flat_bvh() { - traverse_some_bh::(); + traverse_some_bh::(); } } @@ -462,53 +462,53 @@ mod bench { use crate::testbase::{ build_1200_triangles_bh, build_120k_triangles_bh, build_12k_triangles_bh, create_n_cubes, default_bounds, intersect_1200_triangles_bh, intersect_120k_triangles_bh, - intersect_12k_triangles_bh, FlatBVH, BVH, + intersect_12k_triangles_bh, TFlatBvh3, TBvh3, }; #[bench] - /// Benchmark the flattening of a BVH with 120,000 triangles. + /// Benchmark the flattening of a Bvh with 120,000 triangles. fn bench_flatten_120k_triangles_bvh(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); - let bvh = BVH::build(&mut triangles); + let bvh = TBvh3::build(&mut triangles); b.iter(|| { bvh.flatten(); }); } #[bench] - /// Benchmark the construction of a `FlatBVH` with 1,200 triangles. + /// Benchmark the construction of a `FlatBvh` with 1,200 triangles. fn bench_build_1200_triangles_flat_bvh(b: &mut ::test::Bencher) { - build_1200_triangles_bh::(b); + build_1200_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `FlatBVH` with 12,000 triangles. + /// Benchmark the construction of a `FlatBvh` with 12,000 triangles. fn bench_build_12k_triangles_flat_bvh(b: &mut ::test::Bencher) { - build_12k_triangles_bh::(b); + build_12k_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `FlatBVH` with 120,000 triangles. + /// Benchmark the construction of a `FlatBvh` with 120,000 triangles. fn bench_build_120k_triangles_flat_bvh(b: &mut ::test::Bencher) { - build_120k_triangles_bh::(b); + build_120k_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 1,200 triangles using the recursive `FlatBVH`. + /// Benchmark intersecting 1,200 triangles using the recursive `FlatBvh`. fn bench_intersect_1200_triangles_flat_bvh(b: &mut ::test::Bencher) { - intersect_1200_triangles_bh::(b); + intersect_1200_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 12,000 triangles using the recursive `FlatBVH`. + /// Benchmark intersecting 12,000 triangles using the recursive `FlatBvh`. fn bench_intersect_12k_triangles_flat_bvh(b: &mut ::test::Bencher) { - intersect_12k_triangles_bh::(b); + intersect_12k_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 120,000 triangles using the recursive `FlatBVH`. + /// Benchmark intersecting 120,000 triangles using the recursive `FlatBvh`. fn bench_intersect_120k_triangles_flat_bvh(b: &mut ::test::Bencher) { - intersect_120k_triangles_bh::(b); + intersect_120k_triangles_bh::(b); } } diff --git a/src/lib.rs b/src/lib.rs index be67b07..b16e28f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,19 +4,19 @@ //! ## About //! //! This crate can be used for applications which contain intersection computations of rays -//! with primitives. For this purpose a binary tree BVH (Bounding Volume Hierarchy) is of great -//! use if the scene which the ray traverses contains a huge number of primitives. With a BVH the +//! with primitives. For this purpose a binary tree Bvh (Bounding Volume Hierarchy) is of great +//! use if the scene which the ray traverses contains a huge number of primitives. With a Bvh the //! intersection test complexity is reduced from O(n) to O(log2(n)) at the cost of building -//! the BVH once in advance. This technique is especially useful in ray/path tracers. For +//! the Bvh once in advance. This technique is especially useful in ray/path tracers. For //! use in a shader this module also exports a flattening procedure, which allows for -//! iterative traversal of the BVH. +//! iterative traversal of the Bvh. //! //! ## Example //! //! ``` -//! use bvh::aabb::{AABB, Bounded}; +//! use bvh::aabb::{Aabb, Bounded}; //! use bvh::bounding_hierarchy::BHShape; -//! use bvh::bvh::BVH; +//! use bvh::bvh::Bvh; //! use nalgebra::{Point3, Vector3}; //! use bvh::ray::Ray; //! @@ -31,11 +31,11 @@ //! } //! //! impl Bounded for Sphere { -//! fn aabb(&self) -> AABB { +//! fn aabb(&self) -> Aabb { //! let half_size = Vector3::new(self.radius, self.radius, self.radius); //! let min = self.position - half_size; //! let max = self.position + half_size; -//! AABB::with_bounds(min, max) +//! Aabb::with_bounds(min, max) //! } //! } //! @@ -60,7 +60,7 @@ //! }); //! } //! -//! let bvh = BVH::build(&mut spheres); +//! let bvh = Bvh::build(&mut spheres); //! let hit_sphere_aabbs = bvh.traverse(&ray, &spheres); //! ``` //! diff --git a/src/ray/intersect_default.rs b/src/ray/intersect_default.rs index 6b5a0bd..40a101a 100644 --- a/src/ray/intersect_default.rs +++ b/src/ray/intersect_default.rs @@ -1,5 +1,5 @@ use super::Ray; -use crate::{aabb::AABB, utils::fast_max}; +use crate::{aabb::Aabb, utils::fast_max}; use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::Zero; @@ -7,7 +7,7 @@ pub trait RayIntersection where T: Copy + Scalar, { - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool; + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool; } #[cfg(not(feature = "simd"))] @@ -15,7 +15,7 @@ impl RayIntersection for Ray where T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, { - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); @@ -33,7 +33,7 @@ impl RayIntersection for Ray where T: Scalar + Copy + ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, { - default fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + default fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let lbr = (aabb[0].coords - self.origin.coords).component_mul(&self.inv_direction); let rtr = (aabb[1].coords - self.origin.coords).component_mul(&self.inv_direction); diff --git a/src/ray/intersect_x86_64.rs b/src/ray/intersect_x86_64.rs index dfb5697..cedd37f 100644 --- a/src/ray/intersect_x86_64.rs +++ b/src/ray/intersect_x86_64.rs @@ -2,7 +2,7 @@ use std::arch::x86_64::*; use nalgebra::SVector; -use crate::{aabb::AABB, utils::fast_max}; +use crate::{aabb::Aabb, utils::fast_max}; use super::{intersect_default::RayIntersection, Ray}; @@ -46,10 +46,10 @@ fn max_elem_m128(mm: __m128) -> f32 { let b = _mm_unpackhi_ps(mm, mm); // z z w w let c = _mm_max_ps(a, b); // ..., max(x, z), ..., ... let res = _mm_max_ps(mm, c); // ..., max(y, max(x, z)), ..., ... - #[allow(invalid_value)] - let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); - _mm_store_ps(data.as_mut_ptr(), res); - return data[1]; + let mut data = std::mem::MaybeUninit::<[f32; 4]>::uninit(); + _mm_store_ps(data.as_mut_ptr() as *mut f32, res); + let data = data.assume_init(); + data[1] } } @@ -60,10 +60,10 @@ fn min_elem_m128(mm: __m128) -> f32 { let b = _mm_unpackhi_ps(mm, mm); // z z w w let c = _mm_min_ps(a, b); // ..., min(x, z), ..., ... let res = _mm_min_ps(mm, c); // ..., min(y, min(x, z)), ..., ... - #[allow(invalid_value)] - let mut data: [f32; 4] = std::mem::MaybeUninit::uninit().assume_init(); - _mm_store_ps(data.as_mut_ptr(), res); - return data[1]; + let mut data = std::mem::MaybeUninit::<[f32; 4]>::uninit(); + _mm_store_ps(data.as_mut_ptr() as *mut f32, res); + let data = data.assume_init(); + data[1] } } @@ -90,7 +90,7 @@ fn ray_intersects_aabb_m128( impl RayIntersection for Ray { #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let ro = self.origin.coords.to_register(); let ri = self.inv_direction.to_register(); let aabb_0 = aabb[0].coords.to_register(); @@ -102,7 +102,7 @@ impl RayIntersection for Ray { impl RayIntersection for Ray { #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let ro = self.origin.coords.to_register(); let ri = self.inv_direction.to_register(); let aabb_0 = aabb[0].coords.to_register(); @@ -114,7 +114,7 @@ impl RayIntersection for Ray { impl RayIntersection for Ray { #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let ro = self.origin.coords.to_register(); let ri = self.inv_direction.to_register(); let aabb_0 = aabb[0].coords.to_register(); @@ -138,10 +138,10 @@ fn max_elem_m128d(mm: __m128d) -> f64 { unsafe { let a = _mm_unpacklo_pd(mm, mm); // x x let b = _mm_max_pd(mm, a); // max(x, x), max(x, y) - #[allow(invalid_value)] - let mut data: [f64; 2] = std::mem::MaybeUninit::uninit().assume_init(); - _mm_store_pd(data.as_mut_ptr(), b); - return data[1]; + let mut data = std::mem::MaybeUninit::<[f64; 2]>::uninit(); + _mm_store_pd(data.as_mut_ptr() as *mut f64, b); + let data = data.assume_init(); + data[1] } } @@ -151,10 +151,10 @@ fn min_elem_m128d(mm: __m128d) -> f64 { let a = _mm_unpacklo_pd(mm, mm); // x x let b = _mm_unpackhi_pd(mm, mm); // y y let c = _mm_min_pd(a, b); // min(x, y), min(x, y) - #[allow(invalid_value)] - let mut data: [f64; 2] = std::mem::MaybeUninit::uninit().assume_init(); - _mm_store_pd(data.as_mut_ptr(), c); - return data[0]; + let mut data = std::mem::MaybeUninit::<[f64; 2]>::uninit(); + _mm_store_pd(data.as_mut_ptr() as *mut f64, c); + let data = data.assume_init(); + data[0] } } @@ -181,7 +181,7 @@ fn ray_intersects_aabb_m128d( impl RayIntersection for Ray { #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let ro = self.origin.coords.to_register(); let ri = self.inv_direction.to_register(); let aabb_0 = aabb[0].coords.to_register(); @@ -216,10 +216,10 @@ fn max_elem_m256d(mm: __m256d) -> f64 { let b = _mm256_unpackhi_pd(mm, mm); // z z w w let c = _mm256_max_pd(a, b); // ..., max(x, z), ..., ... let res = _mm256_max_pd(mm, c); // ..., max(y, max(x, z)), ..., ... - #[allow(invalid_value)] - let mut data: [f64; 4] = std::mem::MaybeUninit::uninit().assume_init(); - _mm256_store_pd(data.as_mut_ptr(), res); - return data[1]; + let mut data = std::mem::MaybeUninit::<[f64; 4]>::uninit(); + _mm256_store_pd(data.as_mut_ptr() as *mut f64, res); + let data = data.assume_init(); + data[1] } } @@ -230,10 +230,10 @@ fn min_elem_m256d(mm: __m256d) -> f64 { let b = _mm256_unpackhi_pd(mm, mm); // z z w w let c = _mm256_min_pd(a, b); // ..., min(x, z), ..., ... let res = _mm256_min_pd(mm, c); // ..., min(y, min(x, z)), ..., ... - #[allow(invalid_value)] - let mut data: [f64; 4] = std::mem::MaybeUninit::uninit().assume_init(); - _mm256_store_pd(data.as_mut_ptr(), res); - return data[1]; + let mut data = std::mem::MaybeUninit::<[f64; 4]>::uninit(); + _mm256_store_pd(data.as_mut_ptr() as *mut f64, res); + let data = data.assume_init(); + data[1] } } @@ -260,7 +260,7 @@ fn ray_intersects_aabb_m256d( impl RayIntersection for Ray { #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let ro = self.origin.coords.to_register(); let ri = self.inv_direction.to_register(); let aabb_0 = aabb[0].coords.to_register(); @@ -272,7 +272,7 @@ impl RayIntersection for Ray { impl RayIntersection for Ray { #[inline(always)] - fn ray_intersects_aabb(&self, aabb: &AABB) -> bool { + fn ray_intersects_aabb(&self, aabb: &Aabb) -> bool { let ro = self.origin.coords.to_register(); let ri = self.inv_direction.to_register(); let aabb_0 = aabb[0].coords.to_register(); diff --git a/src/ray/ray_impl.rs b/src/ray/ray_impl.rs index 759b5b2..dc146be 100644 --- a/src/ray/ray_impl.rs +++ b/src/ray/ray_impl.rs @@ -1,7 +1,7 @@ //! This module defines a Ray structure and intersection algorithms //! for axis aligned bounding boxes and triangles. -use crate::aabb::AABB; +use crate::aabb::Aabb; use nalgebra::{ ClosedAdd, ClosedMul, ClosedSub, ComplexField, Point, SVector, Scalar, SimdPartialOrd, }; @@ -18,9 +18,9 @@ pub struct Ray { /// The ray direction. pub direction: SVector, - /// Inverse (1/x) ray direction. Cached for use in [`AABB`] intersections. + /// Inverse (1/x) ray direction. Cached for use in [`Aabb`] intersections. /// - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// pub inv_direction: SVector, } @@ -76,12 +76,12 @@ impl Ray { } } - /// Tests the intersection of a [`Ray`] with an [`AABB`] using the optimized algorithm + /// Tests the intersection of a [`Ray`] with an [`Aabb`] using the optimized algorithm /// from [this paper](http://www.cs.utah.edu/~awilliam/box/box.pdf). /// /// # Examples /// ``` - /// use bvh::aabb::AABB; + /// use bvh::aabb::Aabb; /// use bvh::ray::Ray; /// use nalgebra::{Point3,Vector3}; /// @@ -91,15 +91,15 @@ impl Ray { /// /// let point1 = Point3::new(99.9,-1.0,-1.0); /// let point2 = Point3::new(100.1,1.0,1.0); - /// let aabb = AABB::with_bounds(point1, point2); + /// let aabb = Aabb::with_bounds(point1, point2); /// /// assert!(ray.intersects_aabb(&aabb)); /// ``` /// /// [`Ray`]: struct.Ray.html - /// [`AABB`]: struct.AABB.html + /// [`Aabb`]: struct.Aabb.html /// - pub fn intersects_aabb(&self, aabb: &AABB) -> bool + pub fn intersects_aabb(&self, aabb: &Aabb) -> bool where T: ClosedSub + ClosedMul + Zero + PartialOrd + SimdPartialOrd, { @@ -180,14 +180,14 @@ mod tests { use std::cmp; use std::f32::INFINITY; - use crate::testbase::{tuple_to_point, tuplevec_small_strategy, Ray, TupleVec, AABB}; + use crate::testbase::{tuple_to_point, tuplevec_small_strategy, TRay3, TupleVec, TAabb3}; use proptest::prelude::*; - /// Generates a random `Ray` which points at at a random `AABB`. - fn gen_ray_to_aabb(data: (TupleVec, TupleVec, TupleVec)) -> (Ray, AABB) { - // Generate a random AABB - let aabb = AABB::empty() + /// Generates a random `Ray` which points at at a random `Aabb`. + fn gen_ray_to_aabb(data: (TupleVec, TupleVec, TupleVec)) -> (TRay3, TAabb3) { + // Generate a random Aabb + let aabb = TAabb3::empty() .grow(&tuple_to_point(&data.0)) .grow(&tuple_to_point(&data.1)); @@ -196,12 +196,12 @@ mod tests { // Generate random ray pointing at the center let pos = tuple_to_point(&data.2); - let ray = Ray::new(pos, center - pos); + let ray = TRay3::new(pos, center - pos); (ray, aabb) } proptest! { - // Test whether a `Ray` which points at the center of an `AABB` intersects it. + // Test whether a `Ray` which points at the center of an `Aabb` intersects it. #[test] fn test_ray_points_at_aabb_center(data in (tuplevec_small_strategy(), tuplevec_small_strategy(), @@ -210,8 +210,8 @@ mod tests { assert!(ray.intersects_aabb(&aabb)); } - // Test whether a `Ray` which points away from the center of an `AABB` - // does not intersect it, unless its origin is inside the `AABB`. + // Test whether a `Ray` which points away from the center of an `Aabb` + // does not intersect it, unless its origin is inside the `Aabb`. #[test] fn test_ray_points_from_aabb_center(data in (tuplevec_small_strategy(), tuplevec_small_strategy(), @@ -250,7 +250,7 @@ mod tests { // Define a ray which points at the triangle let origin = tuple_to_point(&origin); - let ray = Ray::new(origin, point_on_triangle - origin); + let ray = TRay3::new(origin, point_on_triangle - origin); let on_back_side = normal.dot(&(ray.origin - triangle.0)) <= 0.0; // Perform the intersection test @@ -293,25 +293,25 @@ mod bench { use rand::{Rng, SeedableRng}; use test::{black_box, Bencher}; - use crate::testbase::{tuple_to_point, tuple_to_vector, Ray, TupleVec, AABB}; + use crate::testbase::{tuple_to_point, tuple_to_vector, TRay3, TupleVec, TAabb3}; /// Generate a random deterministic `Ray`. - fn random_ray(rng: &mut StdRng) -> Ray { + fn random_ray(rng: &mut StdRng) -> TRay3 { let a = tuple_to_point(&rng.gen::()); let b = tuple_to_vector(&rng.gen::()); - Ray::new(a, b) + TRay3::new(a, b) } - /// Generate a random deterministic `AABB`. - fn random_aabb(rng: &mut StdRng) -> AABB { + /// Generate a random deterministic `Aabb`. + fn random_aabb(rng: &mut StdRng) -> TAabb3 { let a = tuple_to_point(&rng.gen::()); let b = tuple_to_point(&rng.gen::()); - AABB::empty().grow(&a).grow(&b) + TAabb3::empty().grow(&a).grow(&b) } /// Generate the ray and boxes used for benchmarks. - fn random_ray_and_boxes() -> (Ray, Vec) { + fn random_ray_and_boxes() -> (TRay3, Vec) { let seed = [0; 32]; let mut rng = StdRng::from_seed(seed); diff --git a/src/testbase.rs b/src/testbase.rs index 1162d68..dced56f 100644 --- a/src/testbase.rs +++ b/src/testbase.rs @@ -20,13 +20,13 @@ use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; /// A vector represented as a tuple pub type TupleVec = (f32, f32, f32); -pub type Ray = crate::ray::Ray; -pub type AABB = crate::aabb::AABB; -pub type BVH = crate::bvh::BVH; -pub type BVHNode = crate::bvh::BVHNode; -pub type Vector3 = nalgebra::SVector; -pub type Point3 = nalgebra::Point; -pub type FlatBVH = crate::flat_bvh::FlatBVH; +pub type TRay3 = crate::ray::Ray; +pub type TAabb3 = crate::aabb::Aabb; +pub type TBvh3 = crate::bvh::Bvh; +pub type TBvhNode3 = crate::bvh::BvhNode; +pub type TVector3 = nalgebra::SVector; +pub type TPoint3 = nalgebra::Point; +pub type TFlatBvh3 = crate::flat_bvh::FlatBvh; /// Generate a `TupleVec` for [`proptest::strategy::Strategy`] from -10e10 to 10e10 /// A small enough range to prevent most fp32 errors from breaking certain tests @@ -50,24 +50,24 @@ pub fn tuplevec_large_strategy() -> impl Strategy { } /// Convert a `TupleVec` to a [`Point3`]. -pub fn tuple_to_point(tpl: &TupleVec) -> Point3 { - Point3::new(tpl.0, tpl.1, tpl.2) +pub fn tuple_to_point(tpl: &TupleVec) -> TPoint3 { + TPoint3::new(tpl.0, tpl.1, tpl.2) } /// Convert a `TupleVec` to a [`Vector3`]. -pub fn tuple_to_vector(tpl: &TupleVec) -> Vector3 { - Vector3::new(tpl.0, tpl.1, tpl.2) +pub fn tuple_to_vector(tpl: &TupleVec) -> TVector3 { + TVector3::new(tpl.0, tpl.1, tpl.2) } /// Define some `Bounded` structure. pub struct UnitBox { pub id: i32, - pub pos: Point3, + pub pos: TPoint3, node_index: usize, } impl UnitBox { - pub fn new(id: i32, pos: Point3) -> UnitBox { + pub fn new(id: i32, pos: TPoint3) -> UnitBox { UnitBox { id, pos, @@ -76,12 +76,12 @@ impl UnitBox { } } -/// `UnitBox`'s `AABB`s are unit `AABB`s centered on the box's position. +/// `UnitBox`'s `Aabb`s are unit `Aabb`s centered on the box's position. impl Bounded for UnitBox { - fn aabb(&self) -> AABB { - let min = self.pos + Vector3::new(-0.5, -0.5, -0.5); - let max = self.pos + Vector3::new(0.5, 0.5, 0.5); - AABB::with_bounds(min, max) + fn aabb(&self) -> TAabb3 { + let min = self.pos + TVector3::new(-0.5, -0.5, -0.5); + let max = self.pos + TVector3::new(0.5, 0.5, 0.5); + TAabb3::with_bounds(min, max) } } @@ -101,7 +101,7 @@ pub fn generate_aligned_boxes() -> Vec { // Create 21 boxes along the x-axis let mut shapes = Vec::new(); for x in -10..11 { - shapes.push(UnitBox::new(x, Point3::new(x as f32, 0.0, 0.0))); + shapes.push(UnitBox::new(x, TPoint3::new(x as f32, 0.0, 0.0))); } shapes } @@ -116,13 +116,13 @@ pub fn build_some_bh>() -> (Vec, BH) { /// Given a ray, a bounding hierarchy, the complete list of shapes in the scene and a list of /// expected hits, verifies, whether the ray hits only the expected shapes. fn traverse_and_verify>( - ray_origin: Point3, - ray_direction: Vector3, + ray_origin: TPoint3, + ray_direction: TVector3, all_shapes: &[UnitBox], bh: &BH, expected_shapes: &HashSet, ) { - let ray = Ray::new(ray_origin, ray_direction); + let ray = TRay3::new(ray_origin, ray_direction); let hit_shapes = bh.traverse(&ray, all_shapes); assert_eq!(expected_shapes.len(), hit_shapes.len()); @@ -137,8 +137,8 @@ pub fn traverse_some_bh>() { { // Define a ray which traverses the x-axis from afar. - let origin = Point3::new(-1000.0, 0.0, 0.0); - let direction = Vector3::new(1.0, 0.0, 0.0); + let origin = TPoint3::new(-1000.0, 0.0, 0.0); + let direction = TVector3::new(1.0, 0.0, 0.0); let mut expected_shapes = HashSet::new(); // It should hit everything. @@ -150,8 +150,8 @@ pub fn traverse_some_bh>() { { // Define a ray which traverses the y-axis from afar. - let origin = Point3::new(0.0, -1000.0, 0.0); - let direction = Vector3::new(0.0, 1.0, 0.0); + let origin = TPoint3::new(0.0, -1000.0, 0.0); + let direction = TVector3::new(0.0, 1.0, 0.0); // It should hit only one box. let mut expected_shapes = HashSet::new(); @@ -161,8 +161,8 @@ pub fn traverse_some_bh>() { { // Define a ray which intersects the x-axis diagonally. - let origin = Point3::new(6.0, 0.5, 0.0); - let direction = Vector3::new(-2.0, -1.0, 0.0); + let origin = TPoint3::new(6.0, 0.5, 0.0); + let direction = TVector3::new(-2.0, -1.0, 0.0); // It should hit exactly three boxes. let mut expected_shapes = HashSet::new(); @@ -176,27 +176,27 @@ pub fn traverse_some_bh>() { /// A triangle struct. Instance of a more complex `Bounded` primitive. #[derive(Debug)] pub struct Triangle { - pub a: Point3, - pub b: Point3, - pub c: Point3, - aabb: AABB, + pub a: TPoint3, + pub b: TPoint3, + pub c: TPoint3, + aabb: TAabb3, node_index: usize, } impl Triangle { - pub fn new(a: Point3, b: Point3, c: Point3) -> Triangle { + pub fn new(a: TPoint3, b: TPoint3, c: TPoint3) -> Triangle { Triangle { a, b, c, - aabb: AABB::empty().grow(&a).grow(&b).grow(&c), + aabb: TAabb3::empty().grow(&a).grow(&b).grow(&c), node_index: 0, } } } impl Bounded for Triangle { - fn aabb(&self) -> AABB { + fn aabb(&self) -> TAabb3 { self.aabb } } @@ -221,7 +221,7 @@ impl FromRawVertex for Triangle { // Convert the vertices to `Point3`s. let points = vertices .into_iter() - .map(|v| Point3::new(v.0, v.1, v.2)) + .map(|v| TPoint3::new(v.0, v.1, v.2)) .collect::>(); // Estimate for the number of triangles, assuming that each polygon is a triangle. @@ -256,15 +256,15 @@ impl FromRawVertex for Triangle { } /// Creates a unit size cube centered at `pos` and pushes the triangles to `shapes`. -fn push_cube(pos: Point3, shapes: &mut Vec) { - let top_front_right = pos + Vector3::new(0.5, 0.5, -0.5); - let top_back_right = pos + Vector3::new(0.5, 0.5, 0.5); - let top_back_left = pos + Vector3::new(-0.5, 0.5, 0.5); - let top_front_left = pos + Vector3::new(-0.5, 0.5, -0.5); - let bottom_front_right = pos + Vector3::new(0.5, -0.5, -0.5); - let bottom_back_right = pos + Vector3::new(0.5, -0.5, 0.5); - let bottom_back_left = pos + Vector3::new(-0.5, -0.5, 0.5); - let bottom_front_left = pos + Vector3::new(-0.5, -0.5, -0.5); +fn push_cube(pos: TPoint3, shapes: &mut Vec) { + let top_front_right = pos + TVector3::new(0.5, 0.5, -0.5); + let top_back_right = pos + TVector3::new(0.5, 0.5, 0.5); + let top_back_left = pos + TVector3::new(-0.5, 0.5, 0.5); + let top_front_left = pos + TVector3::new(-0.5, 0.5, -0.5); + let bottom_front_right = pos + TVector3::new(0.5, -0.5, -0.5); + let bottom_back_right = pos + TVector3::new(0.5, -0.5, 0.5); + let bottom_back_left = pos + TVector3::new(-0.5, -0.5, 0.5); + let bottom_front_left = pos + TVector3::new(-0.5, -0.5, -0.5); shapes.push(Triangle::new( top_back_right, @@ -344,10 +344,10 @@ pub fn next_point3_raw(seed: &mut u64) -> (i32, i32, i32) { } /// Generates a new `Point3`, which will lie inside the given `aabb`. Mutates the seed. -pub fn next_point3(seed: &mut u64, aabb: &AABB) -> Point3 { +pub fn next_point3(seed: &mut u64, aabb: &TAabb3) -> TPoint3 { let (a, b, c) = next_point3_raw(seed); use std::i32; - let float_vector = Vector3::new( + let float_vector = TVector3::new( (a as f32 / i32::MAX as f32) + 1.0, (b as f32 / i32::MAX as f32) + 1.0, (c as f32 / i32::MAX as f32) + 1.0, @@ -358,7 +358,7 @@ pub fn next_point3(seed: &mut u64, aabb: &AABB) -> Point3 { assert!(float_vector.z >= 0.0 && float_vector.z <= 1.0); let size = aabb.size(); - let offset = Vector3::new( + let offset = TVector3::new( float_vector.x * size.x, float_vector.y * size.y, float_vector.z * size.z, @@ -366,16 +366,16 @@ pub fn next_point3(seed: &mut u64, aabb: &AABB) -> Point3 { aabb.min + offset } -/// Returns an `AABB` which defines the default testing space bounds. -pub fn default_bounds() -> AABB { - AABB::with_bounds( - Point3::new(-100_000.0, -100_000.0, -100_000.0), - Point3::new(100_000.0, 100_000.0, 100_000.0), +/// Returns an `Aabb` which defines the default testing space bounds. +pub fn default_bounds() -> TAabb3 { + TAabb3::with_bounds( + TPoint3::new(-100_000.0, -100_000.0, -100_000.0), + TPoint3::new(100_000.0, 100_000.0, 100_000.0), ) } /// Creates `n` deterministic random cubes. Returns the `Vec` of surface `Triangle`s. -pub fn create_n_cubes(n: usize, bounds: &AABB) -> Vec { +pub fn create_n_cubes(n: usize, bounds: &TAabb3) -> Vec { let mut vec = Vec::new(); let mut seed = 0; for _ in 0..n { @@ -386,7 +386,7 @@ pub fn create_n_cubes(n: usize, bounds: &AABB) -> Vec { /// Loads the sponza model. #[cfg(feature = "bench")] -pub fn load_sponza_scene() -> (Vec, AABB) { +pub fn load_sponza_scene() -> (Vec, TAabb3) { use std::fs::File; use std::io::BufReader; @@ -395,7 +395,7 @@ pub fn load_sponza_scene() -> (Vec, AABB) { let sponza_obj: Obj = load_obj(file_input).expect("Failed to decode .obj file data."); let triangles = sponza_obj.vertices; - let mut bounds = AABB::empty(); + let mut bounds = TAabb3::empty(); for triangle in &triangles { bounds.join_mut(&triangle.aabb()); } @@ -410,7 +410,7 @@ pub fn load_sponza_scene() -> (Vec, AABB) { pub fn randomly_transform_scene( triangles: &mut Vec, amount: usize, - bounds: &AABB, + bounds: &TAabb3, max_offset_option: Option, seed: &mut u64, ) -> HashSet { @@ -434,7 +434,7 @@ pub fn randomly_transform_scene( let aabb = triangles[*index].aabb(); let min_move_bound = bounds.min - aabb.min; let max_move_bound = bounds.max - aabb.max; - let movement_bounds = AABB::with_bounds(min_move_bound.into(), max_move_bound.into()); + let movement_bounds = TAabb3::with_bounds(min_move_bound.into(), max_move_bound.into()); let mut random_offset = next_point3(seed, &movement_bounds); random_offset.x = max_offset.min((-max_offset).max(random_offset.x)); @@ -458,10 +458,10 @@ pub fn randomly_transform_scene( /// The Ray origin will be inside the `bounds` and point to some other point inside this /// `bounds`. #[cfg(feature = "bench")] -pub fn create_ray(seed: &mut u64, bounds: &AABB) -> Ray { +pub fn create_ray(seed: &mut u64, bounds: &TAabb3) -> TRay3 { let origin = next_point3(seed, bounds); let direction = next_point3(seed, bounds); - Ray::new(origin, direction.coords) + TRay3::new(origin, direction.coords) } /// Benchmark the construction of a `BoundingHierarchy` with `n` triangles. @@ -494,7 +494,7 @@ pub fn build_120k_triangles_bh>(b: &mut ::test::Ben /// Benchmark intersecting the `triangles` list without acceleration structures. #[cfg(feature = "bench")] -pub fn intersect_list(triangles: &[Triangle], bounds: &AABB, b: &mut ::test::Bencher) { +pub fn intersect_list(triangles: &[Triangle], bounds: &TAabb3, b: &mut ::test::Bencher) { let mut seed = 0; b.iter(|| { let ray = create_ray(&mut seed, bounds); @@ -523,17 +523,17 @@ fn bench_intersect_sponza_list(b: &mut ::test::Bencher) { intersect_list(&triangles, &bounds, b); } -/// Benchmark intersecting the `triangles` list with `AABB` checks, but without acceleration +/// Benchmark intersecting the `triangles` list with `Aabb` checks, but without acceleration /// structures. #[cfg(feature = "bench")] -pub fn intersect_list_aabb(triangles: &[Triangle], bounds: &AABB, b: &mut ::test::Bencher) { +pub fn intersect_list_aabb(triangles: &[Triangle], bounds: &TAabb3, b: &mut ::test::Bencher) { let mut seed = 0; b.iter(|| { let ray = create_ray(&mut seed, bounds); // Iterate over the list of triangles. for triangle in triangles { - // First test whether the ray intersects the AABB of the triangle. + // First test whether the ray intersects the Aabb of the triangle. if ray.intersects_aabb(&triangle.aabb()) { ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c); } @@ -543,7 +543,7 @@ pub fn intersect_list_aabb(triangles: &[Triangle], bounds: &AABB, b: &mut ::test #[cfg(feature = "bench")] #[bench] -/// Benchmark intersecting 120,000 triangles with preceeding `AABB` tests. +/// Benchmark intersecting 120,000 triangles with preceeding `Aabb` tests. fn bench_intersect_120k_triangles_list_aabb(b: &mut ::test::Bencher) { let bounds = default_bounds(); let triangles = create_n_cubes(10_000, &bounds); @@ -552,7 +552,7 @@ fn bench_intersect_120k_triangles_list_aabb(b: &mut ::test::Bencher) { #[cfg(feature = "bench")] #[bench] -/// Benchmark intersecting 120,000 triangles with preceeding `AABB` tests. +/// Benchmark intersecting 120,000 triangles with preceeding `Aabb` tests. fn bench_intersect_sponza_list_aabb(b: &mut ::test::Bencher) { let (triangles, bounds) = load_sponza_scene(); intersect_list_aabb(&triangles, &bounds, b); @@ -562,7 +562,7 @@ fn bench_intersect_sponza_list_aabb(b: &mut ::test::Bencher) { pub fn intersect_bh>( bh: &T, triangles: &[Triangle], - bounds: &AABB, + bounds: &TAabb3, b: &mut ::test::Bencher, ) { let mut seed = 0; @@ -572,7 +572,7 @@ pub fn intersect_bh>( // Traverse the `BoundingHierarchy` recursively. let hits = bh.traverse(&ray, triangles); - // Traverse the resulting list of positive `AABB` tests + // Traverse the resulting list of positive `Aabb` tests for triangle in &hits { ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c); } diff --git a/src/utils.rs b/src/utils.rs index 90916f7..810f363 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,7 +3,7 @@ use nalgebra::{Scalar, SimdPartialOrd}; use num::Float; -use crate::aabb::AABB; +use crate::aabb::Aabb; use crate::bounding_hierarchy::BHShape; /// Fast floating point minimum. This function matches the semantics of @@ -67,14 +67,14 @@ pub fn concatenate_vectors(vectors: &mut [Vec]) -> Vec { } /// Defines a Bucket utility object. Used to store the properties of shape-partitions -/// in the BVH build procedure using SAH. +/// in the Bvh build procedure using SAH. #[derive(Clone, Copy)] pub struct Bucket { /// The number of shapes in this `Bucket`. pub size: usize, - /// The joint `AABB` of the shapes in this `Bucket`. - pub aabb: AABB, + /// The joint `Aabb` of the shapes in this `Bucket`. + pub aabb: Aabb, } impl Bucket { @@ -82,12 +82,12 @@ impl Bucket { pub fn empty() -> Bucket { Bucket { size: 0, - aabb: AABB::empty(), + aabb: Aabb::empty(), } } - /// Extend this `Bucket` by a shape with the given `AABB`. - pub fn add_aabb(&mut self, aabb: &AABB) { + /// Extend this `Bucket` by a shape with the given `Aabb`. + pub fn add_aabb(&mut self, aabb: &Aabb) { self.size += 1; self.aabb = self.aabb.join(aabb); } @@ -108,8 +108,8 @@ pub fn joint_aabb_of_shapes< >( indices: &[usize], shapes: &[Shape], -) -> AABB { - let mut aabb = AABB::empty(); +) -> Aabb { + let mut aabb = Aabb::empty(); for index in indices { let shape = &shapes[*index]; aabb.join_mut(&shape.aabb()); From 64f89860201039555e51d9d8666e70df4bd7e58b Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Wed, 26 Apr 2023 01:06:27 -0700 Subject: [PATCH 10/20] Formatting fix --- examples/simple.rs | 2 +- src/aabb.rs | 3 ++- src/bvh/bvh_impl.rs | 4 ++-- src/bvh/iter.rs | 4 ++-- src/bvh/optimization.rs | 6 +++--- src/flat_bvh.rs | 6 +++--- src/ray/ray_impl.rs | 4 ++-- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 68f5142..8efca7d 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,4 +1,4 @@ -use bvh::aabb::{Bounded, Aabb}; +use bvh::aabb::{Aabb, Bounded}; use bvh::bounding_hierarchy::BHShape; use bvh::bvh::Bvh; use bvh::ray::Ray; diff --git a/src/aabb.rs b/src/aabb.rs index b9bd1b1..e1080e2 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -704,7 +704,8 @@ impl Bounded for Point { mod tests { use crate::aabb::Bounded; use crate::testbase::{ - tuple_to_point, tuple_to_vector, tuplevec_large_strategy, TPoint3, TupleVec, TVector3, TAabb3, + tuple_to_point, tuple_to_vector, tuplevec_large_strategy, TAabb3, TPoint3, TVector3, + TupleVec, }; use float_eq::assert_float_eq; diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index e9bcdb7..fb62d15 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -7,7 +7,7 @@ use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::{Float, FromPrimitive, Signed, ToPrimitive, Zero}; -use crate::aabb::{Bounded, Aabb}; +use crate::aabb::{Aabb, Bounded}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; //use crate::bounds::ScalarType; use crate::bvh::iter::BvhTraverseIterator; @@ -796,7 +796,7 @@ where #[cfg(test)] mod tests { - use crate::testbase::{build_some_bh, traverse_some_bh, TBvhNode3, TBvh3}; + use crate::testbase::{build_some_bh, traverse_some_bh, TBvh3, TBvhNode3}; #[test] /// Tests whether the building procedure succeeds in not failing. diff --git a/src/bvh/iter.rs b/src/bvh/iter.rs index 6ef31a5..02c7706 100644 --- a/src/bvh/iter.rs +++ b/src/bvh/iter.rs @@ -2,7 +2,7 @@ use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::Zero; use crate::aabb::Bounded; -use crate::bvh::{BvhNode, Bvh}; +use crate::bvh::{Bvh, BvhNode}; use crate::ray::Ray; /// Iterator to traverse a [`Bvh`] without memory allocations @@ -156,7 +156,7 @@ where #[cfg(test)] mod tests { use crate::ray::Ray; - use crate::testbase::{generate_aligned_boxes, TPoint3, UnitBox, TVector3, TBvh3}; + use crate::testbase::{generate_aligned_boxes, TBvh3, TPoint3, TVector3, UnitBox}; use std::collections::HashSet; /// Creates a `Bvh` for a fixed scene structure. diff --git a/src/bvh/optimization.rs b/src/bvh/optimization.rs index 49a9bce..0e6eb13 100644 --- a/src/bvh/optimization.rs +++ b/src/bvh/optimization.rs @@ -536,8 +536,8 @@ mod tests { use crate::aabb::Bounded; use crate::bounding_hierarchy::BHShape; use crate::testbase::{ - build_some_bh, create_n_cubes, default_bounds, randomly_transform_scene, TBvhNode3, TPoint3, - UnitBox, TBvh3, + build_some_bh, create_n_cubes, default_bounds, randomly_transform_scene, TBvh3, TBvhNode3, + TPoint3, UnitBox, }; use std::collections::HashSet; @@ -956,7 +956,7 @@ mod tests { mod bench { use crate::testbase::{ create_n_cubes, default_bounds, intersect_bh, load_sponza_scene, randomly_transform_scene, - Triangle, TAabb3, TBvh3, + TAabb3, TBvh3, Triangle, }; #[bench] diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index c6ea88f..8eb2e82 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -2,9 +2,9 @@ use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::{Float, FromPrimitive, ToPrimitive}; -use crate::aabb::{Bounded, Aabb}; +use crate::aabb::{Aabb, Bounded}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; -use crate::bvh::{BvhNode, Bvh}; +use crate::bvh::{Bvh, BvhNode}; use crate::ray::Ray; /// A structure of a node of a flat [`Bvh`]. The structure of the nodes allows for an @@ -462,7 +462,7 @@ mod bench { use crate::testbase::{ build_1200_triangles_bh, build_120k_triangles_bh, build_12k_triangles_bh, create_n_cubes, default_bounds, intersect_1200_triangles_bh, intersect_120k_triangles_bh, - intersect_12k_triangles_bh, TFlatBvh3, TBvh3, + intersect_12k_triangles_bh, TBvh3, TFlatBvh3, }; #[bench] diff --git a/src/ray/ray_impl.rs b/src/ray/ray_impl.rs index dc146be..e9e8018 100644 --- a/src/ray/ray_impl.rs +++ b/src/ray/ray_impl.rs @@ -180,7 +180,7 @@ mod tests { use std::cmp; use std::f32::INFINITY; - use crate::testbase::{tuple_to_point, tuplevec_small_strategy, TRay3, TupleVec, TAabb3}; + use crate::testbase::{tuple_to_point, tuplevec_small_strategy, TAabb3, TRay3, TupleVec}; use proptest::prelude::*; @@ -293,7 +293,7 @@ mod bench { use rand::{Rng, SeedableRng}; use test::{black_box, Bencher}; - use crate::testbase::{tuple_to_point, tuple_to_vector, TRay3, TupleVec, TAabb3}; + use crate::testbase::{tuple_to_point, tuple_to_vector, TAabb3, TRay3, TupleVec}; /// Generate a random deterministic `Ray`. fn random_ray(rng: &mut StdRng) -> TRay3 { From 1c63a6d444b479708203774229eff216a8faec2f Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Wed, 26 Apr 2023 23:52:45 -0700 Subject: [PATCH 11/20] Remove upper-case acronyms flags --- src/aabb.rs | 1 - src/bounding_hierarchy.rs | 1 - src/bvh/bvh_impl.rs | 2 -- src/bvh/iter.rs | 1 - src/bvh/optimization.rs | 1 - src/flat_bvh.rs | 1 - 6 files changed, 7 deletions(-) diff --git a/src/aabb.rs b/src/aabb.rs index e1080e2..0766f73 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -19,7 +19,6 @@ use num::Zero; /// Aabb struct. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[allow(clippy::upper_case_acronyms)] pub struct Aabb { /// Minimum coordinates pub min: Point, diff --git a/src/bounding_hierarchy.rs b/src/bounding_hierarchy.rs index 7356406..01d7cba 100644 --- a/src/bounding_hierarchy.rs +++ b/src/bounding_hierarchy.rs @@ -10,7 +10,6 @@ use crate::ray::Ray; /// /// [`BoundingHierarchy`]: struct.BoundingHierarchy.html /// -#[allow(clippy::upper_case_acronyms)] pub trait BHShape: Bounded { /// Sets the index of the referenced [`BoundingHierarchy`] node. /// diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index fb62d15..eabad24 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -25,7 +25,6 @@ use crate::utils::{concatenate_vectors, joint_aabb_of_shapes, Bucket}; /// #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[allow(clippy::upper_case_acronyms)] pub enum BvhNode { /// Leaf node. Leaf { @@ -428,7 +427,6 @@ impl BvhNode { /// /// [`Bvh`]: struct.Bvh.html /// -#[allow(clippy::upper_case_acronyms)] #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Bvh { diff --git a/src/bvh/iter.rs b/src/bvh/iter.rs index 02c7706..ae43969 100644 --- a/src/bvh/iter.rs +++ b/src/bvh/iter.rs @@ -6,7 +6,6 @@ use crate::bvh::{Bvh, BvhNode}; use crate::ray::Ray; /// Iterator to traverse a [`Bvh`] without memory allocations -#[allow(clippy::upper_case_acronyms)] pub struct BvhTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, Shape: Bounded> { /// Reference to the Bvh to traverse diff --git a/src/bvh/optimization.rs b/src/bvh/optimization.rs index 0e6eb13..935bc84 100644 --- a/src/bvh/optimization.rs +++ b/src/bvh/optimization.rs @@ -22,7 +22,6 @@ use std::collections::HashSet; // TODO Consider: Stop updating Aabbs upwards the tree once an Aabb didn't get changed. #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] -#[allow(clippy::upper_case_acronyms)] enum OptimizationIndex { Refit(usize), FixAabbs(usize), diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index 8eb2e82..c250695 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -140,7 +140,6 @@ impl BvhNode { /// [`FlatNode`]: struct.FlatNode.html /// [`FlatBvh`]: struct.FlatBvh.html /// -#[allow(clippy::upper_case_acronyms)] pub type FlatBvh = Vec>; impl Bvh { From 90feed531f2640b46e5e78cc34359439f7b622ca Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 08:47:15 -0700 Subject: [PATCH 12/20] Bring back capitalization in docs & prose --- Cargo.toml | 2 +- README.md | 38 +++++++++++++++++++------------------- src/lib.rs | 8 ++++---- src/utils.rs | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5fa7c41..199e3aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bvh" -description = "A fast Bvh using SAH" +description = "A fast BVH using SAH" version = "0.7.2" edition = "2018" authors = [ diff --git a/README.md b/README.md index 08be538..1a4c5a7 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ volume hierarchies.** ## About This crate can be used for applications which contain intersection computations of rays -with primitives. For this purpose a binary tree Bvh (Bounding Volume Hierarchy) is of great -use if the scene which the ray traverses contains a huge number of primitives. With a Bvh the +with primitives. For this purpose a binary tree BVH (Bounding Volume Hierarchy) is of great +use if the scene which the ray traverses contains a huge number of primitives. With a BVH the intersection test complexity is reduced from O(n) to O(log2(n)) at the cost of building -the Bvh once in advance. This technique is especially useful in ray/path tracers. For +the BVH once in advance. This technique is especially useful in ray/path tracers. For use in a shader this module also exports a flattening procedure, which allows for -iterative traversal of the Bvh. +iterative traversal of the BVH. ## Example @@ -85,8 +85,8 @@ To enable these optimziations, you must build with the `nightly` toolchain and e ## Optimization -This crate provides Bvh updating, which is also called optimization. With Bvh optimization -you can mutate the shapes on which the Bvh is built and update the tree accordingly without rebuilding it completely. +This crate provides BVH updating, which is also called optimization. With BVH optimization +you can mutate the shapes on which the BVH is built and update the tree accordingly without rebuilding it completely. This method is very useful when there are only very few changes to a huge scene. When the major part of the scene is static, it is faster to update the tree, instead of rebuilding it from scratch. @@ -96,10 +96,10 @@ First of all, optimizing is not helpful if more than half of the scene is not st This is due to how optimizing takes place: Given a set of indices of all shapes which have changed, the optimize procedure tries to rotate fixed constellations in search for a better surface area heuristic (SAH) value. This is done recursively from bottom to top while fixing the Aabbs -in the inner nodes of the Bvh. Which is why it is inefficient to update the Bvh in comparison to rebuilding, when a lot +in the inner nodes of the BVH. Which is why it is inefficient to update the BVH in comparison to rebuilding, when a lot of shapes have moved. -Another problem with updated Bvhs is, that the resulting Bvh is not optimal. Assume that the scene is composed of two major +Another problem with updated BVHs is, that the resulting BVH is not optimal. Assume that the scene is composed of two major groups separated by a large gap. When one shape moves from one group to another, the updating procedure will not be able to find a sequence of bottom-up rotations which inserts the shape deeply into the other branch. @@ -126,7 +126,7 @@ test testbase::bench_intersect_sponza_list_aabb ... ben Aabb checks are cheap, compared to triangle-intersection algorithms. Therefore, preceeding Aabb checks increase intersection speed by filtering negative results a lot faster. -### Build of a Bvh from scratch +### Build of a BVH from scratch ```C test flat_bvh::bench::bench_build_1200_triangles_flat_bvh ... bench: 538,474 ns/iter (+/- 4,001) @@ -138,17 +138,17 @@ test bvh::bvh::bench::bench_build_120k_triangles_bvh ... ben test bvh::bvh::bench::bench_build_sponza_bvh ... bench: 46,802,305 ns/iter (+/- 184,644) ``` -### Flatten a Bvh +### Flatten a BVH ```C test flat_bvh::bench::bench_flatten_120k_triangles_bvh ... bench: 3,891,505 ns/iter (+/- 42,360) ``` -As you can see, building a Bvh takes a long time. Building a Bvh is only useful if the number of intersections performed on the +As you can see, building a BVH takes a long time. Building a BVH is only useful if the number of intersections performed on the scene exceeds the build duration. This is the case in applications such as ray and path tracing, where the minimum number of intersections is `1280 * 720` for an HD image. -### Intersection via Bvh traversal +### Intersection via BVH traversal ```C test flat_bvh::bench::bench_intersect_1200_triangles_flat_bvh ... bench: 168 ns/iter (+/- 2) @@ -163,7 +163,7 @@ test ray::bench::bench_intersects_aabb_branchless ... ben test ray::bench::bench_intersects_aabb_naive ... bench: 34,958 ns/iter (+/- 259) ``` -These performance measurements show that traversing a Bvh is much faster than traversing a list. +These performance measurements show that traversing a BVH is much faster than traversing a list. ### Optimization @@ -180,13 +180,13 @@ test bvh::optimization::bench::bench_randomize_120k_50p ... ben This is the place where you have to differentiate between rebuilding the tree from scratch or trying to optimize the old one. These tests show the impact of moving around a particular percentage of shapes (`10p` => `10%`). It is important to note that the randomization process here moves triangles around indiscriminately. -This will also lead to cases where the Bvh would have to be restructured completely. +This will also lead to cases where the BVH would have to be restructured completely. ### Intersection after the optimization -These intersection tests are grouped by dataset and by the Bvh generation method. -* `_after_optimize` uses a Bvh which was kept up to date with calls to `optimize`, while -* `_with_rebuild` uses the same triangle data as `_after_optimize`, but constructs a Bvh from scratch. +These intersection tests are grouped by dataset and by the BVH generation method. +* `_after_optimize` uses a BVH which was kept up to date with calls to `optimize`, while +* `_with_rebuild` uses the same triangle data as `_after_optimize`, but constructs a BVH from scratch. *120K Triangles* ```C @@ -218,9 +218,9 @@ This set of tests shows the impact of randomly moving triangles around and produ The *120K Triangles* dataset has been updated randomly. The *Sponza* scene was updated using a method which has a maximum offset distance for shapes. This simulates a more realistic scenario. -We also see that the *Sponza* scene by itself contains some structures which can be tightly wrapped in a Bvh. +We also see that the *Sponza* scene by itself contains some structures which can be tightly wrapped in a BVH. By mowing those structures around we destroy the locality of the triangle groups which leads to more branches in the -Bvh requiring a check, thus leading to a higher intersection duration. +BVH requiring a check, thus leading to a higher intersection duration. ### Running the benchmark suite diff --git a/src/lib.rs b/src/lib.rs index b16e28f..204d81f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,12 @@ //! ## About //! //! This crate can be used for applications which contain intersection computations of rays -//! with primitives. For this purpose a binary tree Bvh (Bounding Volume Hierarchy) is of great -//! use if the scene which the ray traverses contains a huge number of primitives. With a Bvh the +//! with primitives. For this purpose a binary tree BVH (Bounding Volume Hierarchy) is of great +//! use if the scene which the ray traverses contains a huge number of primitives. With a BVH the //! intersection test complexity is reduced from O(n) to O(log2(n)) at the cost of building -//! the Bvh once in advance. This technique is especially useful in ray/path tracers. For +//! the BVH once in advance. This technique is especially useful in ray/path tracers. For //! use in a shader this module also exports a flattening procedure, which allows for -//! iterative traversal of the Bvh. +//! iterative traversal of the BVH. //! //! ## Example //! diff --git a/src/utils.rs b/src/utils.rs index 810f363..471a7e7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -67,7 +67,7 @@ pub fn concatenate_vectors(vectors: &mut [Vec]) -> Vec { } /// Defines a Bucket utility object. Used to store the properties of shape-partitions -/// in the Bvh build procedure using SAH. +/// in the BVH build procedure using SAH. #[derive(Clone, Copy)] pub struct Bucket { /// The number of shapes in this `Bucket`. From 1493fe4504941854672fd3f8b66c4421a738d816 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 08:50:23 -0700 Subject: [PATCH 13/20] CR changes to ci file --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ff1233..154bda5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,10 +23,10 @@ jobs: toolchain: nightly components: clippy - name: Run clippy - run: cargo clippy --workspace --all-targets --all-features -- -D warnings + run: cargo clippy --all-targets --all-features -- -D warnings - build-and-test-no-features: - name: CI with ${{ matrix.rust }} on ${{ matrix.os }} [no-features] + build-and-test-no-simd: + name: CI with ${{ matrix.rust }} on ${{ matrix.os }} [no SIMD] runs-on: ${{ matrix.os }} strategy: matrix: @@ -40,7 +40,7 @@ jobs: toolchain: ${{ matrix.rust }} - name: cargo build - run: cargo build --workspace + run: cargo build - name: cargo test run: cargo test @@ -50,7 +50,7 @@ jobs: if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable' build-and-test-simd: - name: CI with nightly on ${{ matrix.os }} [simd] + name: CI with nightly on ${{ matrix.os }} [SIMD] runs-on: ${{ matrix.os }} strategy: matrix: @@ -61,7 +61,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - name: cargo build - run: cargo build --workspace --features simd + run: cargo build --features simd - name: cargo test run: cargo test --features simd From b48e0a7911a7b7588f5d673a896164013dee5687 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 09:04:24 -0700 Subject: [PATCH 14/20] Organize imports --- src/aabb.rs | 15 ++------------- src/bvh/bvh_impl.rs | 1 - src/bvh/iter.rs | 6 +++--- src/flat_bvh.rs | 6 +++--- src/testbase.rs | 9 ++++----- src/utils.rs | 6 +++--- 6 files changed, 15 insertions(+), 28 deletions(-) diff --git a/src/aabb.rs b/src/aabb.rs index 0766f73..68b2c06 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -1,21 +1,10 @@ //! Axis Aligned Bounding Boxes. +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Point, SVector, Scalar, SimdPartialOrd}; +use num::{Float, FromPrimitive, One, Signed, Zero}; use std::fmt; use std::ops::Index; -use nalgebra::ClosedAdd; -use nalgebra::ClosedMul; -use nalgebra::ClosedSub; -use nalgebra::Point; -use nalgebra::SVector; -use nalgebra::Scalar; -use nalgebra::SimdPartialOrd; -use num::Float; -use num::FromPrimitive; -use num::One; -use num::Signed; -use num::Zero; - /// Aabb struct. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index eabad24..09f3fe7 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -9,7 +9,6 @@ use num::{Float, FromPrimitive, Signed, ToPrimitive, Zero}; use crate::aabb::{Aabb, Bounded}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; -//use crate::bounds::ScalarType; use crate::bvh::iter::BvhTraverseIterator; use crate::ray::Ray; use crate::utils::{concatenate_vectors, joint_aabb_of_shapes, Bucket}; diff --git a/src/bvh/iter.rs b/src/bvh/iter.rs index ae43969..c890758 100644 --- a/src/bvh/iter.rs +++ b/src/bvh/iter.rs @@ -1,10 +1,10 @@ -use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; -use num::Zero; - use crate::aabb::Bounded; use crate::bvh::{Bvh, BvhNode}; use crate::ray::Ray; +use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::Zero; + /// Iterator to traverse a [`Bvh`] without memory allocations pub struct BvhTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, Shape: Bounded> { diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index c250695..8d0213f 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -1,12 +1,12 @@ //! This module exports methods to flatten the `Bvh` and traverse it iteratively. -use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; -use num::{Float, FromPrimitive, ToPrimitive}; - use crate::aabb::{Aabb, Bounded}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; use crate::bvh::{Bvh, BvhNode}; use crate::ray::Ray; +use nalgebra::{ClosedAdd, ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; +use num::{Float, FromPrimitive, ToPrimitive}; + /// A structure of a node of a flat [`Bvh`]. The structure of the nodes allows for an /// iterative traversal approach without the necessity to maintain a stack or queue. /// diff --git a/src/testbase.rs b/src/testbase.rs index dced56f..f9688fa 100644 --- a/src/testbase.rs +++ b/src/testbase.rs @@ -1,8 +1,8 @@ //! Common utilities shared by unit tests. #![cfg(test)] -use std::collections::HashSet; -use std::f32; +use crate::aabb::Bounded; +use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; use num::{FromPrimitive, Integer}; use obj::raw::object::Polygon; @@ -11,9 +11,8 @@ use proptest::prelude::*; use rand::rngs::StdRng; use rand::seq::SliceRandom; use rand::SeedableRng; - -use crate::aabb::Bounded; -use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; +use std::collections::HashSet; +use std::f32; // TODO These all need to be realtyped and bounded diff --git a/src/utils.rs b/src/utils.rs index 471a7e7..25a7b70 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,11 +1,11 @@ //! Utilities module. -use nalgebra::{Scalar, SimdPartialOrd}; -use num::Float; - use crate::aabb::Aabb; use crate::bounding_hierarchy::BHShape; +use nalgebra::{Scalar, SimdPartialOrd}; +use num::Float; + /// Fast floating point minimum. This function matches the semantics of /// /// ```no_compile From e37e3119a689d7f0055a99ec7d592c0c01b5fe89 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 09:20:00 -0700 Subject: [PATCH 15/20] Documentation updates --- CHANGELOG.md | 20 ++++++++++++++++++++ README.md | 4 ++++ src/lib.rs | 7 +++++++ 3 files changed, 31 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..917b2e3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,20 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] - ReleaseDate + +### Added + +- BVH now works with 2d+ dimensions +- BVH now works with f32/f64 +- `simd` feature flag, allows for optimizations via explicit SIMD instructions on nightly + +### Changed + +- Moved from glam to nalgebra +- Code uppercase acronyms changed to API conventions (BVH -> Bvh) +- Major performance improvements on BVH optimization \ No newline at end of file diff --git a/README.md b/README.md index 1a4c5a7..48a3cbb 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,10 @@ These performance measurements show that traversing a BVH is much faster than tr ### Optimization +> **Warning** +> The optimization benchmarks here are no longer current, and perform around 1/4 as fast as the current implementation. +> This section needs to be revisited. + The benchmarks for how long it takes to update the scene also contain a randomization process which takes some time. ```C diff --git a/src/lib.rs b/src/lib.rs index 204d81f..8382a7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,12 @@ //! the BVH once in advance. This technique is especially useful in ray/path tracers. For //! use in a shader this module also exports a flattening procedure, which allows for //! iterative traversal of the BVH. +//! +//! ## Note +//! +//! If you are concerned about performance and do not mind using nightly, it is recommended to +//! use the `simd` feature as it introduces explicitly written simd to optimize certain areas +//! of the BVH. //! //! ## Example //! @@ -67,6 +73,7 @@ //! ## Features //! //! - `serde` (default **disabled**) - adds `Serialize` and `Deserialize` implementations for some types +//! - 'simd' (default **disabled**) - adds explicitly written SIMD instructions for certain architectures (requires nightly) //! #![deny(missing_docs)] From 9804f7ab9895069e17110fae41a85e973bfb3999 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 09:59:47 -0700 Subject: [PATCH 16/20] Fix format on lib.rs --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8382a7e..810b7bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,10 +10,10 @@ //! the BVH once in advance. This technique is especially useful in ray/path tracers. For //! use in a shader this module also exports a flattening procedure, which allows for //! iterative traversal of the BVH. -//! +//! //! ## Note -//! -//! If you are concerned about performance and do not mind using nightly, it is recommended to +//! +//! If you are concerned about performance and do not mind using nightly, it is recommended to //! use the `simd` feature as it introduces explicitly written simd to optimize certain areas //! of the BVH. //! From cdbe27405cf3d6f5df87aaa045544f4a2893579a Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 12:53:20 -0700 Subject: [PATCH 17/20] Add comments to fast_min/fast_max --- src/utils.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index 25a7b70..09a78e1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,6 +21,8 @@ use num::Float; /// min( NaN, 1.0): 1.0 /// min( 1.0, NaN): NaN /// ``` +/// +/// Note: This exists because std::cmp::min requires Ord which floating point types do not satisfy #[inline(always)] #[allow(dead_code)] pub fn fast_min(x: T, y: T) -> T { @@ -46,6 +48,8 @@ pub fn fast_min(x: T, y: T) -> T { /// max( NaN, 1.0): 1.0 /// max( 1.0, NaN): NaN /// ``` +/// +/// Note: This exists because std::cmp::max requires Ord which floating point types do not satisfy #[inline(always)] #[allow(dead_code)] pub fn fast_max(x: T, y: T) -> T { From 608f897db03f88622b8bb058e345cd1af069b971 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Thu, 27 Apr 2023 20:28:18 -0700 Subject: [PATCH 18/20] Fix Aabb in prose to AABB --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 48a3cbb..9f7af60 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ it is faster to update the tree, instead of rebuilding it from scratch. First of all, optimizing is not helpful if more than half of the scene is not static. This is due to how optimizing takes place: Given a set of indices of all shapes which have changed, the optimize procedure tries to rotate fixed constellations -in search for a better surface area heuristic (SAH) value. This is done recursively from bottom to top while fixing the Aabbs +in search for a better surface area heuristic (SAH) value. This is done recursively from bottom to top while fixing the AABBs in the inner nodes of the BVH. Which is why it is inefficient to update the BVH in comparison to rebuilding, when a lot of shapes have moved. @@ -116,14 +116,14 @@ test testbase::bench_intersect_sponza_list ... ben This is the most naive approach to intersecting a scene with a ray. It defines the baseline. -### Intersection via traversal of the list of triangles with Aabb checks +### Intersection via traversal of the list of triangles with AABB checks ```C test testbase::bench_intersect_120k_triangles_list_aabb ... bench: 229,088 ns/iter (+/- 6,727) test testbase::bench_intersect_sponza_list_aabb ... bench: 107,514 ns/iter (+/- 1,511) ``` -Aabb checks are cheap, compared to triangle-intersection algorithms. Therefore, preceeding Aabb checks +AABB checks are cheap, compared to triangle-intersection algorithms. Therefore, preceeding AABB checks increase intersection speed by filtering negative results a lot faster. ### Build of a BVH from scratch From e59f1f0896f5ff1f33d143ba802fa6fb38a0d536 Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Wed, 3 May 2023 22:26:18 -0700 Subject: [PATCH 19/20] Document spring cleaning! --- Cargo.toml | 1 + src/aabb.rs | 30 ++++++++--------- src/bounding_hierarchy.rs | 2 +- src/bvh/bvh_impl.rs | 38 +++++++++++----------- src/bvh/iter.rs | 14 ++++---- src/bvh/optimization.rs | 54 +++++++++++++++---------------- src/flat_bvh.rs | 22 ++++++------- src/lib.rs | 10 +++--- src/ray/intersect_default.rs | 4 +++ src/ray/intersect_x86_64.rs | 3 ++ src/ray/mod.rs | 3 +- src/ray/ray_impl.rs | 8 ++--- src/testbase.rs | 62 ++++++++++++++++++------------------ src/utils.rs | 16 +++++----- 14 files changed, 137 insertions(+), 130 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 199e3aa..0ff6b03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" authors = [ "Sven-Hendrik Haase ", "Alexander Dmitriev ", + "Marios Staikopoulos ", ] readme = "README.md" repository = "https://github.com/svenstaro/bvh" diff --git a/src/aabb.rs b/src/aabb.rs index 68b2c06..4c17441 100644 --- a/src/aabb.rs +++ b/src/aabb.rs @@ -5,7 +5,7 @@ use num::{Float, FromPrimitive, One, Signed, Zero}; use std::fmt; use std::ops::Index; -/// Aabb struct. +/// [`Aabb`] struct. #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Aabb { @@ -109,7 +109,7 @@ impl Aabb { /// let y = rand::random(); /// let z = rand::random(); /// - /// // An empty Aabb should not contain it + /// // An empty `Aabb` should not contain it /// assert!(x < min.x && y < min.y && z < min.z); /// assert!(max.x < x && max.y < y && max.z < z); /// # } @@ -143,7 +143,7 @@ impl Aabb { /// let y = rand::random(); /// let z = rand::random(); /// - /// // An infintie Aabb should contain it + /// // An infinite `Aabb` should contain it /// assert!(x > min.x && y > min.y && z > min.z); /// assert!(max.x > x && max.y > y && max.z > z); /// # } @@ -161,7 +161,7 @@ impl Aabb { } } - /// Returns true if the [`Point3`] is inside the [`Aabb`]. + /// Returns true if the [`Point`] is inside the [`Aabb`]. /// /// # Examples /// ``` @@ -177,7 +177,7 @@ impl Aabb { /// ``` /// /// [`Aabb`]: struct.Aabb.html - /// [`Point3`]: nalgebra::Vec3 + /// [`Point`]: nalgebra::Point /// pub fn contains(&self, p: &Point) -> bool where @@ -775,7 +775,7 @@ mod tests { assert!(aabb1_contains_init_five && aabb2_contains_last_five && aabbu_contains_all); } - // Test whether some points relative to the center of an Aabb are classified correctly. + // Test whether some points relative to the center of an `Aabb` are classified correctly. // Currently doesn't test `approx_contains_eps` or `contains` very well due to scaling by 0.9 and 1.1. #[test] fn test_points_relative_to_center_and_size(a in tuplevec_large_strategy(), b in tuplevec_large_strategy()) { @@ -789,11 +789,11 @@ mod tests { let size_half = size / 2.0; let center = aabb.center(); - // Compute the min and the max corners of the Aabb by hand + // Compute the min and the max corners of the `Aabb` by hand let inside_ppp = center + size_half * 0.9; let inside_mmm = center - size_half * 0.9; - // Generate two points which are outside the Aabb + // Generate two points which are outside the `Aabb` let outside_ppp = inside_ppp + size_half * 1.1; let outside_mmm = inside_mmm - size_half * 1.1; @@ -803,7 +803,7 @@ mod tests { assert!(!aabb.contains(&outside_mmm)); } - // Test whether the surface of a nonempty Aabb is always positive. + // Test whether the surface of a nonempty `Aabb is always positive. #[test] fn test_surface_always_positive(a: TupleVec, b: TupleVec) { let aabb = TAabb3::empty() @@ -812,7 +812,7 @@ mod tests { assert!(aabb.surface_area() >= 0.0); } - // Compute and compare the surface area of an Aabb by hand. + // Compute and compare the surface area of an `Aabb` by hand. #[test] fn test_surface_area_cube(pos: TupleVec, size in f32::EPSILON..10e30_f32) { // Generate some non-empty Aabb @@ -826,7 +826,7 @@ mod tests { assert_float_eq!(area_a, area_b, rmax <= f32::EPSILON); } - // Test whether the volume of a nonempty Aabb is always positive. + // Test whether the volume of a nonempty `Aabb` is always positive. #[test] fn test_volume_always_positive(a in tuplevec_large_strategy(), b in tuplevec_large_strategy()) { let aabb = TAabb3::empty() @@ -835,7 +835,7 @@ mod tests { assert!(aabb.volume() >= 0.0); } - // Compute and compare the volume of an Aabb by hand. + // Compute and compare the volume of an `Aabb` by hand. #[test] fn test_volume_by_hand(pos in tuplevec_large_strategy(), size in tuplevec_large_strategy()) { // Generate some non-empty Aabb @@ -855,15 +855,15 @@ mod tests { // Create a random point let point = tuple_to_point(&p); - // Create a random Aabb + // Create a random `Aabb` let aabb = TAabb3::empty() .grow(&tuple_to_point(&a)) .grow(&tuple_to_point(&b)); - // Create an Aabb by using the index-access method + // Create an `Aabb` by using the index-access method let aabb_by_index = TAabb3::with_bounds(aabb[0], aabb[1]); - // The Aabbs should be the same + // The `Aabb`s should be the same assert!(aabb.contains(&point) == aabb_by_index.contains(&point)); } } diff --git a/src/bounding_hierarchy.rs b/src/bounding_hierarchy.rs index 01d7cba..79b249b 100644 --- a/src/bounding_hierarchy.rs +++ b/src/bounding_hierarchy.rs @@ -1,4 +1,4 @@ -//! This module defines the `BoundingHierarchy` trait. +//! This module defines the [`BoundingHierarchy`] trait. use nalgebra::Scalar; diff --git a/src/bvh/bvh_impl.rs b/src/bvh/bvh_impl.rs index 09f3fe7..caaa962 100644 --- a/src/bvh/bvh_impl.rs +++ b/src/bvh/bvh_impl.rs @@ -47,19 +47,19 @@ pub enum BvhNode { /// Index of the left subtree's root node. child_l_index: usize, - /// The convex hull of the shapes' `Aabb`s in child_l. + /// The convex hull of the shapes' [`Aabb`]'s in child_l. child_l_aabb: Aabb, /// Index of the right subtree's root node. child_r_index: usize, - /// The convex hull of the shapes' `Aabb`s in child_r. + /// The convex hull of the shapes' [`Aabb`]'s in child_r. child_r_aabb: Aabb, }, } impl PartialEq for BvhNode { - // TODO Consider also comparing Aabbs + // TODO Consider also comparing [`Aabbs`] fn eq(&self, other: &BvhNode) -> bool { match (self, other) { ( @@ -142,7 +142,7 @@ impl BvhNode { } } - /// Returns a mutable reference to the `Aabb` of the left child node. + /// Returns a mutable reference to the [`Aabb`] of the left child node. pub fn child_l_aabb_mut(&mut self) -> &mut Aabb { match *self { BvhNode::Node { @@ -161,7 +161,7 @@ impl BvhNode { } } - /// Returns the `Aabb` of the right child node. + /// Returns the [`Aabb`] of the right child node. pub fn child_r_aabb(&self) -> Aabb { match *self { BvhNode::Node { child_r_aabb, .. } => child_r_aabb, @@ -169,7 +169,7 @@ impl BvhNode { } } - /// Returns a mutable reference to the `Aabb` of the right child node. + /// Returns a mutable reference to the [`Aabb`] of the right child node. pub fn child_r_aabb_mut(&mut self) -> &mut Aabb { match *self { BvhNode::Node { @@ -187,9 +187,9 @@ impl BvhNode { } } - /// Gets the `Aabb` for a `BvhNode`. - /// Returns the shape's `Aabb` for leaves, and the joined `Aabb` of - /// the two children's `Aabb`s for non-leaves. + /// Gets the [`Aabb`] for a [`BvhNode`]. + /// Returns the shape's [`Aabb`] for leaves, and the joined [`Aabb`] of + /// the two children's [`Aabb`]'s for non-leaves. pub fn get_node_aabb>(&self, shapes: &[Shape]) -> Aabb where T: SimdPartialOrd, @@ -205,7 +205,7 @@ impl BvhNode { } /// Returns the index of the shape contained within the node if is a leaf, - /// or `None` if it is an interior node. + /// or [`None`] if it is an interior node. pub fn shape_index(&self) -> Option { match *self { BvhNode::Leaf { shape_index, .. } => Some(shape_index), @@ -802,7 +802,7 @@ mod tests { } #[test] - /// Runs some primitive tests for intersections of a ray with a fixed scene given as a Bvh. + /// Runs some primitive tests for intersections of a ray with a fixed scene given as a [`Bvh`]. fn test_traverse_bvh() { traverse_some_bh::(); } @@ -845,25 +845,25 @@ mod bench { }; #[bench] - /// Benchmark the construction of a `Bvh` with 1,200 triangles. + /// Benchmark the construction of a [`Bvh`] with 1,200 triangles. fn bench_build_1200_triangles_bvh(b: &mut ::test::Bencher) { build_1200_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `Bvh` with 12,000 triangles. + /// Benchmark the construction of a [`Bvh`] with 12,000 triangles. fn bench_build_12k_triangles_bvh(b: &mut ::test::Bencher) { build_12k_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `Bvh` with 120,000 triangles. + /// Benchmark the construction of a [`Bvh`] with 120,000 triangles. fn bench_build_120k_triangles_bvh(b: &mut ::test::Bencher) { build_120k_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `Bvh` for the Sponza scene. + /// Benchmark the construction of a [`Bvh`] for the Sponza scene. fn bench_build_sponza_bvh(b: &mut ::test::Bencher) { let (mut triangles, _) = load_sponza_scene(); b.iter(|| { @@ -872,25 +872,25 @@ mod bench { } #[bench] - /// Benchmark intersecting 1,200 triangles using the recursive `Bvh`. + /// Benchmark intersecting 1,200 triangles using the recursive [`Bvh`]. fn bench_intersect_1200_triangles_bvh(b: &mut ::test::Bencher) { intersect_1200_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 12,000 triangles using the recursive `Bvh`. + /// Benchmark intersecting 12,000 triangles using the recursive [`Bvh`]. fn bench_intersect_12k_triangles_bvh(b: &mut ::test::Bencher) { intersect_12k_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 120,000 triangles using the recursive `Bvh`. + /// Benchmark intersecting 120,000 triangles using the recursive [`Bvh`]. fn bench_intersect_120k_triangles_bvh(b: &mut ::test::Bencher) { intersect_120k_triangles_bh::(b); } #[bench] - /// Benchmark the traversal of a `Bvh` with the Sponza scene. + /// Benchmark the traversal of a [`Bvh`] with the Sponza scene. fn bench_intersect_sponza_bvh(b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); let bvh = TBvh3::build(&mut triangles); diff --git a/src/bvh/iter.rs b/src/bvh/iter.rs index c890758..f1a1243 100644 --- a/src/bvh/iter.rs +++ b/src/bvh/iter.rs @@ -8,7 +8,7 @@ use num::Zero; /// Iterator to traverse a [`Bvh`] without memory allocations pub struct BvhTraverseIterator<'bvh, 'shape, T: Scalar + Copy, const D: usize, Shape: Bounded> { - /// Reference to the Bvh to traverse + /// Reference to the [`Bvh`] to traverse bvh: &'bvh Bvh, /// Reference to the input ray ray: &'bvh Ray, @@ -29,7 +29,7 @@ impl<'bvh, 'shape, T, const D: usize, Shape: Bounded> where T: Scalar + Copy + SimdPartialOrd + ClosedSub + PartialOrd + ClosedMul + Zero, { - /// Creates a new `BvhTraverseIterator` + /// Creates a new [`BvhTraverseIterator`] pub fn new(bvh: &'bvh Bvh, ray: &'bvh Ray, shapes: &'shape [Shape]) -> Self { BvhTraverseIterator { bvh, @@ -68,7 +68,7 @@ where } /// Attempt to move to the left node child of the current node. - /// If it is a leaf, or the ray does not intersect the node `Aabb`, `has_node` will become false. + /// If it is a leaf, or the ray does not intersect the node [`Aabb`], `has_node` will become false. fn move_left(&mut self) { match self.bvh.nodes[self.node_index] { BvhNode::Node { @@ -90,7 +90,7 @@ where } /// Attempt to move to the right node child of the current node. - /// If it is a leaf, or the ray does not intersect the node `Aabb`, `has_node` will become false. + /// If it is a leaf, or the ray does not intersect the node [`Aabb`], `has_node` will become false. fn move_right(&mut self) { match self.bvh.nodes[self.node_index] { BvhNode::Node { @@ -158,7 +158,7 @@ mod tests { use crate::testbase::{generate_aligned_boxes, TBvh3, TPoint3, TVector3, UnitBox}; use std::collections::HashSet; - /// Creates a `Bvh` for a fixed scene structure. + /// Creates a [`Bvh`] for a fixed scene structure. pub fn build_some_bvh() -> (Vec, TBvh3) { let mut boxes = generate_aligned_boxes(); let bvh = TBvh3::build(&mut boxes); @@ -266,7 +266,7 @@ mod bench { use crate::testbase::{create_ray, load_sponza_scene, TBvh3}; #[bench] - /// Benchmark the traversal of a `Bvh` with the Sponza scene with Vec return. + /// Benchmark the traversal of a [`Bvh`] with the Sponza scene with [`Vec`] return. fn bench_intersect_128rays_sponza_vec(b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); let bvh = TBvh3::build(&mut triangles); @@ -288,7 +288,7 @@ mod bench { } #[bench] - /// Benchmark the traversal of a `Bvh` with the Sponza scene with `BvhTraverseIterator`. + /// Benchmark the traversal of a `Bvh` with the Sponza scene with [`BvhTraverseIterator`]. fn bench_intersect_128rays_sponza_iter(b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); let bvh = TBvh3::build(&mut triangles); diff --git a/src/bvh/optimization.rs b/src/bvh/optimization.rs index 935bc84..0be5604 100644 --- a/src/bvh/optimization.rs +++ b/src/bvh/optimization.rs @@ -16,10 +16,10 @@ use num::{FromPrimitive, Zero}; use rand::{thread_rng, Rng}; use std::collections::HashSet; -// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an Aabb +// TODO Consider: Instead of getting the scene's shapes passed, let leaf nodes store an `Aabb` // that is updated from the outside, perhaps by passing not only the indices of the changed -// shapes, but also their new Aabbs into optimize(). -// TODO Consider: Stop updating Aabbs upwards the tree once an Aabb didn't get changed. +// shapes, but also their new `Aabb`'s into optimize(). +// TODO Consider: Stop updating `Aabb`'s upwards the tree once an `Aabb` didn't get changed. #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] enum OptimizationIndex { @@ -79,9 +79,9 @@ where + PartialOrd + std::fmt::Display, { - /// Optimizes the `Bvh` by batch-reorganizing updated nodes. + /// Optimizes the [`Bvh`] by batch-reorganizing updated nodes. /// Based on - /// [`https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBvh/ssBvh.cs`] + /// [`https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH.cs`] /// /// Needs all the scene's shapes, plus the indices of the shapes that were updated. /// @@ -165,7 +165,7 @@ where } /// This method is called for each node which has been modified and needs to be updated. - /// If the specified node is a grandparent, then try to optimize the `Bvh` by rotating its + /// If the specified node is a grandparent, then try to optimize the [`Bvh`] by rotating its /// children. fn update>( &mut self, @@ -288,12 +288,12 @@ where } /// Checks if there is a way to rotate a child and a grandchild (or two grandchildren) of - /// the given node (specified by `node_index`) that would improve the `Bvh`. + /// the given node (specified by `node_index`) that would improve the [`Bvh`]. /// If there is, the best rotation found is performed. /// /// # Preconditions /// - /// This function requires that the subtree at `node_index` has correct `Aabb`s. + /// This function requires that the subtree at `node_index` has correct [`Aabb`]s. /// /// # Returns /// @@ -348,7 +348,7 @@ where if node_index != 0 { // Even with no rotation being useful for this node, a parent node's rotation // could be beneficial, so queue the parent *sometimes*. For reference see: - // https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBvh/ssBvh_Node.cs#L307 + // https://github.com/jeske/SimpleScene/blob/master/SimpleScene/Util/ssBVH/ssBVH_Node.cs#L307 // TODO Evaluate whether this is a smart thing to do. let mut rng = thread_rng(); if rng.gen_bool(0.01) { @@ -363,7 +363,7 @@ where } } - /// Sets child_l_aabb and child_r_aabb of a BvhNode::Node to match its children, + /// Sets `child_l_aabb` and child_r_aabb of a [`BvhNode::Node`] to match its children, /// right after updating the children themselves. Not recursive. fn fix_children_and_own_aabbs>( &mut self, @@ -421,7 +421,7 @@ where } /// Switch two nodes by rewiring the involved indices (not by moving them in the nodes slice). - /// Also updates the Aabbs of the parents. + /// Also updates the [`Aabbs`]'s of the parents. fn rotate>( &mut self, node_a_index: usize, @@ -496,7 +496,7 @@ where ) { let child_aabb = self.nodes[child_index].get_node_aabb(shapes); info!("\tConnecting: {} < {}.", child_index, parent_index); - // Set parent's child and child_aabb; and get its depth. + // Set parent's child and `child_aabb`; and get its depth. let parent_depth = { match self.nodes[parent_index] { BvhNode::Node { @@ -517,7 +517,7 @@ where info!("\t {}'s new {}", parent_index, child_aabb); depth } - // Assuming that our Bvh is correct, the parent cannot be a leaf. + // Assuming that our `Bvh` is correct, the parent cannot be a leaf. _ => unreachable!(), } }; @@ -541,7 +541,7 @@ mod tests { use std::collections::HashSet; #[test] - /// Tests if `optimize` does not modify a fresh `Bvh`. + /// Tests if [`Bvh::optimize()`] does not modify a fresh [`Bvh`]. fn test_optimizing_new_bvh() { let (shapes, mut bvh) = build_some_bh::(); let original_nodes = bvh.nodes.clone(); @@ -573,7 +573,7 @@ mod tests { } #[test] - /// Test whether a simple update on a simple Bvh yields the expected optimization result. + /// Test whether a simple update on a simple [`Bvh]` yields the expected optimization result. fn test_optimize_simple_update() { let mut shapes = vec![ UnitBox::new(0, TPoint3::new(-50.0, 0.0, 0.0)), @@ -642,7 +642,7 @@ mod tests { } } - /// Creates a small `Bvh` with 4 shapes and 7 nodes. + /// Creates a small [`Bvh`] with 4 shapes and 7 nodes. fn create_predictable_bvh() -> (Vec, TBvh3) { let shapes = vec![ UnitBox::new(0, TPoint3::new(0.0, 0.0, 0.0)), @@ -926,7 +926,7 @@ mod tests { } #[test] - /// Test optimizing `Bvh` after randomizing 50% of the shapes. + /// Test optimizing [`Bvh`] after randomizing 50% of the shapes. fn test_optimize_bvh_12k_75p() { let bounds = default_bounds(); let mut triangles = create_n_cubes(1_000, &bounds); @@ -959,7 +959,7 @@ mod bench { }; #[bench] - /// Benchmark randomizing 50% of the shapes in a `Bvh`. + /// Benchmark randomizing 50% of the shapes in a [`Bvh`]. fn bench_randomize_120k_50p(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); @@ -970,8 +970,8 @@ mod bench { }); } - /// Benchmark optimizing a `Bvh` with 120,000 `Triangle`s, where `percent` - /// `Triangles` have been randomly moved. + /// Benchmark optimizing a [`Bvh`] with 120,000 [`Triangle`]'ss, where `percent` + /// [`Triangle`]'s have been randomly moved. fn optimize_bvh_120k(percent: f32, b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); @@ -1006,9 +1006,9 @@ mod bench { optimize_bvh_120k(0.5, b); } - /// Move `percent` `Triangle`s in the scene given by `triangles` and optimize the - /// `Bvh`. Iterate this procedure `iterations` times. Afterwards benchmark the performance - /// of intersecting this scene/`Bvh`. + /// Move `percent` [`Triangle`]`s in the scene given by `triangles` and optimize the + /// [`Bvh`]. Iterate this procedure `iterations` times. Afterwards benchmark the performance + /// of intersecting this scene/[`Bvh`]. fn intersect_scene_after_optimize( triangles: &mut Vec, bounds: &TAabb3, @@ -1058,9 +1058,9 @@ mod bench { intersect_scene_after_optimize(&mut triangles, &bounds, 0.5, None, 10, b); } - /// Move `percent` `Triangle`s in the scene given by `triangles` `iterations` times. + /// Move `percent` [`Triangle`]'s in the scene given by `triangles` `iterations` times. /// Afterwards optimize the `Bvh` and benchmark the performance of intersecting this - /// scene/`Bvh`. Used to compare optimizing with rebuilding. For reference see + /// scene/[`Bvh`]. Used to compare optimizing with rebuilding. For reference see /// `intersect_scene_after_optimize`. fn intersect_scene_with_rebuild( triangles: &mut Vec, @@ -1108,7 +1108,7 @@ mod bench { intersect_scene_with_rebuild(&mut triangles, &bounds, 0.5, None, 10, b); } - /// Benchmark intersecting a `Bvh` for Sponza after randomly moving one `Triangle` and + /// Benchmark intersecting a [`Bvh`] for Sponza after randomly moving one [`Triangle`] and /// optimizing. fn intersect_sponza_after_optimize(percent: f32, b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); @@ -1135,7 +1135,7 @@ mod bench { intersect_sponza_after_optimize(0.5, b); } - /// Benchmark intersecting a `Bvh` for Sponza after rebuilding. Used to compare optimizing + /// Benchmark intersecting a [`Bvh`] for Sponza after rebuilding. Used to compare optimizing /// with rebuilding. For reference see `intersect_sponza_after_optimize`. fn intersect_sponza_with_rebuild(percent: f32, b: &mut ::test::Bencher) { let (mut triangles, bounds) = load_sponza_scene(); diff --git a/src/flat_bvh.rs b/src/flat_bvh.rs index 8d0213f..12f430a 100644 --- a/src/flat_bvh.rs +++ b/src/flat_bvh.rs @@ -1,4 +1,4 @@ -//! This module exports methods to flatten the `Bvh` and traverse it iteratively. +//! This module exports methods to flatten the [`Bvh`] into a [`FlatBvh`] and traverse it iteratively. use crate::aabb::{Aabb, Bounded}; use crate::bounding_hierarchy::{BHShape, BoundingHierarchy}; use crate::bvh::{Bvh, BvhNode}; @@ -33,7 +33,7 @@ pub struct FlatNode { /// pub entry_index: u32, - /// The index of the `FlatNode` to jump to, if the [`Aabb`] test is negative. + /// The index of the [`FlatNode`] to jump to, if the [`Aabb`] test is negative. /// /// [`Aabb`]: ../aabb/struct.Aabb.html /// @@ -44,7 +44,7 @@ pub struct FlatNode { } impl BvhNode { - /// Creates a flat node from a `Bvh` inner node and its `Aabb`. Returns the next free index. + /// Creates a flat node from a `Bvh` inner node and its [`Aabb`]. Returns the next free index. /// TODO: change the algorithm which pushes `FlatNode`s to a vector to not use indices this /// much. Implement an algorithm which writes directly to a writable slice. fn create_flat_branch( @@ -149,7 +149,7 @@ impl Bvh { /// non-default structure. /// The `constructor` is fed the following arguments in this order: /// - /// 1 - &Aabb: The enclosing `Aabb` + /// 1 - &Aabb: The enclosing [`Aabb`] /// 2 - u32: The index of the nested node /// 3 - u32: The exit index /// 4 - u32: The shape index @@ -465,7 +465,7 @@ mod bench { }; #[bench] - /// Benchmark the flattening of a Bvh with 120,000 triangles. + /// Benchmark the flattening of a [`Bvh`] with 120,000 triangles. fn bench_flatten_120k_triangles_bvh(b: &mut ::test::Bencher) { let bounds = default_bounds(); let mut triangles = create_n_cubes(10_000, &bounds); @@ -476,37 +476,37 @@ mod bench { }); } #[bench] - /// Benchmark the construction of a `FlatBvh` with 1,200 triangles. + /// Benchmark the construction of a [`FlatBvh`] with 1,200 triangles. fn bench_build_1200_triangles_flat_bvh(b: &mut ::test::Bencher) { build_1200_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `FlatBvh` with 12,000 triangles. + /// Benchmark the construction of a [`FlatBvh`] with 12,000 triangles. fn bench_build_12k_triangles_flat_bvh(b: &mut ::test::Bencher) { build_12k_triangles_bh::(b); } #[bench] - /// Benchmark the construction of a `FlatBvh` with 120,000 triangles. + /// Benchmark the construction of a [`FlatBvh`] with 120,000 triangles. fn bench_build_120k_triangles_flat_bvh(b: &mut ::test::Bencher) { build_120k_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 1,200 triangles using the recursive `FlatBvh`. + /// Benchmark intersecting 1,200 triangles using the recursive [`FlatBvh`]. fn bench_intersect_1200_triangles_flat_bvh(b: &mut ::test::Bencher) { intersect_1200_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 12,000 triangles using the recursive `FlatBvh`. + /// Benchmark intersecting 12,000 triangles using the recursive [`FlatBvh`]. fn bench_intersect_12k_triangles_flat_bvh(b: &mut ::test::Bencher) { intersect_12k_triangles_bh::(b); } #[bench] - /// Benchmark intersecting 120,000 triangles using the recursive `FlatBvh`. + /// Benchmark intersecting 120,000 triangles using the recursive [`FlatBvh`]. fn bench_intersect_120k_triangles_flat_bvh(b: &mut ::test::Bencher) { intersect_120k_triangles_bh::(b); } diff --git a/src/lib.rs b/src/lib.rs index 810b7bf..cd81510 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,12 +4,12 @@ //! ## About //! //! This crate can be used for applications which contain intersection computations of rays -//! with primitives. For this purpose a binary tree BVH (Bounding Volume Hierarchy) is of great -//! use if the scene which the ray traverses contains a huge number of primitives. With a BVH the +//! with primitives. For this purpose a binary tree [`Bvh`](bvh::Bvh) (Bounding Volume Hierarchy) is of great +//! use if the scene which the ray traverses contains a huge number of primitives. With a [`Bvh`](bvh::Bvh) the //! intersection test complexity is reduced from O(n) to O(log2(n)) at the cost of building -//! the BVH once in advance. This technique is especially useful in ray/path tracers. For +//! the [`Bvh`](bvh::Bvh) once in advance. This technique is especially useful in ray/path tracers. For //! use in a shader this module also exports a flattening procedure, which allows for -//! iterative traversal of the BVH. +//! iterative traversal of the [`Bvh`](bvh::Bvh). //! //! ## Note //! @@ -73,7 +73,7 @@ //! ## Features //! //! - `serde` (default **disabled**) - adds `Serialize` and `Deserialize` implementations for some types -//! - 'simd' (default **disabled**) - adds explicitly written SIMD instructions for certain architectures (requires nightly) +//! - `simd` (default **disabled**) - adds explicitly written SIMD instructions for certain architectures (requires nightly) //! #![deny(missing_docs)] diff --git a/src/ray/intersect_default.rs b/src/ray/intersect_default.rs index 40a101a..772dfa7 100644 --- a/src/ray/intersect_default.rs +++ b/src/ray/intersect_default.rs @@ -1,8 +1,12 @@ +//! This file contains the generic implementation of [`RayIntersection`] + use super::Ray; use crate::{aabb::Aabb, utils::fast_max}; use nalgebra::{ClosedMul, ClosedSub, Scalar, SimdPartialOrd}; use num::Zero; +/// The [`RayIntersection`] trait allows for generic implementation of ray intersection +/// useful for our SIMD optimizations. pub trait RayIntersection where T: Copy + Scalar, diff --git a/src/ray/intersect_x86_64.rs b/src/ray/intersect_x86_64.rs index cedd37f..4fb5946 100644 --- a/src/ray/intersect_x86_64.rs +++ b/src/ray/intersect_x86_64.rs @@ -1,3 +1,6 @@ +//! This file contains overrides for specific SIMD implementations of [`RayIntersection`] +//! for the x86_64 architecture. + use std::arch::x86_64::*; use nalgebra::SVector; diff --git a/src/ray/mod.rs b/src/ray/mod.rs index 44ccdd3..07ca3ed 100644 --- a/src/ray/mod.rs +++ b/src/ray/mod.rs @@ -1,5 +1,4 @@ -//! This module holds the ray definition, and overloads for x86_64 simd operations - +//! This module holds the [`Ray`] definition, and `RayIntersection` functions. mod intersect_default; mod ray_impl; diff --git a/src/ray/ray_impl.rs b/src/ray/ray_impl.rs index e9e8018..4db198c 100644 --- a/src/ray/ray_impl.rs +++ b/src/ray/ray_impl.rs @@ -25,7 +25,7 @@ pub struct Ray { pub inv_direction: SVector, } -/// A struct which is returned by the `intersects_triangle` method. +/// A struct which is returned by the [`Ray::intersects_triangle()`] method. pub struct Intersection { /// Distance from the ray origin to the intersection point. pub distance: T, @@ -38,7 +38,7 @@ pub struct Intersection { } impl Intersection { - /// Constructs an `Intersection`. `distance` should be set to positive infinity, + /// Constructs an [`Intersection`]. `distance` should be set to positive infinity, /// if the intersection does not occur. pub fn new(distance: T, u: T, v: T) -> Intersection { Intersection { distance, u, v } @@ -184,9 +184,9 @@ mod tests { use proptest::prelude::*; - /// Generates a random `Ray` which points at at a random `Aabb`. + /// Generates a random [`Ray`] which points at at a random [`Aabb`]. fn gen_ray_to_aabb(data: (TupleVec, TupleVec, TupleVec)) -> (TRay3, TAabb3) { - // Generate a random Aabb + // Generate a random `Aabb` let aabb = TAabb3::empty() .grow(&tuple_to_point(&data.0)) .grow(&tuple_to_point(&data.1)); diff --git a/src/testbase.rs b/src/testbase.rs index f9688fa..df63fa0 100644 --- a/src/testbase.rs +++ b/src/testbase.rs @@ -27,7 +27,7 @@ pub type TVector3 = nalgebra::SVector; pub type TPoint3 = nalgebra::Point; pub type TFlatBvh3 = crate::flat_bvh::FlatBvh; -/// Generate a `TupleVec` for [`proptest::strategy::Strategy`] from -10e10 to 10e10 +/// Generate a [`TupleVec`] for [`proptest::strategy::Strategy`] from -10e10 to 10e10 /// A small enough range to prevent most fp32 errors from breaking certain tests /// Tests which rely on this strategy should probably be rewritten pub fn tuplevec_small_strategy() -> impl Strategy { @@ -38,7 +38,7 @@ pub fn tuplevec_small_strategy() -> impl Strategy { ) } -/// Generate a `TupleVec` for [`proptest::strategy::Strategy`] from -10e30 to 10e30 +/// Generate a [`TupleVec`] for [`proptest::strategy::Strategy`] from -10e30 to 10e30 /// A small enough range to prevent `f32::MAX` ranges from breaking certain tests pub fn tuplevec_large_strategy() -> impl Strategy { ( @@ -48,17 +48,17 @@ pub fn tuplevec_large_strategy() -> impl Strategy { ) } -/// Convert a `TupleVec` to a [`Point3`]. +/// Convert a [`TupleVec`] to a [`TPoint3`]. pub fn tuple_to_point(tpl: &TupleVec) -> TPoint3 { TPoint3::new(tpl.0, tpl.1, tpl.2) } -/// Convert a `TupleVec` to a [`Vector3`]. +/// Convert a [`TupleVec`] to a [`TVector3`]. pub fn tuple_to_vector(tpl: &TupleVec) -> TVector3 { TVector3::new(tpl.0, tpl.1, tpl.2) } -/// Define some `Bounded` structure. +/// Define some [`Bounded`] structure. pub struct UnitBox { pub id: i32, pub pos: TPoint3, @@ -75,7 +75,7 @@ impl UnitBox { } } -/// `UnitBox`'s `Aabb`s are unit `Aabb`s centered on the box's position. +/// [`UnitBox`]'s [`Aabb`]'s are unit [`Aabb`]'s centered on the box's position. impl Bounded for UnitBox { fn aabb(&self) -> TAabb3 { let min = self.pos + TVector3::new(-0.5, -0.5, -0.5); @@ -94,7 +94,7 @@ impl BHShape for UnitBox { } } -/// Generate 21 `UnitBox`s along the X axis centered on whole numbers (-10,9,..,10). +/// Generate 21 [`UnitBox`]'s along the X axis centered on whole numbers (-10,9,..,10). /// The index is set to the rounded x-coordinate of the box center. pub fn generate_aligned_boxes() -> Vec { // Create 21 boxes along the x-axis @@ -105,7 +105,7 @@ pub fn generate_aligned_boxes() -> Vec { shapes } -/// Creates a `BoundingHierarchy` for a fixed scene structure. +/// Creates a [`BoundingHierarchy`] for a fixed scene structure. pub fn build_some_bh>() -> (Vec, BH) { let mut boxes = generate_aligned_boxes(); let bh = BH::build(&mut boxes); @@ -130,7 +130,7 @@ fn traverse_and_verify>( } } -/// Perform some fixed intersection tests on BH structures. +/// Perform some fixed intersection tests on [`BoundingHierarchy`] structures. pub fn traverse_some_bh>() { let (all_shapes, bh) = build_some_bh::(); @@ -172,7 +172,7 @@ pub fn traverse_some_bh>() { } } -/// A triangle struct. Instance of a more complex `Bounded` primitive. +/// A triangle struct. Instance of a more complex [`Bounded`] primitive. #[derive(Debug)] pub struct Triangle { pub a: TPoint3, @@ -217,7 +217,7 @@ impl FromRawVertex for Triangle { _: Vec<(f32, f32, f32)>, polygons: Vec, ) -> ObjResult<(Vec, Vec)> { - // Convert the vertices to `Point3`s. + // Convert the vertices to `Point3`'s. let points = vertices .into_iter() .map(|v| TPoint3::new(v.0, v.1, v.2)) @@ -333,7 +333,7 @@ fn splitmix64(x: &mut u64) -> u64 { z ^ (z >> 31) } -/// Generates a new `i32` triple. Mutates the seed. +/// Generates a new [`i32`] triple. Mutates the seed. pub fn next_point3_raw(seed: &mut u64) -> (i32, i32, i32) { let u = splitmix64(seed); let a = ((u >> 32) & 0xFFFFFFFF) as i64 - 0x80000000; @@ -342,7 +342,7 @@ pub fn next_point3_raw(seed: &mut u64) -> (i32, i32, i32) { (a as i32, b as i32, c as i32) } -/// Generates a new `Point3`, which will lie inside the given `aabb`. Mutates the seed. +/// Generates a new [`Point3`], which will lie inside the given [`Aabb`]. Mutates the seed. pub fn next_point3(seed: &mut u64, aabb: &TAabb3) -> TPoint3 { let (a, b, c) = next_point3_raw(seed); use std::i32; @@ -365,7 +365,7 @@ pub fn next_point3(seed: &mut u64, aabb: &TAabb3) -> TPoint3 { aabb.min + offset } -/// Returns an `Aabb` which defines the default testing space bounds. +/// Returns an [`Aabb`] which defines the default testing space bounds. pub fn default_bounds() -> TAabb3 { TAabb3::with_bounds( TPoint3::new(-100_000.0, -100_000.0, -100_000.0), @@ -373,7 +373,7 @@ pub fn default_bounds() -> TAabb3 { ) } -/// Creates `n` deterministic random cubes. Returns the `Vec` of surface `Triangle`s. +/// Creates `n` deterministic random cubes. Returns the [`Vec`] of surface [`Triangle`]'s. pub fn create_n_cubes(n: usize, bounds: &TAabb3) -> Vec { let mut vec = Vec::new(); let mut seed = 0; @@ -405,7 +405,7 @@ pub fn load_sponza_scene() -> (Vec, TAabb3) { /// This functions moves `amount` shapes in the `triangles` array to a new position inside /// `bounds`. If `max_offset_option` is not `None` then the wrapped value is used as the maximum /// offset of a shape. This is used to simulate a realistic scene. -/// Returns a `HashSet` of indices of modified triangles. +/// Returns a [`HashSet`] of indices of modified triangles. pub fn randomly_transform_scene( triangles: &mut Vec, amount: usize, @@ -453,8 +453,8 @@ pub fn randomly_transform_scene( indices.into_iter().collect() } -/// Creates a `Ray` from the random `seed`. Mutates the `seed`. -/// The Ray origin will be inside the `bounds` and point to some other point inside this +/// Creates a [`Ray`] from the random `seed`. Mutates the `seed`. +/// The [`Ray`] origin will be inside the `bounds` and point to some other point inside this /// `bounds`. #[cfg(feature = "bench")] pub fn create_ray(seed: &mut u64, bounds: &TAabb3) -> TRay3 { @@ -463,7 +463,7 @@ pub fn create_ray(seed: &mut u64, bounds: &TAabb3) -> TRay3 { TRay3::new(origin, direction.coords) } -/// Benchmark the construction of a `BoundingHierarchy` with `n` triangles. +/// Benchmark the construction of a [`BoundingHierarchy`] with `n` triangles. #[cfg(feature = "bench")] fn build_n_triangles_bh>(n: usize, b: &mut ::test::Bencher) { let bounds = default_bounds(); @@ -473,19 +473,19 @@ fn build_n_triangles_bh>(n: usize, b: &mut ::test:: }); } -/// Benchmark the construction of a `BoundingHierarchy` with 1,200 triangles. +/// Benchmark the construction of a [`BoundingHierarchy`] with 1,200 triangles. #[cfg(feature = "bench")] pub fn build_1200_triangles_bh>(b: &mut ::test::Bencher) { build_n_triangles_bh::(100, b); } -/// Benchmark the construction of a `BoundingHierarchy` with 12,000 triangles. +/// Benchmark the construction of a [`BoundingHierarchy`] with 12,000 triangles. #[cfg(feature = "bench")] pub fn build_12k_triangles_bh>(b: &mut ::test::Bencher) { build_n_triangles_bh::(1_000, b); } -/// Benchmark the construction of a `BoundingHierarchy` with 120,000 triangles. +/// Benchmark the construction of a [`BoundingHierarchy`] with 120,000 triangles. #[cfg(feature = "bench")] pub fn build_120k_triangles_bh>(b: &mut ::test::Bencher) { build_n_triangles_bh::(10_000, b); @@ -522,7 +522,7 @@ fn bench_intersect_sponza_list(b: &mut ::test::Bencher) { intersect_list(&triangles, &bounds, b); } -/// Benchmark intersecting the `triangles` list with `Aabb` checks, but without acceleration +/// Benchmark intersecting the `triangles` list with [`Aabb`] checks, but without acceleration /// structures. #[cfg(feature = "bench")] pub fn intersect_list_aabb(triangles: &[Triangle], bounds: &TAabb3, b: &mut ::test::Bencher) { @@ -532,7 +532,7 @@ pub fn intersect_list_aabb(triangles: &[Triangle], bounds: &TAabb3, b: &mut ::te // Iterate over the list of triangles. for triangle in triangles { - // First test whether the ray intersects the Aabb of the triangle. + // First test whether the ray intersects the `Aabb` of the triangle. if ray.intersects_aabb(&triangle.aabb()) { ray.intersects_triangle(&triangle.a, &triangle.b, &triangle.c); } @@ -542,7 +542,7 @@ pub fn intersect_list_aabb(triangles: &[Triangle], bounds: &TAabb3, b: &mut ::te #[cfg(feature = "bench")] #[bench] -/// Benchmark intersecting 120,000 triangles with preceeding `Aabb` tests. +/// Benchmark intersecting 120,000 triangles with preceeding [`Aabb`] tests. fn bench_intersect_120k_triangles_list_aabb(b: &mut ::test::Bencher) { let bounds = default_bounds(); let triangles = create_n_cubes(10_000, &bounds); @@ -551,7 +551,7 @@ fn bench_intersect_120k_triangles_list_aabb(b: &mut ::test::Bencher) { #[cfg(feature = "bench")] #[bench] -/// Benchmark intersecting 120,000 triangles with preceeding `Aabb` tests. +/// Benchmark intersecting 120,000 triangles with preceeding [`Aabb`] tests. fn bench_intersect_sponza_list_aabb(b: &mut ::test::Bencher) { let (triangles, bounds) = load_sponza_scene(); intersect_list_aabb(&triangles, &bounds, b); @@ -568,7 +568,7 @@ pub fn intersect_bh>( b.iter(|| { let ray = create_ray(&mut seed, bounds); - // Traverse the `BoundingHierarchy` recursively. + // Traverse the [`BoundingHierarchy`] recursively. let hits = bh.traverse(&ray, triangles); // Traverse the resulting list of positive `Aabb` tests @@ -578,7 +578,7 @@ pub fn intersect_bh>( }); } -/// Benchmark the traversal of a `BoundingHierarchy` with `n` triangles. +/// Benchmark the traversal of a [`BoundingHierarchy`] with `n` triangles. #[cfg(feature = "bench")] pub fn intersect_n_triangles>(n: usize, b: &mut ::test::Bencher) { let bounds = default_bounds(); @@ -587,19 +587,19 @@ pub fn intersect_n_triangles>(n: usize, b: &mut ::t intersect_bh(&bh, &triangles, &bounds, b) } -/// Benchmark the traversal of a `BoundingHierarchy` with 1,200 triangles. +/// Benchmark the traversal of a [`BoundingHierarchy`] with 1,200 triangles. #[cfg(feature = "bench")] pub fn intersect_1200_triangles_bh>(b: &mut ::test::Bencher) { intersect_n_triangles::(100, b); } -/// Benchmark the traversal of a `BoundingHierarchy` with 12,000 triangles. +/// Benchmark the traversal of a [`BoundingHierarchy`] with 12,000 triangles. #[cfg(feature = "bench")] pub fn intersect_12k_triangles_bh>(b: &mut ::test::Bencher) { intersect_n_triangles::(1_000, b); } -/// Benchmark the traversal of a `BoundingHierarchy` with 120,000 triangles. +/// Benchmark the traversal of a [`BoundingHierarchy`] with 120,000 triangles. #[cfg(feature = "bench")] pub fn intersect_120k_triangles_bh>(b: &mut ::test::Bencher) { intersect_n_triangles::(10_000, b); diff --git a/src/utils.rs b/src/utils.rs index 09a78e1..a8760e2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -22,7 +22,7 @@ use num::Float; /// min( 1.0, NaN): NaN /// ``` /// -/// Note: This exists because std::cmp::min requires Ord which floating point types do not satisfy +/// Note: This exists because [`std::cmp::min`] requires Ord which floating point types do not satisfy #[inline(always)] #[allow(dead_code)] pub fn fast_min(x: T, y: T) -> T { @@ -49,7 +49,7 @@ pub fn fast_min(x: T, y: T) -> T { /// max( 1.0, NaN): NaN /// ``` /// -/// Note: This exists because std::cmp::max requires Ord which floating point types do not satisfy +/// Note: This exists because [`std::cmp::max`] requires Ord which floating point types do not satisfy #[inline(always)] #[allow(dead_code)] pub fn fast_max(x: T, y: T) -> T { @@ -71,13 +71,13 @@ pub fn concatenate_vectors(vectors: &mut [Vec]) -> Vec { } /// Defines a Bucket utility object. Used to store the properties of shape-partitions -/// in the BVH build procedure using SAH. +/// in the [`Bvh`] build procedure using SAH. #[derive(Clone, Copy)] pub struct Bucket { /// The number of shapes in this `Bucket`. pub size: usize, - /// The joint `Aabb` of the shapes in this `Bucket`. + /// The joint [`Aabb`] of the shapes in this [`Bucket`]. pub aabb: Aabb, } @@ -90,13 +90,13 @@ impl Bucket { } } - /// Extend this `Bucket` by a shape with the given `Aabb`. + /// Extend this [`Bucket`] by a shape with the given [`Aabb`]. pub fn add_aabb(&mut self, aabb: &Aabb) { self.size += 1; self.aabb = self.aabb.join(aabb); } - /// Join the contents of two `Bucket`s. + /// Join the contents of two [`Bucket`]'s. pub fn join_bucket(a: Bucket, b: &Bucket) -> Bucket { Bucket { size: a.size + b.size, @@ -126,7 +126,7 @@ mod tests { use crate::utils::concatenate_vectors; #[test] - /// Test if concatenating no `Vec`s yields an empty `Vec`. + /// Test if concatenating no [`Vec`]s yields an empty [`Vec`]. fn test_concatenate_empty() { let mut vectors: Vec> = vec![]; let expected = vec![]; @@ -136,7 +136,7 @@ mod tests { } #[test] - /// Test if concatenating some `Vec`s yields the concatenation of the vectors. + /// Test if concatenating some [`Vec`]s yields the concatenation of the vectors. fn test_concatenate_vectors() { let mut vectors = vec![vec![1, 2, 3], vec![], vec![4, 5, 6], vec![7, 8], vec![9]]; let result = concatenate_vectors(vectors.as_mut_slice()); From 102a7abdfe96fd1117001eea6fe03f792d991a9d Mon Sep 17 00:00:00 2001 From: Marios Staikopoulos Date: Wed, 3 May 2023 22:53:49 -0700 Subject: [PATCH 20/20] Add comments to ray intersect x86_64 --- src/ray/intersect_x86_64.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ray/intersect_x86_64.rs b/src/ray/intersect_x86_64.rs index 4fb5946..d4cbc40 100644 --- a/src/ray/intersect_x86_64.rs +++ b/src/ray/intersect_x86_64.rs @@ -42,6 +42,7 @@ impl ToRegisterType for SVector { } } +/// Compute the horizontal maximum of the SIMD vector #[inline(always)] fn max_elem_m128(mm: __m128) -> f32 { unsafe { @@ -56,6 +57,7 @@ fn max_elem_m128(mm: __m128) -> f32 { } } +/// Compute the horizontal minimum of the SIMD vector #[inline(always)] fn min_elem_m128(mm: __m128) -> f32 { unsafe { @@ -136,6 +138,7 @@ impl ToRegisterType for SVector { } } +/// Compute the horizontal maximum of the SIMD vector #[inline(always)] fn max_elem_m128d(mm: __m128d) -> f64 { unsafe { @@ -148,6 +151,7 @@ fn max_elem_m128d(mm: __m128d) -> f64 { } } +/// Compute the horizontal minimum of the SIMD vector #[inline(always)] fn min_elem_m128d(mm: __m128d) -> f64 { unsafe { @@ -212,6 +216,7 @@ impl ToRegisterType for SVector { } } +/// Compute the horizontal maximum of the SIMD vector #[inline(always)] fn max_elem_m256d(mm: __m256d) -> f64 { unsafe { @@ -226,6 +231,7 @@ fn max_elem_m256d(mm: __m256d) -> f64 { } } +/// Compute the horizontal minimum of the SIMD vector #[inline(always)] fn min_elem_m256d(mm: __m256d) -> f64 { unsafe {