Create Real trait to replace nalgebra::RealField

Real inherits RealField, but I want to add more to it.
This commit is contained in:
Matthew Gordon 2020-02-10 16:52:09 -05:00
parent a15eeccdfb
commit d6b5c87759
19 changed files with 141 additions and 111 deletions

View File

@ -1,4 +1,4 @@
use nalgebra::{convert, Point3, RealField, Vector3}; use nalgebra::{convert, Point3, Vector3};
use super::colour::{ColourRgbF, NamedColour}; use super::colour::{ColourRgbF, NamedColour};
use super::image::ImageRgbF; use super::image::ImageRgbF;
@ -7,9 +7,11 @@ use super::raycasting::Ray;
use super::sampler::Sampler; use super::sampler::Sampler;
use super::scene::Scene; use super::scene::Scene;
use crate::Real;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
struct ImageSampler<T: RealField> { struct ImageSampler<T: Real> {
image_height_pixels: u32, image_height_pixels: u32,
image_width_pixels: u32, image_width_pixels: u32,
@ -20,7 +22,7 @@ struct ImageSampler<T: RealField> {
film_distance: T, film_distance: T,
} }
impl<T: RealField> ImageSampler<T> { impl<T: Real> ImageSampler<T> {
pub fn new(width: u32, height: u32, camera_location: Point3<T>) -> ImageSampler<T> { pub fn new(width: u32, height: u32, camera_location: Point3<T>) -> ImageSampler<T> {
let (film_width, film_height) = { let (film_width, film_height) = {
let width: T = convert(width as f64); let width: T = convert(width as f64);
@ -64,13 +66,13 @@ impl<T: RealField> ImageSampler<T> {
} }
} }
pub fn render_scene<T: RealField>(output_image: Arc<Mutex<ImageRgbF<T>>>, scene: Arc<Scene<T>>) { pub fn render_scene<T: Real>(output_image: Arc<Mutex<ImageRgbF<T>>>, scene: Arc<Scene<T>>) {
let height = output_image.lock().unwrap().get_height(); let height = output_image.lock().unwrap().get_height();
let width = output_image.lock().unwrap().get_width(); let width = output_image.lock().unwrap().get_width();
partial_render_scene(output_image, scene, 0, height, 0, width, height, width) partial_render_scene(output_image, scene, 0, height, 0, width, height, width)
} }
pub fn partial_render_scene<T: RealField>( pub fn partial_render_scene<T: Real>(
output_image: Arc<Mutex<ImageRgbF<T>>>, output_image: Arc<Mutex<ImageRgbF<T>>>,
scene: Arc<Scene<T>>, scene: Arc<Scene<T>>,
row_start: u32, row_start: u32,

View File

@ -1,13 +1,15 @@
use nalgebra::{convert, RealField, Vector3}; use nalgebra::{convert, Vector3};
use crate::Real;
use std::ops::{Add, Mul}; use std::ops::{Add, Mul};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct ColourRgbF<T: RealField> { pub struct ColourRgbF<T: Real> {
values: Vector3<T>, values: Vector3<T>,
} }
impl<T: RealField> ColourRgbF<T> { impl<T: Real> ColourRgbF<T> {
pub fn new(red: T, green: T, blue: T) -> ColourRgbF<T> { pub fn new(red: T, green: T, blue: T) -> ColourRgbF<T> {
ColourRgbF { ColourRgbF {
values: Vector3::new(red, green, blue), values: Vector3::new(red, green, blue),
@ -80,7 +82,7 @@ pub enum NamedColour {
Navy, Navy,
} }
impl<T: RealField> Add<ColourRgbF<T>> for ColourRgbF<T> { impl<T: Real> Add<ColourRgbF<T>> for ColourRgbF<T> {
type Output = ColourRgbF<T>; type Output = ColourRgbF<T>;
fn add(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> { fn add(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> {
ColourRgbF { ColourRgbF {
@ -89,7 +91,7 @@ impl<T: RealField> Add<ColourRgbF<T>> for ColourRgbF<T> {
} }
} }
impl<T: RealField> Mul<T> for ColourRgbF<T> { impl<T: Real> Mul<T> for ColourRgbF<T> {
type Output = ColourRgbF<T>; type Output = ColourRgbF<T>;
fn mul(self, rhs: T) -> ColourRgbF<T> { fn mul(self, rhs: T) -> ColourRgbF<T> {
ColourRgbF { ColourRgbF {
@ -98,7 +100,7 @@ impl<T: RealField> Mul<T> for ColourRgbF<T> {
} }
} }
impl<T: RealField> Mul<ColourRgbF<T>> for ColourRgbF<T> { impl<T: Real> Mul<ColourRgbF<T>> for ColourRgbF<T> {
type Output = ColourRgbF<T>; type Output = ColourRgbF<T>;
fn mul(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> { fn mul(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> {
ColourRgbF { ColourRgbF {
@ -115,7 +117,7 @@ mod tests {
use super::*; use super::*;
use quickcheck::{Arbitrary, Gen}; use quickcheck::{Arbitrary, Gen};
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
impl<T: Arbitrary + RealField> Arbitrary for ColourRgbF<T> { impl<T: Arbitrary + Real> Arbitrary for ColourRgbF<T> {
fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF<T> { fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF<T> {
let values = <Vector3<T> as Arbitrary>::arbitrary(g); let values = <Vector3<T> as Arbitrary>::arbitrary(g);
ColourRgbF { values } ColourRgbF { values }

View File

@ -1,9 +1,11 @@
use std::convert::TryInto; use std::convert::TryInto;
use nalgebra::{clamp, convert, RealField, Vector3}; use nalgebra::{clamp, convert, Vector3};
use super::colour::{ColourRgbF, ColourRgbU8}; use super::colour::{ColourRgbF, ColourRgbU8};
use crate::Real;
pub struct ImageRgbU8 { pub struct ImageRgbU8 {
pixel_data: Vec<u8>, pixel_data: Vec<u8>,
width: u32, width: u32,
@ -64,13 +66,13 @@ impl ImageRgbU8 {
} }
} }
pub struct ImageRgbF<T: RealField> { pub struct ImageRgbF<T: Real> {
pixel_data: Vec<T>, pixel_data: Vec<T>,
width: u32, width: u32,
height: u32, height: u32,
} }
impl<T: RealField> ImageRgbF<T> { impl<T: Real> ImageRgbF<T> {
pub fn new(width: u32, height: u32) -> ImageRgbF<T> { pub fn new(width: u32, height: u32) -> ImageRgbF<T> {
ImageRgbF { ImageRgbF {
width, width,
@ -145,7 +147,7 @@ impl NormalizedAsByte for f64 {
} }
} }
pub trait ToneMapper<T: RealField> { pub trait ToneMapper<T: Real> {
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8); fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8);
} }
@ -153,12 +155,12 @@ pub trait ToneMapper<T: RealField> {
pub struct ClampingToneMapper {} pub struct ClampingToneMapper {}
impl ClampingToneMapper { impl ClampingToneMapper {
fn clamp<T: RealField + NormalizedAsByte>(v: &T) -> u8 { fn clamp<T: Real + NormalizedAsByte>(v: &T) -> u8 {
clamp(v, &T::zero(), &T::one()).normalized_to_byte() clamp(v, &T::zero(), &T::one()).normalized_to_byte()
} }
} }
impl<T: RealField + NormalizedAsByte> ToneMapper<T> for ClampingToneMapper { impl<T: Real + NormalizedAsByte> ToneMapper<T> for ClampingToneMapper {
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8) { fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8) {
assert!(image_in.get_width() == image_out.get_width()); assert!(image_in.get_width() == image_out.get_width());
assert!(image_in.get_height() == image_out.get_height()); assert!(image_in.get_height() == image_out.get_height());

View File

@ -1,27 +1,29 @@
use nalgebra::{convert, RealField, Vector3}; use nalgebra::{convert, Vector3};
use super::colour::ColourRgbF; use super::colour::ColourRgbF;
use super::raycasting::{IntersectionInfo, Ray}; use super::raycasting::{IntersectionInfo, Ray};
use super::sampler::Sampler; use super::sampler::Sampler;
use super::util::algebra_utils::try_change_of_basis_matrix; use super::util::algebra_utils::try_change_of_basis_matrix;
pub trait Integrator<T: RealField> { use crate::Real;
pub trait Integrator<T: Real> {
fn integrate(&self, sampler: &Sampler<T>, info: &IntersectionInfo<T>) -> ColourRgbF<T>; fn integrate(&self, sampler: &Sampler<T>, info: &IntersectionInfo<T>) -> ColourRgbF<T>;
} }
pub struct DirectionalLight<T: RealField> { pub struct DirectionalLight<T: Real> {
pub direction: Vector3<T>, pub direction: Vector3<T>,
pub colour: ColourRgbF<T>, pub colour: ColourRgbF<T>,
} }
pub struct WhittedIntegrator<T: RealField> { pub struct WhittedIntegrator<T: Real> {
pub ambient_light: ColourRgbF<T>, pub ambient_light: ColourRgbF<T>,
pub lights: Vec<DirectionalLight<T>>, pub lights: Vec<DirectionalLight<T>>,
} }
// TODO: Get rid of the magic bias number, which should be calculated base on expected error // TODO: Get rid of the magic bias number, which should be calculated base on expected error
// bounds and tangent direction // bounds and tangent direction
impl<T: RealField> Integrator<T> for WhittedIntegrator<T> { impl<T: Real> Integrator<T> for WhittedIntegrator<T> {
fn integrate(&self, sampler: &Sampler<T>, info: &IntersectionInfo<T>) -> ColourRgbF<T> { fn integrate(&self, sampler: &Sampler<T>, info: &IntersectionInfo<T>) -> ColourRgbF<T> {
let world_to_bsdf_space = let world_to_bsdf_space =
try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal) try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal)

View File

@ -5,10 +5,13 @@ pub mod integrators;
pub mod materials; pub mod materials;
pub mod mesh; pub mod mesh;
pub mod raycasting; pub mod raycasting;
pub mod realtype;
pub mod sampler; pub mod sampler;
pub mod scene; pub mod scene;
pub mod util; pub mod util;
use realtype::Real;
#[cfg(bench)] #[cfg(bench)]
mod tests { mod tests {
extern crate test; extern crate test;

View File

@ -1,12 +1,13 @@
use nalgebra::{clamp, convert, RealField, Vector3}; use nalgebra::{clamp, convert, Vector3};
use super::colour::{ColourRgbF, NamedColour}; use super::colour::{ColourRgbF, NamedColour};
use crate::Real;
use std::fmt::Debug; use std::fmt::Debug;
type Bsdf<'a, T> = Box<dyn Fn(Vector3<T>, Vector3<T>, ColourRgbF<T>) -> ColourRgbF<T> + 'a>; type Bsdf<'a, T> = Box<dyn Fn(Vector3<T>, Vector3<T>, ColourRgbF<T>) -> ColourRgbF<T> + 'a>;
pub trait Material<T: RealField>: Debug + Sync + Send { pub trait Material<T: Real>: Debug + Sync + Send {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T>; fn bsdf<'a>(&'a self) -> Bsdf<'a, T>;
fn sample(&self, _w_o: &Vector3<T>) -> Vec<Vector3<T>> { fn sample(&self, _w_o: &Vector3<T>) -> Vec<Vector3<T>> {
@ -15,12 +16,12 @@ pub trait Material<T: RealField>: Debug + Sync + Send {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct LambertianMaterial<T: RealField> { pub struct LambertianMaterial<T: Real> {
pub colour: ColourRgbF<T>, pub colour: ColourRgbF<T>,
pub diffuse_strength: T, pub diffuse_strength: T,
} }
impl<T: RealField> LambertianMaterial<T> { impl<T: Real> LambertianMaterial<T> {
pub fn new_dummy() -> LambertianMaterial<T> { pub fn new_dummy() -> LambertianMaterial<T> {
LambertianMaterial { LambertianMaterial {
colour: ColourRgbF::new(T::one(), T::one(), T::one()), colour: ColourRgbF::new(T::one(), T::one(), T::one()),
@ -29,7 +30,7 @@ impl<T: RealField> LambertianMaterial<T> {
} }
} }
impl<T: RealField> Material<T> for LambertianMaterial<T> { impl<T: Real> Material<T> for LambertianMaterial<T> {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T> { fn bsdf<'a>(&'a self) -> Bsdf<'a, T> {
Box::new( Box::new(
move |_w_o: Vector3<T>, _w_i: Vector3<T>, colour_in: ColourRgbF<T>| { move |_w_o: Vector3<T>, _w_i: Vector3<T>, colour_in: ColourRgbF<T>| {
@ -40,14 +41,14 @@ impl<T: RealField> Material<T> for LambertianMaterial<T> {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct PhongMaterial<T: RealField> { pub struct PhongMaterial<T: Real> {
pub colour: ColourRgbF<T>, pub colour: ColourRgbF<T>,
pub diffuse_strength: T, pub diffuse_strength: T,
pub specular_strength: T, pub specular_strength: T,
pub smoothness: T, pub smoothness: T,
} }
impl<T: RealField> Material<T> for PhongMaterial<T> { impl<T: Real> Material<T> for PhongMaterial<T> {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T> { fn bsdf<'a>(&'a self) -> Bsdf<'a, T> {
Box::new( Box::new(
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| { move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| {
@ -66,13 +67,13 @@ impl<T: RealField> Material<T> for PhongMaterial<T> {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ReflectiveMaterial<T: RealField> { pub struct ReflectiveMaterial<T: Real> {
pub colour: ColourRgbF<T>, pub colour: ColourRgbF<T>,
pub diffuse_strength: T, pub diffuse_strength: T,
pub reflection_strength: T, pub reflection_strength: T,
} }
impl<T: RealField> Material<T> for ReflectiveMaterial<T> { impl<T: Real> Material<T> for ReflectiveMaterial<T> {
fn bsdf<'a>(&'a self) -> Bsdf<'a, T> { fn bsdf<'a>(&'a self) -> Bsdf<'a, T> {
Box::new( Box::new(
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| { move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| {

View File

@ -1,17 +1,18 @@
mod wavefront_obj { mod wavefront_obj {
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use crate::raycasting::Triangle; use crate::raycasting::Triangle;
use alga::general::SupersetOf; use alga::general::SupersetOf;
use nalgebra::{convert, Point3, RealField, Vector3}; use nalgebra::{convert, Point3, Vector3};
use obj::{IndexTuple, Obj, SimplePolygon}; use obj::{IndexTuple, Obj, SimplePolygon};
use std::io::Result; use std::io::Result;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
fn get_vertex_and_normal<T: RealField>( fn get_vertex_and_normal<T: Real>(
index_tuple: &IndexTuple, index_tuple: &IndexTuple,
vertex_positions: &Vec<[f32; 3]>, vertex_positions: &Vec<[f32; 3]>,
normal_positions: &Vec<[f32; 3]>, normal_positions: &Vec<[f32; 3]>,
@ -28,7 +29,7 @@ mod wavefront_obj {
(vertex, normal) (vertex, normal)
} }
fn get_triangles<T: RealField>( fn get_triangles<T: Real>(
polygon: &SimplePolygon, polygon: &SimplePolygon,
vertex_positions: &Vec<[f32; 3]>, vertex_positions: &Vec<[f32; 3]>,
normal_positions: &Vec<[f32; 3]>, normal_positions: &Vec<[f32; 3]>,
@ -63,7 +64,7 @@ mod wavefront_obj {
} }
} }
pub fn load_obj<T: RealField>( pub fn load_obj<T: Real>(
filename: &Path, filename: &Path,
material: Arc<dyn Material<T>>, material: Arc<dyn Material<T>>,
) -> Result<Vec<Triangle<T>>> ) -> Result<Vec<Triangle<T>>>

View File

@ -1,4 +1,4 @@
use nalgebra::RealField; use crate::Real;
use crate::util::Interval; use crate::util::Interval;
@ -8,7 +8,7 @@ use itertools::izip;
pub use crate::util::axis_aligned_bounding_box::BoundingBox; pub use crate::util::axis_aligned_bounding_box::BoundingBox;
impl<T: RealField> IntersectP<T> for BoundingBox<T> { impl<T: Real> IntersectP<T> for BoundingBox<T> {
fn intersect(&self, ray: &Ray<T>) -> bool { fn intersect(&self, ray: &Ray<T>) -> bool {
let mut t_interval_in_bounds = Interval::infinite(); let mut t_interval_in_bounds = Interval::infinite();
for (&ray_origin, &ray_direction, bounds) in for (&ray_origin, &ray_direction, bounds) in

View File

@ -1,6 +1,7 @@
use nalgebra::{Point3, RealField, Vector3}; use nalgebra::{Point3, Vector3};
use super::materials::Material; use super::materials::Material;
use crate::Real;
use std::sync::Arc; use std::sync::Arc;
@ -16,14 +17,13 @@ pub use triangle::Triangle;
pub mod axis_aligned_bounding_box; pub mod axis_aligned_bounding_box;
pub use axis_aligned_bounding_box::BoundingBox; pub use axis_aligned_bounding_box::BoundingBox;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Ray<T: RealField> { pub struct Ray<T: Real> {
pub origin: Point3<T>, pub origin: Point3<T>,
pub direction: Vector3<T>, pub direction: Vector3<T>,
} }
impl<T: RealField> Ray<T> { impl<T: Real> Ray<T> {
pub fn new(origin: Point3<T>, direction: Vector3<T>) -> Ray<T> { pub fn new(origin: Point3<T>, direction: Vector3<T>) -> Ray<T> {
Ray { Ray {
origin, origin,
@ -41,7 +41,7 @@ impl<T: RealField> Ray<T> {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct IntersectionInfo<T: RealField> { pub struct IntersectionInfo<T: Real> {
pub distance: T, pub distance: T,
pub location: Point3<T>, pub location: Point3<T>,
pub normal: Vector3<T>, pub normal: Vector3<T>,
@ -51,21 +51,21 @@ pub struct IntersectionInfo<T: RealField> {
pub material: Arc<dyn Material<T>>, pub material: Arc<dyn Material<T>>,
} }
pub trait Intersect<T: RealField>: Send + Sync { pub trait Intersect<T: Real>: Send + Sync {
/// Test if the ray intersects the object, and return information about the object and intersection. /// Test if the ray intersects the object, and return information about the object and intersection.
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>>; fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>>;
} }
pub trait IntersectP<T: RealField>: Send + Sync { pub trait IntersectP<T: Real>: Send + Sync {
/// Test if the ray intersects the object, without calculating any extra information. /// Test if the ray intersects the object, without calculating any extra information.
fn intersect(&self, ray: &Ray<T>) -> bool; fn intersect(&self, ray: &Ray<T>) -> bool;
} }
pub trait HasBoundingBox<T: RealField>: Send + Sync { pub trait HasBoundingBox<T: Real>: Send + Sync {
fn bounding_box(&self) -> BoundingBox<T>; fn bounding_box(&self) -> BoundingBox<T>;
} }
pub trait Primitive<T: RealField>: Intersect<T> + HasBoundingBox<T> {} pub trait Primitive<T: Real>: Intersect<T> + HasBoundingBox<T> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -73,7 +73,7 @@ mod tests {
use super::*; use super::*;
use quickcheck::{Arbitrary, Gen}; use quickcheck::{Arbitrary, Gen};
impl<T: Arbitrary + RealField> Arbitrary for Ray<T> { impl<T: Arbitrary + Real> Arbitrary for Ray<T> {
fn arbitrary<G: Gen>(g: &mut G) -> Ray<T> { fn arbitrary<G: Gen>(g: &mut G) -> Ray<T> {
let origin = <Point3<T> as Arbitrary>::arbitrary(g); let origin = <Point3<T> as Arbitrary>::arbitrary(g);
let direction = <Vector3<T> as Arbitrary>::arbitrary(g); let direction = <Vector3<T> as Arbitrary>::arbitrary(g);
@ -98,9 +98,9 @@ mod tests {
let p3 = ray.point_at(t3); let p3 = ray.point_at(t3);
let epsilon = [t1, t2, t3, ray.origin[0], ray.origin[1], ray.origin[2]] let epsilon = [t1, t2, t3, ray.origin[0], ray.origin[1], ray.origin[2]]
.iter() .iter()
.fold(0.0, |a, &b| a.max(b.abs())) .fold(0.0f64, |a, &b| a.max(b.abs()))
* std::f64::EPSILON * std::f64::EPSILON
* 256.0; * 256.0f64;
(p2 - p1).cross(&(p3 - p2)).norm() < epsilon (p2 - p1).cross(&(p3 - p2)).norm() < epsilon
} }

View File

@ -1,12 +1,13 @@
use nalgebra::{convert, Point3, RealField, Vector3}; use nalgebra::{convert, Point3, Vector3};
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use std::sync::Arc; use std::sync::Arc;
pub struct Plane<T: RealField> { pub struct Plane<T: Real> {
normal: Vector3<T>, normal: Vector3<T>,
tangent: Vector3<T>, tangent: Vector3<T>,
cotangent: Vector3<T>, cotangent: Vector3<T>,
@ -14,7 +15,7 @@ pub struct Plane<T: RealField> {
material: Arc<dyn Material<T>>, material: Arc<dyn Material<T>>,
} }
impl<T: RealField> Plane<T> { impl<T: Real> Plane<T> {
pub fn new( pub fn new(
normal: Vector3<T>, normal: Vector3<T>,
distance_from_origin: T, distance_from_origin: T,
@ -35,7 +36,7 @@ impl<T: RealField> Plane<T> {
} }
} }
impl<T: RealField> Intersect<T> for Plane<T> { impl<T: Real> Intersect<T> for Plane<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let ray_direction_dot_plane_normal = ray.direction.dot(&self.normal); let ray_direction_dot_plane_normal = ray.direction.dot(&self.normal);
let point_on_plane = self.normal * self.distance_from_origin; let point_on_plane = self.normal * self.distance_from_origin;
@ -64,7 +65,7 @@ impl<T: RealField> Intersect<T> for Plane<T> {
} }
} }
impl<T: RealField> HasBoundingBox<T> for Plane<T> { impl<T: Real> HasBoundingBox<T> for Plane<T> {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox<T> {
let p0 = Point3::from(self.normal * self.distance_from_origin); let p0 = Point3::from(self.normal * self.distance_from_origin);
let f = |v: Vector3<T>| { let f = |v: Vector3<T>| {
@ -87,7 +88,7 @@ impl<T: RealField> HasBoundingBox<T> for Plane<T> {
} }
} }
impl<T: RealField> Primitive<T> for Plane<T> {} impl<T: Real> Primitive<T> for Plane<T> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,18 +1,19 @@
use nalgebra::{convert, Point3, RealField, Vector3}; use nalgebra::{convert, Point3, Vector3};
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use std::sync::Arc; use std::sync::Arc;
pub struct Sphere<T: RealField> { pub struct Sphere<T: Real> {
centre: Point3<T>, centre: Point3<T>,
radius: T, radius: T,
material: Arc<dyn Material<T>>, material: Arc<dyn Material<T>>,
} }
impl<T: RealField> Sphere<T> { impl<T: Real> Sphere<T> {
pub fn new(centre: Point3<T>, radius: T, material: Arc<dyn Material<T>>) -> Sphere<T> { pub fn new(centre: Point3<T>, radius: T, material: Arc<dyn Material<T>>) -> Sphere<T> {
Sphere { Sphere {
centre, centre,
@ -22,7 +23,7 @@ impl<T: RealField> Sphere<T> {
} }
} }
impl<T: RealField> Intersect<T> for Sphere<T> { impl<T: Real> Intersect<T> for Sphere<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let two: T = convert(2.0); let two: T = convert(2.0);
let four: T = convert(4.0); let four: T = convert(4.0);
@ -77,14 +78,14 @@ impl<T: RealField> Intersect<T> for Sphere<T> {
} }
} }
impl<T: RealField> HasBoundingBox<T> for Sphere<T> { impl<T: Real> HasBoundingBox<T> for Sphere<T> {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox<T> {
let radius_xyz = Vector3::new(self.radius, self.radius, self.radius); let radius_xyz = Vector3::new(self.radius, self.radius, self.radius);
BoundingBox::from_corners(self.centre + radius_xyz, self.centre - radius_xyz) BoundingBox::from_corners(self.centre + radius_xyz, self.centre - radius_xyz)
} }
} }
impl<T: RealField> Primitive<T> for Sphere<T> {} impl<T: Real> Primitive<T> for Sphere<T> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,18 +1,19 @@
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use nalgebra::{Point3, RealField, Vector2, Vector3}; use nalgebra::{Point3, Vector2, Vector3};
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug)] #[derive(Debug)]
pub struct Triangle<T: RealField> { pub struct Triangle<T: Real> {
pub vertices: [Point3<T>; 3], pub vertices: [Point3<T>; 3],
pub normals: [Vector3<T>; 3], pub normals: [Vector3<T>; 3],
pub material: Arc<dyn Material<T>>, pub material: Arc<dyn Material<T>>,
} }
impl<T: RealField> Intersect<T> for Triangle<T> { impl<T: Real> Intersect<T> for Triangle<T> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
let translation = -ray.origin.coords; let translation = -ray.origin.coords;
let indices = indices_with_index_of_largest_element_last(&ray.direction); let indices = indices_with_index_of_largest_element_last(&ray.direction);
@ -77,15 +78,15 @@ impl<T: RealField> Intersect<T> for Triangle<T> {
} }
} }
impl<T: RealField> HasBoundingBox<T> for Triangle<T> { impl<T: Real> HasBoundingBox<T> for Triangle<T> {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox<T> {
BoundingBox::from_points(&self.vertices) BoundingBox::from_points(&self.vertices)
} }
} }
impl<T: RealField> Primitive<T> for Triangle<T> {} impl<T: Real> Primitive<T> for Triangle<T> {}
fn indices_with_index_of_largest_element_last<T: RealField>(v: &Vector3<T>) -> [usize; 3] { fn indices_with_index_of_largest_element_last<T: Real>(v: &Vector3<T>) -> [usize; 3] {
if v.x > v.y { if v.x > v.y {
if v.z > v.x { if v.z > v.x {
[0, 1, 2] [0, 1, 2]
@ -105,24 +106,24 @@ fn is_valid_permutation(indices: &[usize; 3]) -> bool {
(0..2).all(|i: usize| indices.iter().any(|&j| j == i)) (0..2).all(|i: usize| indices.iter().any(|&j| j == i))
} }
fn permute_vector_elements<T: RealField>(v: &Vector3<T>, indices: &[usize; 3]) -> Vector3<T> { fn permute_vector_elements<T: Real>(v: &Vector3<T>, indices: &[usize; 3]) -> Vector3<T> {
debug_assert!(is_valid_permutation(&indices)); debug_assert!(is_valid_permutation(&indices));
Vector3::new(v[indices[0]], v[indices[1]], v[indices[2]]) Vector3::new(v[indices[0]], v[indices[1]], v[indices[2]])
} }
fn calculate_shear_to_z_axis<T: RealField>(v: &Vector3<T>) -> Vector2<T> { fn calculate_shear_to_z_axis<T: Real>(v: &Vector3<T>) -> Vector2<T> {
Vector2::new(-v.x / v.z, -v.y / v.z) Vector2::new(-v.x / v.z, -v.y / v.z)
} }
fn apply_shear_to_z_axis<T: RealField>(v: &Vector3<T>, s: &Vector2<T>) -> Vector3<T> { fn apply_shear_to_z_axis<T: Real>(v: &Vector3<T>, s: &Vector2<T>) -> Vector3<T> {
Vector3::new(v.x + s.x * v.z, v.y + s.y * v.z, v.z) Vector3::new(v.x + s.x * v.z, v.y + s.y * v.z, v.z)
} }
fn signed_edge_function<T: RealField>(a: &Vector3<T>, b: &Vector3<T>) -> T { fn signed_edge_function<T: Real>(a: &Vector3<T>, b: &Vector3<T>) -> T {
a.x * b.y - b.x * a.y a.x * b.y - b.x * a.y
} }
fn signed_edge_functions<T: RealField>(vertices: &Vec<Vector3<T>>) -> Vector3<T> { fn signed_edge_functions<T: Real>(vertices: &Vec<Vector3<T>>) -> Vector3<T> {
// Iterate over the inputs in such a way that each output element is calculated // Iterate over the inputs in such a way that each output element is calculated
// from the twoother elements of the input. ( (y,z) -> x, (z,x) -> y, (x,y) -> z ) // from the twoother elements of the input. ( (y,z) -> x, (z,x) -> y, (x,y) -> z )
Vector3::from_iterator( Vector3::from_iterator(
@ -136,7 +137,7 @@ fn signed_edge_functions<T: RealField>(vertices: &Vec<Vector3<T>>) -> Vector3<T>
) )
} }
fn barycentric_coordinates_from_signed_edge_functions<T: RealField>(e: Vector3<T>) -> Vector3<T> { fn barycentric_coordinates_from_signed_edge_functions<T: Real>(e: Vector3<T>) -> Vector3<T> {
e * (T::one() / e.iter().fold(T::zero(), |a, &b| a + b)) e * (T::one() / e.iter().fold(T::zero(), |a, &b| a + b))
} }

6
src/realtype.rs Normal file
View File

@ -0,0 +1,6 @@
use nalgebra::RealField;
pub trait Real: RealField {}
impl Real for f32 {}
impl Real for f64 {}

View File

@ -1,13 +1,13 @@
use super::raycasting::{IntersectionInfo, Ray}; use super::raycasting::{IntersectionInfo, Ray};
use super::scene::Scene; use super::scene::Scene;
use nalgebra::RealField; use crate::Real;
pub struct Sampler<'a, T: RealField> { pub struct Sampler<'a, T: Real> {
pub scene: &'a Scene<T>, pub scene: &'a Scene<T>,
} }
impl<'a, T: RealField> Sampler<'a, T> { impl<'a, T: Real> Sampler<'a, T> {
pub fn sample(&self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { pub fn sample(&self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
self.scene self.scene
.objects .objects

View File

@ -1,10 +1,11 @@
use nalgebra::{Point3, RealField}; use nalgebra::{Point3};
use crate::raycasting::Primitive; use crate::raycasting::Primitive;
use crate::Real;
use std::sync::Arc; use std::sync::Arc;
pub struct Scene<T: RealField> { pub struct Scene<T: Real> {
pub camera_location: Point3<T>, pub camera_location: Point3<T>,
pub objects: Vec<Arc<dyn Primitive<T>>>, pub objects: Vec<Arc<dyn Primitive<T>>>,
} }

View File

@ -1,6 +1,8 @@
use nalgebra::{Matrix3, RealField, Vector3}; use nalgebra::{Matrix3, Vector3};
pub fn try_change_of_basis_matrix<T: RealField>( use crate::Real;
pub fn try_change_of_basis_matrix<T: Real>(
x: &Vector3<T>, x: &Vector3<T>,
y: &Vector3<T>, y: &Vector3<T>,
z: &Vector3<T>, z: &Vector3<T>,

View File

@ -1,15 +1,16 @@
use nalgebra::{Point3, RealField}; use nalgebra::Point3;
use crate::util::Interval; use crate::util::Interval;
use crate::Real;
use itertools::izip; use itertools::izip;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct BoundingBox<T: RealField> { pub struct BoundingBox<T: Real> {
pub bounds: [Interval<T>; 3], pub bounds: [Interval<T>; 3],
} }
impl<T: RealField> BoundingBox<T> { impl<T: Real> BoundingBox<T> {
pub fn from_corners(a: Point3<T>, b: Point3<T>) -> Self { pub fn from_corners(a: Point3<T>, b: Point3<T>) -> Self {
let mut result = BoundingBox { let mut result = BoundingBox {
bounds: [Interval::infinite(); 3], bounds: [Interval::infinite(); 3],

View File

@ -1,12 +1,14 @@
use nalgebra::{convert, RealField}; use nalgebra::convert;
use crate::Real;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Interval<T: RealField> { pub struct Interval<T: Real> {
min: T, min: T,
max: T, max: T,
} }
impl<T: RealField> Interval<T> { impl<T: Real> Interval<T> {
pub fn new(a: T, b: T) -> Self { pub fn new(a: T, b: T) -> Self {
if a > b { if a > b {
Interval { min: b, max: a } Interval { min: b, max: a }

View File

@ -1,17 +1,19 @@
use super::axis_aligned_bounding_box::BoundingBox; use super::axis_aligned_bounding_box::BoundingBox;
use super::Interval; use super::Interval;
use nalgebra::{clamp, Point3, RealField}; use crate::Real;
use nalgebra::{clamp, Point3};
use itertools::izip; use itertools::izip;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct RealFieldNormalizer<T: RealField> { pub struct RealNormalizer<T: Real> {
min: T, min: T,
range: T, range: T,
} }
impl<T: RealField> RealFieldNormalizer<T> { impl<T: Real> RealNormalizer<T> {
pub fn new(interval: Interval<T>) -> Self { pub fn new(interval: Interval<T>) -> Self {
let min = interval.get_min(); let min = interval.get_min();
let range = interval.get_max() - min; let range = interval.get_max() - min;
@ -28,21 +30,21 @@ impl<T: RealField> RealFieldNormalizer<T> {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Point3Normalizer<T: RealField> { pub struct Point3Normalizer<T: Real> {
dimension_normalizers: [RealFieldNormalizer<T>; 3], dimension_normalizers: [RealNormalizer<T>; 3],
} }
impl<T: RealField> Point3Normalizer<T> { impl<T: Real> Point3Normalizer<T> {
pub fn new(bounds: BoundingBox<T>) -> Self { pub fn new(bounds: BoundingBox<T>) -> Self {
let mut normalizer = Point3Normalizer { let mut normalizer = Point3Normalizer {
dimension_normalizers: [RealFieldNormalizer::new(Interval::empty()); 3], dimension_normalizers: [RealNormalizer::new(Interval::empty()); 3],
}; };
for (normalizer, &bounds) in normalizer for (normalizer, &bounds) in normalizer
.dimension_normalizers .dimension_normalizers
.iter_mut() .iter_mut()
.zip(bounds.bounds.iter()) .zip(bounds.bounds.iter())
{ {
*normalizer = RealFieldNormalizer::new(bounds); *normalizer = RealNormalizer::new(bounds);
} }
normalizer normalizer
} }
@ -80,43 +82,43 @@ mod test {
#[quickcheck] #[quickcheck]
fn normalize_zero_to_one_yields_input(value: f64) -> bool { fn normalize_zero_to_one_yields_input(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(0.0, 1.0)); let target = RealNormalizer::new(Interval::new(0.0, 1.0));
target.normalize(value) == value target.normalize(value) == value
} }
#[quickcheck] #[quickcheck]
fn normalize_two_to_three_yields_input_minus_two(value: f64) -> bool { fn normalize_two_to_three_yields_input_minus_two(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 3.0)); let target = RealNormalizer::new(Interval::new(2.0, 3.0));
target.normalize(value) == value - 2.0 target.normalize(value) == value - 2.0
} }
#[quickcheck] #[quickcheck]
fn normalize_negative_three_to_negative_two_yields_input_plus_three(value: f64) -> bool { fn normalize_negative_three_to_negative_two_yields_input_plus_three(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(-3.0, -2.0)); let target = RealNormalizer::new(Interval::new(-3.0, -2.0));
target.normalize(value) == value + 3.0 target.normalize(value) == value + 3.0
} }
#[quickcheck] #[quickcheck]
fn normalize_zero_to_two_yields_input_divided_by_two(value: f64) -> bool { fn normalize_zero_to_two_yields_input_divided_by_two(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(0.0, 2.0)); let target = RealNormalizer::new(Interval::new(0.0, 2.0));
target.normalize(value) == value / 2.0 target.normalize(value) == value / 2.0
} }
#[test] #[test]
fn normalize_two_to_four_yields_zero_when_input_is_two() { fn normalize_two_to_four_yields_zero_when_input_is_two() {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0)); let target = RealNormalizer::new(Interval::new(2.0, 4.0));
assert!(target.normalize(2.0) == 0.0) assert!(target.normalize(2.0) == 0.0)
} }
#[test] #[test]
fn normalize_two_to_four_yields_one_when_input_is_four() { fn normalize_two_to_four_yields_one_when_input_is_four() {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0)); let target = RealNormalizer::new(Interval::new(2.0, 4.0));
assert!(target.normalize(4.0) == 1.0) assert!(target.normalize(4.0) == 1.0)
} }
#[quickcheck] #[quickcheck]
fn normalize_two_to_four_yields_input_divided_by_two_minus_one(value: f64) -> bool { fn normalize_two_to_four_yields_input_divided_by_two_minus_one(value: f64) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0)); let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize(value) == (value - 2.0) / 2.0 target.normalize(value) == (value - 2.0) / 2.0
} }
@ -124,7 +126,7 @@ mod test {
fn normalize_and_clamp_two_to_four_yields_zero_when_input_less_than_or_equal_two( fn normalize_and_clamp_two_to_four_yields_zero_when_input_less_than_or_equal_two(
value: f64, value: f64,
) -> bool { ) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0)); let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize_and_clamp(value) == 0.0 || value > 2.0 target.normalize_and_clamp(value) == 0.0 || value > 2.0
} }
@ -132,7 +134,7 @@ mod test {
fn normalize_and_clamp_two_to_four_yields_one_when_input_greater_than_or_equal_four( fn normalize_and_clamp_two_to_four_yields_one_when_input_greater_than_or_equal_four(
value: f64, value: f64,
) -> bool { ) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0)); let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize_and_clamp(value) == 1.0 || value < 4.0 target.normalize_and_clamp(value) == 1.0 || value < 4.0
} }
@ -140,7 +142,7 @@ mod test {
fn normalize_and_clamp_two_to_four_yields_same_value_as_normalize_when_in_range( fn normalize_and_clamp_two_to_four_yields_same_value_as_normalize_when_in_range(
value: f64, value: f64,
) -> bool { ) -> bool {
let target = RealFieldNormalizer::new(Interval::new(2.0, 4.0)); let target = RealNormalizer::new(Interval::new(2.0, 4.0));
target.normalize_and_clamp(value) == target.normalize(value) || value < 2.0 || value > 4.0 target.normalize_and_clamp(value) == target.normalize(value) || value < 2.0 || value > 4.0
} }
@ -150,9 +152,9 @@ mod test {
b: Point3<f64>, b: Point3<f64>,
c: Point3<f64>, c: Point3<f64>,
) -> bool { ) -> bool {
let x_normalizer = RealFieldNormalizer::new(Interval::new(a.x.min(b.x), a.x.max(b.x))); let x_normalizer = RealNormalizer::new(Interval::new(a.x.min(b.x), a.x.max(b.x)));
let y_normalizer = RealFieldNormalizer::new(Interval::new(a.y.min(b.y), a.y.max(b.y))); let y_normalizer = RealNormalizer::new(Interval::new(a.y.min(b.y), a.y.max(b.y)));
let z_normalizer = dbg!(RealFieldNormalizer::new(Interval::new( let z_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.z.min(b.z), a.z.min(b.z),
a.z.max(b.z) a.z.max(b.z)
))); )));
@ -169,15 +171,15 @@ mod test {
b: Point3<f64>, b: Point3<f64>,
c: Point3<f64>, c: Point3<f64>,
) -> bool { ) -> bool {
let x_normalizer = dbg!(RealFieldNormalizer::new(Interval::new( let x_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.x.min(b.x), a.x.min(b.x),
a.x.max(b.x) a.x.max(b.x)
))); )));
let y_normalizer = dbg!(RealFieldNormalizer::new(Interval::new( let y_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.y.min(b.y), a.y.min(b.y),
a.y.max(b.y) a.y.max(b.y)
))); )));
let z_normalizer = dbg!(RealFieldNormalizer::new(Interval::new( let z_normalizer = dbg!(RealNormalizer::new(Interval::new(
a.z.min(b.z), a.z.min(b.z),
a.z.max(b.z) a.z.max(b.z)
))); )));