From 48bb3550cb18701887adb3b9bd5d9e0b93e3c3e9 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Tue, 24 Mar 2020 20:31:48 +0100 Subject: [PATCH] library: use beevee instead of rust-bvh I also used this opportunity to use `nalgebra::Unit` as much as possible. --- pathtracer/Cargo.toml | 2 +- pathtracer/src/lib.rs | 9 ++--- pathtracer/src/light/directional_light.rs | 27 ++++++++------- pathtracer/src/light/mod.rs | 3 +- pathtracer/src/light/point_light.rs | 7 ++-- pathtracer/src/light/spot_light.rs | 29 ++++++++-------- pathtracer/src/render/light_aggregate.rs | 4 +-- pathtracer/src/render/object.rs | 14 +++----- pathtracer/src/render/scene.rs | 41 ++++++++++------------- pathtracer/src/render/utils.rs | 18 +++++----- pathtracer/src/serialize/vector.rs | 5 +-- pathtracer/src/shape/mod.rs | 11 ++++-- pathtracer/src/shape/sphere.rs | 32 +++++++++++++----- pathtracer/src/shape/triangle.rs | 25 +++++++++----- 14 files changed, 124 insertions(+), 103 deletions(-) diff --git a/pathtracer/Cargo.toml b/pathtracer/Cargo.toml index 770e871..dd98d70 100644 --- a/pathtracer/Cargo.toml +++ b/pathtracer/Cargo.toml @@ -19,7 +19,7 @@ name = "pathtracer" path = "src/main.rs" [dependencies] -bvh = "0.3.2" +beevee = { path = "../beevee" } derive_more = "0.99.3" enum_dispatch = "0.2.1" image = "0.23.0" diff --git a/pathtracer/src/lib.rs b/pathtracer/src/lib.rs index d3e939a..e601b3d 100644 --- a/pathtracer/src/lib.rs +++ b/pathtracer/src/lib.rs @@ -2,14 +2,11 @@ //! A pathtracing crate -use bvh::nalgebra::{Point2, Point3, Vector3}; +/// 3D points and vectors +pub use beevee::{Point, Vector}; /// A 2D point coordinate -pub type Point2D = Point2; -/// A 3D point coordinate -pub type Point = Point3; -/// A 3D vector -pub type Vector = Vector3; +pub type Point2D = nalgebra::Point2; pub mod core; pub mod light; diff --git a/pathtracer/src/light/directional_light.rs b/pathtracer/src/light/directional_light.rs index a3a44a0..b40d153 100644 --- a/pathtracer/src/light/directional_light.rs +++ b/pathtracer/src/light/directional_light.rs @@ -1,13 +1,14 @@ use super::{Light, SpatialLight}; use crate::core::LinearColor; use crate::{Point, Vector}; +use nalgebra::Unit; use serde::Deserialize; /// Represent a light emanating from a far away source, with parallel rays on all points. #[derive(Debug, PartialEq, Deserialize)] pub struct DirectionalLight { #[serde(deserialize_with = "crate::serialize::vector_normalizer")] - direction: Vector, + direction: Unit, color: LinearColor, } @@ -22,15 +23,12 @@ impl DirectionalLight { /// # use pathtracer::Vector; /// # /// let dir_light = DirectionalLight::new( - /// Vector::new(1.0, 0.0, 0.0), + /// Vector::x_axis(), /// LinearColor::new(1.0, 0.0, 1.0), /// ); /// ``` - pub fn new(direction: Vector, color: LinearColor) -> Self { - DirectionalLight { - direction: direction.normalize(), - color, - } + pub fn new(direction: Unit, color: LinearColor) -> Self { + DirectionalLight { direction, color } } } @@ -41,8 +39,8 @@ impl Light for DirectionalLight { } impl SpatialLight for DirectionalLight { - fn to_source(&self, _: &Point) -> (Vector, f32) { - (self.direction * -1., std::f32::INFINITY) + fn to_source(&self, _: &Point) -> (Unit, f32) { + (-self.direction, std::f32::INFINITY) } } @@ -52,7 +50,7 @@ mod test { #[test] fn new_works() { - let direction = Vector::new(1., 0., 0.); + let direction = Vector::x_axis(); let color = LinearColor::new(1., 1., 1.); let light = DirectionalLight::new(direction, color.clone()); let res = DirectionalLight { direction, color }; @@ -60,7 +58,7 @@ mod test { } fn simple_light() -> impl SpatialLight { - let direction = Vector::new(1., 0., 0.); + let direction = Vector::x_axis(); let color = LinearColor::new(1., 1., 1.); DirectionalLight::new(direction, color) } @@ -76,7 +74,10 @@ mod test { fn to_source_is_correct() { let light = simple_light(); let ans = light.to_source(&Point::new(1., 0., 0.)); - let expected = (Vector::new(-1., 0., 0.), std::f32::INFINITY); + let expected = ( + Unit::new_normalize(Vector::new(-1., 0., 0.)), + std::f32::INFINITY, + ); assert_eq!(ans, expected) } @@ -86,7 +87,7 @@ mod test { let light: DirectionalLight = serde_yaml::from_str(yaml).unwrap(); assert_eq!( light, - DirectionalLight::new(Vector::new(1., 0., 0.), LinearColor::new(1., 0.5, 0.2)) + DirectionalLight::new(Vector::x_axis(), LinearColor::new(1., 0.5, 0.2)) ) } } diff --git a/pathtracer/src/light/mod.rs b/pathtracer/src/light/mod.rs index 10f6e55..ca3bf77 100644 --- a/pathtracer/src/light/mod.rs +++ b/pathtracer/src/light/mod.rs @@ -2,6 +2,7 @@ use super::core::LinearColor; use super::{Point, Vector}; +use nalgebra::Unit; /// Represent a light in the scene being rendered. pub trait Light: std::fmt::Debug { @@ -12,7 +13,7 @@ pub trait Light: std::fmt::Debug { /// Represent a light which has an abstract position in the scene being rendered. pub trait SpatialLight: Light { /// Get a unit vector from the origin to the position of the light, and its distance - fn to_source(&self, origin: &Point) -> (Vector, f32); + fn to_source(&self, origin: &Point) -> (Unit, f32); } mod ambient_light; diff --git a/pathtracer/src/light/point_light.rs b/pathtracer/src/light/point_light.rs index 3fb00d5..2e69ec8 100644 --- a/pathtracer/src/light/point_light.rs +++ b/pathtracer/src/light/point_light.rs @@ -1,6 +1,7 @@ use super::{Light, SpatialLight}; use crate::core::LinearColor; use crate::{Point, Vector}; +use nalgebra::Unit; use serde::Deserialize; /// Represent a light emanating from a point in space, following the square distance law. @@ -38,10 +39,10 @@ impl Light for PointLight { } impl SpatialLight for PointLight { - fn to_source(&self, point: &Point) -> (Vector, f32) { + fn to_source(&self, point: &Point) -> (Unit, f32) { let delt = self.position - point; let dist = delt.norm(); - (delt.normalize(), dist) + (Unit::new_normalize(delt), dist) } } @@ -75,7 +76,7 @@ mod test { fn to_source_is_correct() { let light = simple_light(); let ans = light.to_source(&Point::new(1., 0., 0.)); - let expected = (Vector::new(-1., 0., 0.), 1.); + let expected = (Unit::new_normalize(Vector::new(-1., 0., 0.)), 1.); assert_eq!(ans, expected); } diff --git a/pathtracer/src/light/spot_light.rs b/pathtracer/src/light/spot_light.rs index 9fa80ff..5fed907 100644 --- a/pathtracer/src/light/spot_light.rs +++ b/pathtracer/src/light/spot_light.rs @@ -1,6 +1,7 @@ use super::{Light, SpatialLight}; use crate::core::LinearColor; use crate::{Point, Vector}; +use nalgebra::Unit; use serde::{Deserialize, Deserializer}; /// Represent a light emanating from a directed light-source, outputting rays in a cone. @@ -9,7 +10,7 @@ use serde::{Deserialize, Deserializer}; #[derive(Debug, PartialEq)] pub struct SpotLight { position: Point, - direction: Vector, + direction: Unit, cosine_value: f32, color: LinearColor, } @@ -18,13 +19,13 @@ impl SpotLight { /// Construct a SpotLight with the given FOV in radian. pub fn radians_new( position: Point, - direction: Vector, + direction: Unit, fov_rad: f32, color: LinearColor, ) -> Self { SpotLight { position, - direction: direction.normalize(), + direction, cosine_value: (fov_rad / 2.).cos(), color, } @@ -33,7 +34,7 @@ impl SpotLight { /// Construct a SpotLight with the given FOV in degrees. pub fn degrees_new( position: Point, - direction: Vector, + direction: Unit, fov_deg: f32, color: LinearColor, ) -> Self { @@ -59,10 +60,10 @@ impl Light for SpotLight { } impl SpatialLight for SpotLight { - fn to_source(&self, point: &Point) -> (Vector, f32) { + fn to_source(&self, point: &Point) -> (Unit, f32) { let delt = self.position - point; let dist = delt.norm(); - (delt.normalize(), dist) + (Unit::new_normalize(delt), dist) } } @@ -70,7 +71,7 @@ impl SpatialLight for SpotLight { struct SerializedSpotLight { position: Point, #[serde(deserialize_with = "crate::serialize::vector_normalizer")] - direction: Vector, + direction: Unit, fov: f32, color: LinearColor, } @@ -99,7 +100,7 @@ mod test { fn radian_new_works() { let light = SpotLight::radians_new( Point::origin(), - Vector::new(1., 0., 0.), + Vector::x_axis(), std::f32::consts::PI / 2., LinearColor::new(1., 1., 1.), ); @@ -109,7 +110,7 @@ mod test { light, SpotLight { position: Point::origin(), - direction: Vector::new(1., 0., 0.), + direction: Vector::x_axis(), cosine_value: calculated_cosine_value, color: LinearColor::new(1., 1., 1.), } @@ -122,7 +123,7 @@ mod test { fn degrees_new_works() { let light = SpotLight::degrees_new( Point::origin(), - Vector::new(1., 0., 0.), + Vector::x_axis(), 60., LinearColor::new(1., 1., 1.), ); @@ -131,7 +132,7 @@ mod test { light, SpotLight { position: Point::origin(), - direction: Vector::new(1., 0., 0.), + direction: Vector::x_axis(), cosine_value: calculated_cosine_value, color: LinearColor::new(1., 1., 1.), } @@ -143,7 +144,7 @@ mod test { fn simple_light() -> impl SpatialLight { SpotLight::degrees_new( Point::origin(), - Vector::new(1., 0., 0.), + Vector::x_axis(), 90., LinearColor::new(1., 1., 1.), ) @@ -181,7 +182,7 @@ mod test { fn to_source_is_correct() { let light = simple_light(); let ans = light.to_source(&Point::new(1., 0., 0.)); - let expected = (Vector::new(-1., 0., 0.), 1.); + let expected = (Unit::new_normalize(Vector::new(-1., 0., 0.)), 1.); assert_eq!(ans, expected); } @@ -198,7 +199,7 @@ mod test { light, SpotLight::degrees_new( Point::origin(), - Vector::new(1., 0., 0.), + Vector::x_axis(), 90., LinearColor::new(1., 0.5, 0.2) ) diff --git a/pathtracer/src/render/light_aggregate.rs b/pathtracer/src/render/light_aggregate.rs index 26af7cc..48070c2 100644 --- a/pathtracer/src/render/light_aggregate.rs +++ b/pathtracer/src/render/light_aggregate.rs @@ -141,7 +141,7 @@ mod test { let expected = LightAggregate::new( vec![AmbientLight::new(LinearColor::new(1., 0.5, 0.2))], vec![DirectionalLight::new( - Vector::new(1., 0., 0.), + Vector::x_axis(), LinearColor::new(1., 0.5, 0.2), )], vec![PointLight::new( @@ -150,7 +150,7 @@ mod test { )], vec![SpotLight::degrees_new( Point::origin(), - Vector::new(1., 0., 0.), + Vector::x_axis(), 90., LinearColor::new(1., 0.5, 0.2), )], diff --git a/pathtracer/src/render/object.rs b/pathtracer/src/render/object.rs index 7f09fdc..1e9154e 100644 --- a/pathtracer/src/render/object.rs +++ b/pathtracer/src/render/object.rs @@ -3,8 +3,8 @@ use crate::material::MaterialEnum; use crate::shape::{Shape, ShapeEnum}; use crate::texture::TextureEnum; -use bvh::aabb::{Bounded, AABB}; -use bvh::bounding_hierarchy::BHShape; +use crate::Point; +use beevee::aabb::{Bounded, AABB}; use serde::Deserialize; /// An object being rendered in the scene. @@ -60,14 +60,9 @@ impl Bounded for Object { fn aabb(&self) -> AABB { self.shape.aabb() } -} -impl BHShape for Object { - fn set_bh_node_index(&mut self, index: usize) { - self.index = index - } - fn bh_node_index(&self) -> usize { - self.index + fn centroid(&self) -> Point { + self.shape.centroid() } } @@ -79,7 +74,6 @@ mod test { use crate::material::UniformMaterial; use crate::shape::Sphere; use crate::texture::UniformTexture; - use crate::Point; fn simple_object() -> Object { let shape = Sphere::new(Point::new(5., 0., 0.), 1.); diff --git a/pathtracer/src/render/scene.rs b/pathtracer/src/render/scene.rs index f50a6eb..b694b16 100644 --- a/pathtracer/src/render/scene.rs +++ b/pathtracer/src/render/scene.rs @@ -1,7 +1,5 @@ //! Scene rendering logic -use std::cmp::Ordering; - use super::{light_aggregate::LightAggregate, object::Object, utils::*}; use crate::{ core::{Camera, LightProperties, LinearColor, ReflTransEnum}, @@ -10,8 +8,9 @@ use crate::{ texture::Texture, {Point, Vector}, }; -use bvh::{bvh::BVH, ray::Ray}; +use beevee::{bvh::BVH, ray::Ray}; use image::RgbImage; +use nalgebra::Unit; use rand::prelude::thread_rng; use rand::Rng; use serde::{Deserialize, Deserializer}; @@ -120,12 +119,12 @@ impl Scene { fn pixel(&self, x: f32, y: f32) -> LinearColor { let (x, y) = self.camera.film().pixel_ratio(x, y); let pixel = self.camera.film().pixel_at_ratio(x, y); - let direction = (pixel - self.camera.origin()).normalize(); + let direction = Unit::new_normalize(pixel - self.camera.origin()); let indices = RefractionInfo::with_index(self.diffraction_index); self.cast_ray(Ray::new(pixel, direction)) .map_or_else(LinearColor::black, |(t, obj)| { self.color_at( - pixel + direction * t, + pixel + direction.as_ref() * t, obj, direction, self.reflection_limit, @@ -151,19 +150,15 @@ impl Scene { fn cast_ray(&self, ray: Ray) -> Option<(f32, &Object)> { self.bvh - .traverse(&ray, &self.objects) - .iter() - .filter_map(|obj| obj.shape.intersect(&ray).map(|distance| (distance, *obj))) - .min_by(|(dist_a, _), (dist_b, _)| { - dist_a.partial_cmp(dist_b).unwrap_or(Ordering::Equal) - }) + .walk(&ray, &self.objects) + .and_then(|o| o.shape.intersect(&ray).map(|t| (t, o))) } fn color_at( &self, point: Point, object: &Object, - incident_ray: Vector, + incident_ray: Unit, reflection_limit: u32, mut indices: RefractionInfo, ) -> LinearColor { @@ -203,14 +198,14 @@ impl Scene { &self, point: Point, transparency: f32, - refracted: Vector, + refracted: Unit, reflection_limit: u32, indices: RefractionInfo, ) -> LinearColor { if transparency > 1e-5 && reflection_limit > 0 { - let refraction_start = point + refracted * 0.001; + let refraction_start = point + refracted.as_ref() * 0.001; if let Some((t, obj)) = self.cast_ray(Ray::new(refraction_start, refracted)) { - let resulting_position = refraction_start + refracted * t; + let resulting_position = refraction_start + refracted.as_ref() * t; let refracted = self.color_at( resulting_position, obj, @@ -227,14 +222,14 @@ impl Scene { fn reflection( &self, point: Point, - reflected: Vector, + reflected: Unit, reflection_limit: u32, indices: RefractionInfo, ) -> LinearColor { if reflection_limit > 0 { - let reflection_start = point + reflected * 0.001; + let reflection_start = point + reflected.as_ref() * 0.001; if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) { - let resulting_position = reflection_start + reflected * t; + let resulting_position = reflection_start + reflected.as_ref() * t; let color = self.color_at( resulting_position, obj, @@ -253,8 +248,8 @@ impl Scene { point: Point, object_color: LinearColor, properties: &LightProperties, - normal: Vector, - reflected: Vector, + normal: Unit, + reflected: Unit, ) -> LinearColor { let ambient = self.illuminate_ambient(object_color.clone()); let spatial = self.illuminate_spatial(point, properties, normal, reflected); @@ -273,14 +268,14 @@ impl Scene { &self, point: Point, properties: &LightProperties, - normal: Vector, - reflected: Vector, + normal: Unit, + reflected: Unit, ) -> LinearColor { self.lights .spatial_lights_iter() .map(|light| { let (direction, t) = light.to_source(&point); - let light_ray = Ray::new(point + 0.001 * direction, direction); + let light_ray = Ray::new(point + 0.001 * direction.as_ref(), direction); match self.cast_ray(light_ray) { // Take shadows into account Some((obstacle_t, _)) if obstacle_t < t => return LinearColor::black(), diff --git a/pathtracer/src/render/utils.rs b/pathtracer/src/render/utils.rs index 185765a..3f12bfa 100644 --- a/pathtracer/src/render/utils.rs +++ b/pathtracer/src/render/utils.rs @@ -1,19 +1,19 @@ use crate::Vector; +use nalgebra::Unit; -pub fn reflected(incident: Vector, normal: Vector) -> Vector { +pub fn reflected(incident: Unit, normal: Unit) -> Unit { let proj = incident.dot(&normal); - let delt = normal * (proj * 2.); - (incident - delt).normalize() + let delt = normal.into_inner() * (proj * 2.); + Unit::new_normalize(incident.as_ref() - delt) } /// Returns None if the ray was totally reflected, Some(refracted_ray, reflected_amount) if not -/// Adds an element to the top of indices that should be removed pub fn refracted( - incident: Vector, - normal: Vector, + incident: Unit, + normal: Unit, indices: &mut RefractionInfo, new_index: f32, -) -> Option<(Vector, f32)> { +) -> Option<(Unit, f32)> { let cos1 = incident.dot(&normal); let normal = if cos1 < 0. { // Entering object, change the medium @@ -32,12 +32,12 @@ pub fn refracted( } let cos1 = cos1.abs(); let cos2 = k.sqrt(); - let refracted = eta * incident + (eta * cos1 - cos2) * normal; + let refracted = eta * incident.as_ref() + (eta * cos1 - cos2) * normal.as_ref(); let f_r = (n_2 * cos1 - n_1 * cos2) / (n_2 * cos1 + n_1 * cos2); let f_t = (n_1 * cos2 - n_2 * cos1) / (n_1 * cos2 + n_2 * cos1); let refl_t = (f_r * f_r + f_t * f_t) / 2.; //Some((refracted, 0.)) - Some((refracted.normalize(), refl_t)) + Some((Unit::new_normalize(refracted), refl_t)) } #[derive(Debug, PartialEq, Clone)] diff --git a/pathtracer/src/serialize/vector.rs b/pathtracer/src/serialize/vector.rs index 00d40ab..7ffc605 100644 --- a/pathtracer/src/serialize/vector.rs +++ b/pathtracer/src/serialize/vector.rs @@ -1,15 +1,16 @@ //! Helper functions to deserialize `Vector` values. use crate::Vector; +use nalgebra::Unit; use serde::de::{Deserialize, Deserializer}; /// Deserialize a vector. /// /// Needs a custom implementation to make sur the vector is normalized when deserialized. -pub fn vector_normalizer<'de, D>(deserializer: D) -> Result +pub fn vector_normalizer<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { let v: Vector = Deserialize::deserialize(deserializer)?; - Ok(v.normalize()) + Ok(Unit::new_normalize(v)) } diff --git a/pathtracer/src/shape/mod.rs b/pathtracer/src/shape/mod.rs index c820dbd..387da0c 100644 --- a/pathtracer/src/shape/mod.rs +++ b/pathtracer/src/shape/mod.rs @@ -1,10 +1,11 @@ //! Various shape implementations use super::{Point, Point2D, Vector}; -use bvh::{ +use beevee::{ aabb::{Bounded, AABB}, ray::Ray, }; +use nalgebra::Unit; use serde::Deserialize; /// All the existing `Shape` implementation. @@ -24,17 +25,23 @@ pub trait Shape: std::fmt::Debug { /// Return the distance at which the object intersects with the ray, or None if it does not. fn intersect(&self, ray: &Ray) -> Option; /// Return the unit vector corresponding to the normal at this point of the shape. - fn normal(&self, point: &Point) -> Vector; + fn normal(&self, point: &Point) -> Unit; /// Project the point from the shape's surface to its texel coordinates. fn project_texel(&self, point: &Point) -> Point2D; /// Enclose the `Shape` in an axi-aligned bounding-box. fn aabb(&self) -> AABB; + /// Return the centroid of the shape. + fn centroid(&self) -> Point; } impl Bounded for dyn Shape { fn aabb(&self) -> AABB { self.aabb() } + + fn centroid(&self) -> Point { + self.centroid() + } } mod sphere; diff --git a/pathtracer/src/shape/sphere.rs b/pathtracer/src/shape/sphere.rs index 56ce835..8022cae 100644 --- a/pathtracer/src/shape/sphere.rs +++ b/pathtracer/src/shape/sphere.rs @@ -1,7 +1,8 @@ use super::Shape; use crate::{Point, Point2D, Vector}; -use bvh::aabb::AABB; -use bvh::ray::Ray; +use beevee::aabb::AABB; +use beevee::ray::Ray; +use nalgebra::Unit; use serde::Deserialize; /// Represent a sphere shape inside the scene. @@ -67,13 +68,13 @@ impl Shape for Sphere { } } - fn normal(&self, point: &Point) -> Vector { + fn normal(&self, point: &Point) -> Unit { let delt = if self.inverted { self.center - point } else { point - self.center }; - delt.normalize() + Unit::new_normalize(delt) } fn project_texel(&self, point: &Point) -> Point2D { @@ -90,6 +91,10 @@ impl Shape for Sphere { let max = self.center + delt; AABB::with_bounds(min, max) } + + fn centroid(&self) -> Point { + self.center + } } #[cfg(test)] @@ -103,21 +108,30 @@ mod test { #[test] fn intersect_along_axis_works() { let sphere = simple_sphere(); - let ray = Ray::new(Point::new(-2., 0., 0.), Vector::new(1., 0., 0.)); + let ray = Ray::new( + Point::new(-2., 0., 0.), + Unit::new_normalize(Vector::new(1., 0., 0.)), + ); assert_eq!(sphere.intersect(&ray), Some(1.)) } #[test] fn non_intersect_along_axis_works() { let sphere = simple_sphere(); - let ray = Ray::new(Point::new(-2., 0., 0.), Vector::new(-1., 0., 0.)); + let ray = Ray::new( + Point::new(-2., 0., 0.), + Unit::new_normalize(Vector::new(-1., 0., 0.)), + ); assert_eq!(sphere.intersect(&ray), None) } #[test] fn intersect_not_on_axis() { let sphere = simple_sphere(); - let ray = Ray::new(Point::new(1., 1., 1.), Vector::new(-1., -1., -1.)); + let ray = Ray::new( + Point::new(1., 1., 1.), + Unit::new_normalize(Vector::new(-1., -1., -1.)), + ); assert_eq!(sphere.intersect(&ray), Some(f32::sqrt(3.) - 1.)) } @@ -126,7 +140,7 @@ mod test { let sphere = simple_sphere(); assert_eq!( sphere.normal(&Point::new(-1., 0., 0.)), - Vector::new(-1., 0., 0.) + Unit::new_normalize(Vector::new(-1., 0., 0.)) ) } @@ -135,7 +149,7 @@ mod test { let sphere = Sphere::inverted_new(Point::origin(), 1.); assert_eq!( sphere.normal(&Point::new(-1., 0., 0.)), - Vector::new(1., 0., 0.) + Unit::new_normalize(Vector::new(1., 0., 0.)) ) } diff --git a/pathtracer/src/shape/triangle.rs b/pathtracer/src/shape/triangle.rs index a78af0e..1fe5d25 100644 --- a/pathtracer/src/shape/triangle.rs +++ b/pathtracer/src/shape/triangle.rs @@ -1,7 +1,8 @@ use super::Shape; use crate::{Point, Point2D, Vector}; -use bvh::aabb::AABB; -use bvh::ray::Ray; +use beevee::aabb::AABB; +use beevee::ray::Ray; +use nalgebra::Unit; use serde::{Deserialize, Deserializer}; /// Represent a triangle inside the scene. @@ -88,8 +89,8 @@ impl Shape for Triangle { } } - fn normal(&self, _: &Point) -> Vector { - self.c0c1.cross(&self.c0c2).normalize() + fn normal(&self, _: &Point) -> Unit { + Unit::new_normalize(self.c0c1.cross(&self.c0c2)) } fn project_texel(&self, point: &Point) -> Point2D { @@ -102,6 +103,10 @@ impl Shape for Triangle { .grow(&(self.c0 + self.c0c1)) .grow(&(self.c0 + self.c0c2)) } + + fn centroid(&self) -> Point { + self.c0 + (self.c0c1 + self.c0c2) / 2. + } } #[derive(Debug, Deserialize)] @@ -132,6 +137,7 @@ impl<'de> Deserialize<'de> for Triangle { #[cfg(test)] mod test { use super::*; + use nalgebra::Unit; fn simple_triangle() -> Triangle { Triangle::new( @@ -146,7 +152,7 @@ mod test { let triangle = simple_triangle(); let ans = triangle.intersect(&Ray::new( Point::new(-1., 0.5, 0.5), - Vector::new(1., 0., 0.), + Unit::new_normalize(Vector::new(1., 0., 0.)), )); assert_eq!(ans, Some(1.0)) } @@ -156,7 +162,7 @@ mod test { let triangle = simple_triangle(); let ans = triangle.intersect(&Ray::new( Point::new(-1., 0.5, 0.), - Vector::new(1., 0., 0.5), + Unit::new_normalize(Vector::new(1., 0., 0.5)), )); assert!(ans.is_some()); assert!((ans.unwrap() - f32::sqrt(1.0 + 0.25)).abs() < 1e-5) @@ -165,7 +171,10 @@ mod test { #[test] fn intersect_out_of_bounds_is_none() { let triangle = simple_triangle(); - let ans = triangle.intersect(&Ray::new(Point::new(-1., 0.5, 0.), Vector::new(1., 1., 1.))); + let ans = triangle.intersect(&Ray::new( + Point::new(-1., 0.5, 0.), + Unit::new_normalize(Vector::new(1., 1., 1.)), + )); assert_eq!(ans, None) } @@ -173,7 +182,7 @@ mod test { fn normal_works() { let triangle = simple_triangle(); let normal = triangle.normal(&Point::origin()); - assert_eq!(normal, Vector::new(-1., 0., 0.)); + assert_eq!(normal, Unit::new_normalize(Vector::new(-1., 0., 0.))); } #[test]