diff --git a/pathtracer/src/render/object.rs b/pathtracer/src/render/object.rs index c76b166..deea9a7 100644 --- a/pathtracer/src/render/object.rs +++ b/pathtracer/src/render/object.rs @@ -1,7 +1,7 @@ //! Logic for the scene objects use crate::material::MaterialEnum; -use crate::shape::{Shape, ShapeEnum}; +use crate::shape::ShapeEnum; use crate::texture::TextureEnum; use crate::Point; use beevee::{ diff --git a/pathtracer/src/shape/interpolated_triangle.rs b/pathtracer/src/shape/interpolated_triangle.rs index 77af206..6b63cdd 100644 --- a/pathtracer/src/shape/interpolated_triangle.rs +++ b/pathtracer/src/shape/interpolated_triangle.rs @@ -1,7 +1,8 @@ use super::triangle::Triangle; use super::Shape; use crate::{Point, Point2D, Vector}; -use beevee::aabb::AABB; +use beevee::aabb::{Bounded, AABB}; +use beevee::bvh::Intersected; use beevee::ray::Ray; use nalgebra::Unit; use serde::Deserialize; @@ -52,10 +53,6 @@ impl InterpolatedTriangle { } impl Shape for InterpolatedTriangle { - fn intersect(&self, ray: &Ray) -> Option { - self.tri.intersect(ray) - } - fn normal(&self, point: &Point) -> Unit { let (u, v) = { let c = self.tri.barycentric(point); @@ -70,7 +67,9 @@ impl Shape for InterpolatedTriangle { fn project_texel(&self, point: &Point) -> Point2D { self.tri.project_texel(point) } +} +impl Bounded for InterpolatedTriangle { fn aabb(&self) -> AABB { self.tri.aabb() } @@ -80,6 +79,12 @@ impl Shape for InterpolatedTriangle { } } +impl Intersected for InterpolatedTriangle { + fn intersect(&self, ray: &Ray) -> Option { + self.tri.intersect(ray) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/pathtracer/src/shape/mod.rs b/pathtracer/src/shape/mod.rs index e510e93..69aa181 100644 --- a/pathtracer/src/shape/mod.rs +++ b/pathtracer/src/shape/mod.rs @@ -21,35 +21,42 @@ pub enum ShapeEnum { InterpolatedTriangle, } +// FIXME: this has to be written by hand due to a limitation of `enum_dispatch` on super traits +impl Bounded for ShapeEnum { + fn aabb(&self) -> AABB { + match self { + ShapeEnum::Sphere(s) => s.aabb(), + ShapeEnum::Triangle(s) => s.aabb(), + ShapeEnum::InterpolatedTriangle(s) => s.aabb(), + } + } + + fn centroid(&self) -> Point { + match self { + ShapeEnum::Sphere(s) => s.centroid(), + ShapeEnum::Triangle(s) => s.centroid(), + ShapeEnum::InterpolatedTriangle(s) => s.centroid(), + } + } +} + +impl Intersected for ShapeEnum { + fn intersect(&self, ray: &Ray) -> Option { + match self { + ShapeEnum::Sphere(s) => s.intersect(ray), + ShapeEnum::Triangle(s) => s.intersect(ray), + ShapeEnum::InterpolatedTriangle(s) => s.intersect(ray), + } + } +} + /// Represent an abstract shape inside the scene. #[enum_dispatch::enum_dispatch(ShapeEnum)] -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; +pub trait Shape: std::fmt::Debug + Intersected { /// Return the unit vector corresponding to the normal at this point of the shape. 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() - } -} - -impl Intersected for dyn Shape { - fn intersect(&self, ray: &Ray) -> Option { - self.intersect(ray) - } } mod interpolated_triangle; diff --git a/pathtracer/src/shape/sphere.rs b/pathtracer/src/shape/sphere.rs index 8022cae..bbd74a5 100644 --- a/pathtracer/src/shape/sphere.rs +++ b/pathtracer/src/shape/sphere.rs @@ -1,6 +1,7 @@ use super::Shape; use crate::{Point, Point2D, Vector}; -use beevee::aabb::AABB; +use beevee::aabb::{Bounded, AABB}; +use beevee::bvh::Intersected; use beevee::ray::Ray; use nalgebra::Unit; use serde::Deserialize; @@ -38,6 +39,38 @@ impl Sphere { } impl Shape for Sphere { + fn normal(&self, point: &Point) -> Unit { + let delt = if self.inverted { + self.center - point + } else { + point - self.center + }; + Unit::new_normalize(delt) + } + + fn project_texel(&self, point: &Point) -> Point2D { + // Project the sphere on the XY-plane + Point2D::new( + 0.5 + (point.x - self.center.x) / (2. * self.radius), + 0.5 + (point.y - self.center.y) / (2. * self.radius), + ) + } +} + +impl Bounded for Sphere { + fn aabb(&self) -> AABB { + let delt = Vector::new(self.radius, self.radius, self.radius); + let min = self.center - delt; + let max = self.center + delt; + AABB::with_bounds(min, max) + } + + fn centroid(&self) -> Point { + self.center + } +} + +impl Intersected for Sphere { fn intersect(&self, ray: &Ray) -> Option { use std::mem; @@ -67,34 +100,6 @@ impl Shape for Sphere { Some(t_0) } } - - fn normal(&self, point: &Point) -> Unit { - let delt = if self.inverted { - self.center - point - } else { - point - self.center - }; - Unit::new_normalize(delt) - } - - fn project_texel(&self, point: &Point) -> Point2D { - // Project the sphere on the XY-plane - Point2D::new( - 0.5 + (point.x - self.center.x) / (2. * self.radius), - 0.5 + (point.y - self.center.y) / (2. * self.radius), - ) - } - - fn aabb(&self) -> AABB { - let delt = Vector::new(self.radius, self.radius, self.radius); - let min = self.center - delt; - let max = self.center + delt; - AABB::with_bounds(min, max) - } - - fn centroid(&self) -> Point { - self.center - } } #[cfg(test)] diff --git a/pathtracer/src/shape/triangle.rs b/pathtracer/src/shape/triangle.rs index b539d01..0ab3861 100644 --- a/pathtracer/src/shape/triangle.rs +++ b/pathtracer/src/shape/triangle.rs @@ -1,6 +1,7 @@ use super::Shape; use crate::{Point, Point2D, Vector}; -use beevee::aabb::AABB; +use beevee::aabb::{Bounded, AABB}; +use beevee::bvh::Intersected; use beevee::ray::Ray; use nalgebra::Unit; use serde::{Deserialize, Deserializer}; @@ -58,6 +59,29 @@ impl Triangle { } impl Shape for Triangle { + fn normal(&self, _: &Point) -> Unit { + Unit::new_normalize(self.c0c1.cross(&self.c0c2)) + } + + fn project_texel(&self, point: &Point) -> Point2D { + self.barycentric(point) + } +} + +impl Bounded for Triangle { + fn aabb(&self) -> AABB { + AABB::empty() + .grow(&self.c0) + .grow(&(self.c0 + self.c0c1)) + .grow(&(self.c0 + self.c0c2)) + } + + fn centroid(&self) -> Point { + self.c0 + (self.c0c1 + self.c0c2) / 2. + } +} + +impl Intersected for Triangle { fn intersect(&self, ray: &Ray) -> Option { let pvec = ray.direction.cross(&self.c0c2); let det = self.c0c1.dot(&pvec); @@ -88,25 +112,6 @@ impl Shape for Triangle { Some(t) } } - - fn normal(&self, _: &Point) -> Unit { - Unit::new_normalize(self.c0c1.cross(&self.c0c2)) - } - - fn project_texel(&self, point: &Point) -> Point2D { - self.barycentric(point) - } - - fn aabb(&self) -> AABB { - AABB::empty() - .grow(&self.c0) - .grow(&(self.c0 + self.c0c1)) - .grow(&(self.c0 + self.c0c2)) - } - - fn centroid(&self) -> Point { - self.c0 + (self.c0c1 + self.c0c2) / 2. - } } #[derive(Debug, Deserialize)]