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:
Bruno BELANYI 2020-03-18 15:24:09 +01:00
parent 3ba17fac49
commit 05fb6601b2
9 changed files with 103 additions and 105 deletions

View file

@ -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;

View file

@ -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,

View file

@ -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<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!()
}
}
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(),
}
)
}
}

View file

@ -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<Object<'a>>,
objects: Vec<Object>,
aliasing_limit: u32,
reflection_limit: u32,
}
impl<'a> Scene<'a> {
impl Scene {
pub fn new(
camera: Camera,
lights: LightAggregate,
objects: Vec<Object<'a>>,
objects: Vec<Object>,
aliasing_limit: u32,
reflection_limit: u32,
) -> Self {

View file

@ -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<f32>;
/// 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;

View file

@ -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<f32> {
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)]

View file

@ -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<f32> {
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)]

View file

@ -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;

View file

@ -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,
}