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.
This commit is contained in:
parent
3ba17fac49
commit
05fb6601b2
|
@ -1,7 +1,15 @@
|
||||||
use super::core::color::LinearColor;
|
use super::core::color::LinearColor;
|
||||||
use super::Point2D;
|
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;
|
/// Represent the physical light properties of an object in the scene;
|
||||||
|
#[enum_dispatch::enum_dispatch(MaterialEnum)]
|
||||||
pub trait Material: std::fmt::Debug {
|
pub trait Material: std::fmt::Debug {
|
||||||
/// The diffuse component on a texel point.
|
/// The diffuse component on a texel point.
|
||||||
fn diffuse(&self, point: Point2D) -> LinearColor;
|
fn diffuse(&self, point: Point2D) -> LinearColor;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::Point2D;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// A material with the same characteristics on all points.
|
/// A material with the same characteristics on all points.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||||
pub struct UniformMaterial {
|
pub struct UniformMaterial {
|
||||||
diffuse: LinearColor,
|
diffuse: LinearColor,
|
||||||
specular: LinearColor,
|
specular: LinearColor,
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
use crate::material::Material;
|
use crate::material::MaterialEnum;
|
||||||
use crate::shape::Shape;
|
use crate::shape::ShapeEnum;
|
||||||
use crate::texture::Texture;
|
use crate::texture::TextureEnum;
|
||||||
|
|
||||||
/// An object being rendered in the scene.
|
/// An object being rendered in the scene.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Object<'a> {
|
pub struct Object {
|
||||||
pub shape: &'a dyn Shape,
|
pub shape: ShapeEnum,
|
||||||
pub material: &'a dyn Material,
|
pub material: MaterialEnum,
|
||||||
pub texture: &'a dyn Texture,
|
pub texture: TextureEnum,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Object<'a> {
|
impl Object {
|
||||||
pub fn new(shape: &'a dyn Shape, material: &'a dyn Material, texture: &'a dyn Texture) -> Self {
|
pub fn new(shape: ShapeEnum, material: MaterialEnum, texture: TextureEnum) -> Self {
|
||||||
Object {
|
Object {
|
||||||
shape,
|
shape,
|
||||||
material,
|
material,
|
||||||
|
@ -23,70 +23,33 @@ impl<'a> Object<'a> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::core::color::LinearColor;
|
use crate::core::color::LinearColor;
|
||||||
use crate::*;
|
use crate::material::UniformMaterial;
|
||||||
use bvh::{
|
use crate::shape::Sphere;
|
||||||
aabb::{Bounded, AABB},
|
use crate::texture::UniformTexture;
|
||||||
ray::Ray,
|
use crate::Point;
|
||||||
};
|
|
||||||
|
|
||||||
/// 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<f32> {
|
|
||||||
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!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_works() {
|
fn new_works() {
|
||||||
let shape = DummyShape {};
|
let shape = Sphere::new(Point::origin(), 1.);
|
||||||
let material = DummyMaterial {};
|
let material = UniformMaterial::new(
|
||||||
let texture = DummyTexture {};
|
LinearColor::new(0.5, 0.5, 0.5),
|
||||||
let _ = Object::new(&shape, &material, &texture);
|
LinearColor::new(1., 1., 1.),
|
||||||
// Can't compare the results... Just make sure the new compiles.
|
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(),
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,30 @@
|
||||||
use super::{light_aggregate::LightAggregate, object::Object};
|
use super::{light_aggregate::LightAggregate, object::Object};
|
||||||
use crate::core::Camera;
|
use crate::{
|
||||||
use crate::core::LinearColor;
|
core::{Camera, LinearColor},
|
||||||
use crate::{Point, Point2D, Vector};
|
material::Material,
|
||||||
|
shape::Shape,
|
||||||
|
texture::Texture,
|
||||||
|
{Point, Point2D, Vector},
|
||||||
|
};
|
||||||
use bvh::ray::Ray;
|
use bvh::ray::Ray;
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
use rand::prelude::thread_rng;
|
use rand::prelude::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
/// Represent the scene being rendered.
|
/// Represent the scene being rendered.
|
||||||
pub struct Scene<'a> {
|
pub struct Scene {
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
lights: LightAggregate,
|
lights: LightAggregate,
|
||||||
objects: Vec<Object<'a>>,
|
objects: Vec<Object>,
|
||||||
aliasing_limit: u32,
|
aliasing_limit: u32,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Scene<'a> {
|
impl Scene {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
lights: LightAggregate,
|
lights: LightAggregate,
|
||||||
objects: Vec<Object<'a>>,
|
objects: Vec<Object>,
|
||||||
aliasing_limit: u32,
|
aliasing_limit: u32,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
@ -1,15 +1,34 @@
|
||||||
use super::{Point, Point2D, Vector};
|
use super::{Point, Point2D, Vector};
|
||||||
use bvh::aabb::Bounded;
|
use bvh::{
|
||||||
use bvh::ray::Ray;
|
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.
|
/// 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.
|
/// Return the distance at which the object intersects with the ray, or None if it does not.
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f32>;
|
fn intersect(&self, ray: &Ray) -> Option<f32>;
|
||||||
/// Return the unit vector corresponding to the normal at this point of the shape.
|
/// 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) -> Vector;
|
||||||
/// Project the point from the shape's surface to its texel coordinates.
|
/// Project the point from the shape's surface to its texel coordinates.
|
||||||
fn project_texel(&self, point: &Point) -> Point2D;
|
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;
|
pub mod sphere;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use super::Shape;
|
use super::Shape;
|
||||||
use crate::{Point, Point2D, Vector};
|
use crate::{Point, Point2D, Vector};
|
||||||
use bvh::aabb::{Bounded, AABB};
|
use bvh::aabb::AABB;
|
||||||
use bvh::ray::Ray;
|
use bvh::ray::Ray;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// Represent a sphere shape inside the scene.
|
/// Represent a sphere shape inside the scene.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
/// The sphere is inverted if it is expected to be seen from the inside.
|
/// The sphere is inverted if it is expected to be seen from the inside.
|
||||||
inverted: bool,
|
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 {
|
impl Shape for Sphere {
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -91,6 +82,13 @@ impl Shape for Sphere {
|
||||||
0.5 + (point.y - self.center.y) / (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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use super::Shape;
|
use super::Shape;
|
||||||
use crate::{Point, Point2D, Vector};
|
use crate::{Point, Point2D, Vector};
|
||||||
use bvh::aabb::{Bounded, AABB};
|
use bvh::aabb::AABB;
|
||||||
use bvh::ray::Ray;
|
use bvh::ray::Ray;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
/// Represent a triangle inside the scene.
|
/// Represent a triangle inside the scene.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Triangle {
|
pub struct Triangle {
|
||||||
c0: Point,
|
c0: Point,
|
||||||
c0c1: Vector,
|
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 {
|
impl Shape for Triangle {
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
||||||
let pvec = ray.direction.cross(&self.c0c2);
|
let pvec = ray.direction.cross(&self.c0c2);
|
||||||
|
@ -88,6 +79,13 @@ impl Shape for Triangle {
|
||||||
fn project_texel(&self, point: &Point) -> Point2D {
|
fn project_texel(&self, point: &Point) -> Point2D {
|
||||||
self.barycentric(point)
|
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)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
use super::core::LinearColor;
|
use super::core::LinearColor;
|
||||||
use super::Point2D;
|
use super::Point2D;
|
||||||
|
|
||||||
|
/// All the existing `Texture` implementation.
|
||||||
|
#[enum_dispatch::enum_dispatch]
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum TextureEnum {
|
||||||
|
UniformTexture,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represent an object's texture.
|
/// Represent an object's texture.
|
||||||
|
#[enum_dispatch::enum_dispatch(TextureEnum)]
|
||||||
pub trait Texture: std::fmt::Debug {
|
pub trait Texture: std::fmt::Debug {
|
||||||
/// Get the color at a given texel coordinate
|
/// Get the color at a given texel coordinate
|
||||||
fn texel_color(&self, point: Point2D) -> LinearColor;
|
fn texel_color(&self, point: Point2D) -> LinearColor;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::Point2D;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// A texture with the same color on all points.
|
/// A texture with the same color on all points.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||||
pub struct UniformTexture {
|
pub struct UniformTexture {
|
||||||
color: LinearColor,
|
color: LinearColor,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue