From 05fb6601b261104066a3752add69b25d1ce39513 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 18 Mar 2020 15:24:09 +0100 Subject: [PATCH] library: use enum_dispatch on Object related trait This allows for easier testing (notable allowing for comparisons), better performance (thanks to inlining instead of vtable indirection), and should ease the deserialization of `Material`, `Shape`, and `Texture` traits. This should allow `Object` to be deserialized easily. --- src/material/mod.rs | 8 +++ src/material/uniform.rs | 2 +- src/render/object.rs | 105 +++++++++++++--------------------------- src/render/scene.rs | 18 ++++--- src/shape/mod.rs | 25 ++++++++-- src/shape/sphere.rs | 20 ++++---- src/shape/triangle.rs | 20 ++++---- src/texture/mod.rs | 8 +++ src/texture/uniform.rs | 2 +- 9 files changed, 103 insertions(+), 105 deletions(-) diff --git a/src/material/mod.rs b/src/material/mod.rs index 0c0c77e..66ef234 100644 --- a/src/material/mod.rs +++ b/src/material/mod.rs @@ -1,7 +1,15 @@ use super::core::color::LinearColor; use super::Point2D; +/// All the existing `Material` implementation. +#[enum_dispatch::enum_dispatch] +#[derive(Debug, PartialEq)] +pub enum MaterialEnum { + UniformMaterial, +} + /// Represent the physical light properties of an object in the scene; +#[enum_dispatch::enum_dispatch(MaterialEnum)] pub trait Material: std::fmt::Debug { /// The diffuse component on a texel point. fn diffuse(&self, point: Point2D) -> LinearColor; diff --git a/src/material/uniform.rs b/src/material/uniform.rs index 3cb7b8b..bb73c11 100644 --- a/src/material/uniform.rs +++ b/src/material/uniform.rs @@ -4,7 +4,7 @@ use crate::Point2D; use serde::Deserialize; /// A material with the same characteristics on all points. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize)] pub struct UniformMaterial { diffuse: LinearColor, specular: LinearColor, diff --git a/src/render/object.rs b/src/render/object.rs index f40919b..b97b098 100644 --- a/src/render/object.rs +++ b/src/render/object.rs @@ -1,17 +1,17 @@ -use crate::material::Material; -use crate::shape::Shape; -use crate::texture::Texture; +use crate::material::MaterialEnum; +use crate::shape::ShapeEnum; +use crate::texture::TextureEnum; /// An object being rendered in the scene. -#[derive(Debug)] -pub struct Object<'a> { - pub shape: &'a dyn Shape, - pub material: &'a dyn Material, - pub texture: &'a dyn Texture, +#[derive(Debug, PartialEq)] +pub struct Object { + pub shape: ShapeEnum, + pub material: MaterialEnum, + pub texture: TextureEnum, } -impl<'a> Object<'a> { - pub fn new(shape: &'a dyn Shape, material: &'a dyn Material, texture: &'a dyn Texture) -> Self { +impl Object { + pub fn new(shape: ShapeEnum, material: MaterialEnum, texture: TextureEnum) -> Self { Object { shape, material, @@ -23,70 +23,33 @@ impl<'a> Object<'a> { #[cfg(test)] mod test { use super::*; - use crate::core::color::LinearColor; - use crate::*; - use bvh::{ - aabb::{Bounded, AABB}, - ray::Ray, - }; - - /// NOTE(Bruno): those dummy implementations could be used somewhere else ? - - #[derive(Debug)] - struct DummyShape {} - - impl Bounded for DummyShape { - fn aabb(&self) -> AABB { - todo!() - } - } - - impl Shape for DummyShape { - /// Return the distance at which the object intersects with the ray, or None if it does not. - fn intersect(&self, _: &Ray) -> Option { - todo!() - } - /// Return the unit vector corresponding to the normal at this point of the shape. - fn normal(&self, _: &Point) -> Vector { - todo!() - } - /// Project the point from the shape's surface to its texel coordinates. - fn project_texel(&self, _: &Point) -> Point2D { - todo!() - } - } - - #[derive(Debug)] - struct DummyMaterial {} - - impl Material for DummyMaterial { - fn diffuse(&self, _: Point2D) -> LinearColor { - todo!() - } - fn specular(&self, _: Point2D) -> LinearColor { - todo!() - } - fn reflectivity(&self, _: Point2D) -> f32 { - todo!() - } - } - - #[derive(Debug)] - struct DummyTexture {} - - impl Texture for DummyTexture { - fn texel_color(&self, _: Point2D) -> LinearColor { - todo!() - } - } + use crate::material::UniformMaterial; + use crate::shape::Sphere; + use crate::texture::UniformTexture; + use crate::Point; #[test] fn new_works() { - let shape = DummyShape {}; - let material = DummyMaterial {}; - let texture = DummyTexture {}; - let _ = Object::new(&shape, &material, &texture); - // Can't compare the results... Just make sure the new compiles. + let shape = Sphere::new(Point::origin(), 1.); + let material = UniformMaterial::new( + LinearColor::new(0.5, 0.5, 0.5), + LinearColor::new(1., 1., 1.), + 0.5, + ); + let texture = UniformTexture::new(LinearColor::new(0.25, 0.5, 1.)); + let object = Object::new( + shape.clone().into(), + material.clone().into(), + texture.clone().into(), + ); + assert_eq!( + object, + Object { + shape: shape.into(), + material: material.into(), + texture: texture.into(), + } + ) } } diff --git a/src/render/scene.rs b/src/render/scene.rs index ee2b96f..bef987d 100644 --- a/src/render/scene.rs +++ b/src/render/scene.rs @@ -1,26 +1,30 @@ use super::{light_aggregate::LightAggregate, object::Object}; -use crate::core::Camera; -use crate::core::LinearColor; -use crate::{Point, Point2D, Vector}; +use crate::{ + core::{Camera, LinearColor}, + material::Material, + shape::Shape, + texture::Texture, + {Point, Point2D, Vector}, +}; use bvh::ray::Ray; use image::RgbImage; use rand::prelude::thread_rng; use rand::Rng; /// Represent the scene being rendered. -pub struct Scene<'a> { +pub struct Scene { camera: Camera, lights: LightAggregate, - objects: Vec>, + objects: Vec, aliasing_limit: u32, reflection_limit: u32, } -impl<'a> Scene<'a> { +impl Scene { pub fn new( camera: Camera, lights: LightAggregate, - objects: Vec>, + objects: Vec, aliasing_limit: u32, reflection_limit: u32, ) -> Self { diff --git a/src/shape/mod.rs b/src/shape/mod.rs index 79d2df1..e4af43b 100644 --- a/src/shape/mod.rs +++ b/src/shape/mod.rs @@ -1,15 +1,34 @@ use super::{Point, Point2D, Vector}; -use bvh::aabb::Bounded; -use bvh::ray::Ray; +use bvh::{ + aabb::{Bounded, AABB}, + ray::Ray, +}; + +/// All the existing `Shape` implementation. +#[enum_dispatch::enum_dispatch] +#[derive(Debug, PartialEq)] +pub enum ShapeEnum { + Sphere, + Triangle, +} /// Represent an abstract shape inside the scene. -pub trait Shape: Bounded + std::fmt::Debug { +#[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; /// Return the unit vector corresponding to the normal at this point of the shape. fn normal(&self, point: &Point) -> Vector; /// 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; +} + +impl Bounded for dyn Shape { + fn aabb(&self) -> AABB { + self.aabb() + } } pub mod sphere; diff --git a/src/shape/sphere.rs b/src/shape/sphere.rs index 2eab7d0..c85d3fb 100644 --- a/src/shape/sphere.rs +++ b/src/shape/sphere.rs @@ -1,11 +1,11 @@ use super::Shape; use crate::{Point, Point2D, Vector}; -use bvh::aabb::{Bounded, AABB}; +use bvh::aabb::AABB; use bvh::ray::Ray; use serde::Deserialize; /// Represent a sphere shape inside the scene. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize)] pub struct Sphere { /// The sphere is inverted if it is expected to be seen from the inside. inverted: bool, @@ -35,15 +35,6 @@ impl Sphere { } } -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) - } -} - impl Shape for Sphere { fn intersect(&self, ray: &Ray) -> Option { use std::mem; @@ -91,6 +82,13 @@ impl Shape for Sphere { 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) + } } #[cfg(test)] diff --git a/src/shape/triangle.rs b/src/shape/triangle.rs index b5f1c8d..a1a8fd5 100644 --- a/src/shape/triangle.rs +++ b/src/shape/triangle.rs @@ -1,11 +1,11 @@ use super::Shape; use crate::{Point, Point2D, Vector}; -use bvh::aabb::{Bounded, AABB}; +use bvh::aabb::AABB; use bvh::ray::Ray; use serde::{Deserialize, Deserializer}; /// Represent a triangle inside the scene. -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Triangle { c0: Point, c0c1: Vector, @@ -40,15 +40,6 @@ impl Triangle { } } -impl Bounded for Triangle { - fn aabb(&self) -> AABB { - AABB::empty() - .grow(&self.c0) - .grow(&(self.c0 + self.c0c1)) - .grow(&(self.c0 + self.c0c2)) - } -} - impl Shape for Triangle { fn intersect(&self, ray: &Ray) -> Option { let pvec = ray.direction.cross(&self.c0c2); @@ -88,6 +79,13 @@ impl Shape for Triangle { 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)) + } } #[derive(Debug, Deserialize)] diff --git a/src/texture/mod.rs b/src/texture/mod.rs index 455e670..1c1504c 100644 --- a/src/texture/mod.rs +++ b/src/texture/mod.rs @@ -1,7 +1,15 @@ use super::core::LinearColor; use super::Point2D; +/// All the existing `Texture` implementation. +#[enum_dispatch::enum_dispatch] +#[derive(Debug, PartialEq)] +pub enum TextureEnum { + UniformTexture, +} + /// Represent an object's texture. +#[enum_dispatch::enum_dispatch(TextureEnum)] pub trait Texture: std::fmt::Debug { /// Get the color at a given texel coordinate fn texel_color(&self, point: Point2D) -> LinearColor; diff --git a/src/texture/uniform.rs b/src/texture/uniform.rs index 02492eb..2f1d587 100644 --- a/src/texture/uniform.rs +++ b/src/texture/uniform.rs @@ -4,7 +4,7 @@ use crate::Point2D; use serde::Deserialize; /// A texture with the same color on all points. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize)] pub struct UniformTexture { color: LinearColor, }