Change a bunch of generics that used with RealType to just use f64

This commit is contained in:
Matthew Gordon 2020-08-18 23:12:28 -04:00
parent a98c0c4bca
commit 99cf127c9f
28 changed files with 503 additions and 587 deletions

View File

@ -8,25 +8,23 @@ use super::sampler::Sampler;
use super::scene::Scene; use super::scene::Scene;
use super::util::Tile; use super::util::Tile;
use crate::Real; struct ImageSampler {
struct ImageSampler<T: Real> {
image_height_pixels: usize, image_height_pixels: usize,
image_width_pixels: usize, image_width_pixels: usize,
film_width: T, film_width: f64,
film_height: T, film_height: f64,
camera_location: Point3<T>, camera_location: Point3<f64>,
film_distance: T, film_distance: f64,
} }
impl<T: Real> ImageSampler<T> { impl ImageSampler {
pub fn new(width: usize, height: usize, camera_location: Point3<T>) -> ImageSampler<T> { pub fn new(width: usize, height: usize, camera_location: Point3<f64>) -> ImageSampler {
let (film_width, film_height) = { let (film_width, film_height) = {
let width: T = convert(width as f64); let width = width as f64;
let height: T = convert(height as f64); let height = height as f64;
let film_size: T = convert(1.0); let film_size = 1.0;
if width > height { if width > height {
(width / height, film_size) (width / height, film_size)
} else { } else {
@ -43,22 +41,21 @@ impl<T: Real> ImageSampler<T> {
} }
} }
fn scale(i: usize, n: usize, l: T) -> T { fn scale(i: usize, n: usize, l: f64) -> f64 {
let one: T = convert(1.0); let n = n as f64;
let n: T = convert(n as f64); let i = i as f64;
let i: T = convert(i as f64); let pixel_size = l * (1.0 / n);
let pixel_size: T = l * (one / n); (i + 0.5) * pixel_size
(i + convert(0.5)) * pixel_size
} }
fn ray_for_pixel(&self, row: usize, column: usize) -> Ray<T> { fn ray_for_pixel(&self, row: usize, column: usize) -> Ray {
Ray::new( Ray::new(
self.camera_location, self.camera_location,
Vector3::new( Vector3::new(
Self::scale(column, self.image_width_pixels, self.film_width) Self::scale(column, self.image_width_pixels, self.film_width)
- self.film_width * convert(0.5), - self.film_width * 0.5,
Self::scale(row, self.image_height_pixels, self.film_height) Self::scale(row, self.image_height_pixels, self.film_height)
- self.film_height * convert(0.5), - self.film_height * 0.5,
self.film_distance, self.film_distance,
), ),
) )
@ -82,7 +79,7 @@ const RECURSION_LIMIT: u16 = 32;
/// # use vanrijn::scene::Scene; /// # use vanrijn::scene::Scene;
/// # use vanrijn::util::TileIterator; /// # use vanrijn::util::TileIterator;
/// # use vanrijn::partial_render_scene; /// # use vanrijn::partial_render_scene;
/// # let scene = Scene { camera_location: Point3::new(0.0f32, 0.0, 0.0), objects: vec![] }; /// # let scene = Scene { camera_location: Point3::new(0.0, 0.0, 0.0), objects: vec![] };
/// let image_width = 640; /// let image_width = 640;
/// let image_height = 480; /// let image_height = 480;
/// let time_size = 32; /// let time_size = 32;
@ -91,19 +88,14 @@ const RECURSION_LIMIT: u16 = 32;
/// // display and/or save tile_image /// // display and/or save tile_image
/// } /// }
/// ``` /// ```
pub fn partial_render_scene<T: Real>( pub fn partial_render_scene(scene: &Scene, tile: Tile, height: usize, width: usize) -> ImageRgbF {
scene: &Scene<T>,
tile: Tile,
height: usize,
width: usize,
) -> ImageRgbF<T> {
let mut output_image_tile = ImageRgbF::new(tile.width(), tile.height()); let mut output_image_tile = ImageRgbF::new(tile.width(), tile.height());
let image_sampler = ImageSampler::new(width, height, scene.camera_location); let image_sampler = ImageSampler::new(width, height, scene.camera_location);
let ambient_intensity: T = convert(0.0); let ambient_intensity = 0.0;
let directional_intensity1: T = convert(7.0); let directional_intensity1 = 7.0;
let directional_intensity2: T = convert(3.0); let directional_intensity2 = 3.0;
let directional_intensity3: T = convert(2.0); let directional_intensity3 = 2.0;
let integrator = WhittedIntegrator::<T> { let integrator = WhittedIntegrator {
ambient_light: ColourRgbF::from_named(NamedColour::White) * ambient_intensity, ambient_light: ColourRgbF::from_named(NamedColour::White) * ambient_intensity,
lights: vec![ lights: vec![
DirectionalLight { DirectionalLight {
@ -167,7 +159,7 @@ mod tests {
let film_plane = Plane::new( let film_plane = Plane::new(
Vector3::new(0.0, 0.0, 1.0), Vector3::new(0.0, 0.0, 1.0),
target.film_distance, target.film_distance,
Arc::new(LambertianMaterial::<f64>::new_dummy()), Arc::new(LambertianMaterial::new_dummy()),
); );
let point_on_film_plane = match film_plane.intersect(&ray) { let point_on_film_plane = match film_plane.intersect(&ray) {
Some(IntersectionInfo { Some(IntersectionInfo {

View File

@ -1,61 +1,56 @@
use nalgebra::{convert, Vector3}; use nalgebra::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: Real> { pub struct ColourRgbF {
values: Vector3<T>, values: Vector3<f64>,
} }
impl<T: Real> ColourRgbF<T> { impl ColourRgbF {
pub fn new(red: T, green: T, blue: T) -> ColourRgbF<T> { pub fn new(red: f64, green: f64, blue: f64) -> ColourRgbF {
ColourRgbF { ColourRgbF {
values: Vector3::new(red, green, blue), values: Vector3::new(red, green, blue),
} }
} }
pub fn from_named(name: NamedColour) -> ColourRgbF<T> { pub fn from_named(name: NamedColour) -> ColourRgbF {
let zero: T = convert(0.0);
let half: T = convert(0.5);
let one: T = convert(1.0);
match name { match name {
NamedColour::Black => ColourRgbF::new(zero, zero, zero), NamedColour::Black => ColourRgbF::new(0.0, 0.0, 0.0),
NamedColour::White => ColourRgbF::new(one, one, one), NamedColour::White => ColourRgbF::new(1.0, 1.0, 1.0),
NamedColour::Red => ColourRgbF::new(one, zero, zero), NamedColour::Red => ColourRgbF::new(1.0, 0.0, 0.0),
NamedColour::Lime => ColourRgbF::new(zero, one, zero), NamedColour::Lime => ColourRgbF::new(0.0, 1.0, 0.0),
NamedColour::Blue => ColourRgbF::new(zero, zero, one), NamedColour::Blue => ColourRgbF::new(0.0, 0.0, 1.0),
NamedColour::Yellow => ColourRgbF::new(one, one, zero), NamedColour::Yellow => ColourRgbF::new(1.0, 1.0, 0.0),
NamedColour::Cyan => ColourRgbF::new(zero, one, one), NamedColour::Cyan => ColourRgbF::new(0.0, 1.0, 1.0),
NamedColour::Magenta => ColourRgbF::new(one, zero, one), NamedColour::Magenta => ColourRgbF::new(1.0, 0.0, 1.0),
NamedColour::Gray => ColourRgbF::new(half, half, half), NamedColour::Gray => ColourRgbF::new(0.5, 0.5, 0.5),
NamedColour::Maroon => ColourRgbF::new(half, zero, zero), NamedColour::Maroon => ColourRgbF::new(0.5, 0.0, 0.0),
NamedColour::Olive => ColourRgbF::new(half, half, zero), NamedColour::Olive => ColourRgbF::new(0.5, 0.5, 0.0),
NamedColour::Green => ColourRgbF::new(zero, half, zero), NamedColour::Green => ColourRgbF::new(0.0, 0.5, 0.0),
NamedColour::Purple => ColourRgbF::new(half, zero, half), NamedColour::Purple => ColourRgbF::new(0.5, 0.0, 0.5),
NamedColour::Teal => ColourRgbF::new(zero, half, half), NamedColour::Teal => ColourRgbF::new(0.0, 0.5, 0.5),
NamedColour::Navy => ColourRgbF::new(zero, zero, half), NamedColour::Navy => ColourRgbF::new(0.0, 0.0, 0.5),
} }
} }
pub fn from_vector3(v: &Vector3<T>) -> ColourRgbF<T> { pub fn from_vector3(v: &Vector3<f64>) -> ColourRgbF {
ColourRgbF { values: *v } ColourRgbF { values: *v }
} }
pub fn red(&self) -> T { pub fn red(&self) -> f64 {
self.values[0] self.values[0]
} }
pub fn green(&self) -> T { pub fn green(&self) -> f64 {
self.values[1] self.values[1]
} }
pub fn blue(&self) -> T { pub fn blue(&self) -> f64 {
self.values[2] self.values[2]
} }
pub fn as_vector3(&self) -> &Vector3<T> { pub fn as_vector3(&self) -> &Vector3<f64> {
&self.values &self.values
} }
} }
@ -82,27 +77,27 @@ pub enum NamedColour {
Navy, Navy,
} }
impl<T: Real> Add<ColourRgbF<T>> for ColourRgbF<T> { impl Add<ColourRgbF> for ColourRgbF {
type Output = ColourRgbF<T>; type Output = ColourRgbF;
fn add(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> { fn add(self, rhs: ColourRgbF) -> ColourRgbF {
ColourRgbF { ColourRgbF {
values: self.values + rhs.values, values: self.values + rhs.values,
} }
} }
} }
impl<T: Real> Mul<T> for ColourRgbF<T> { impl Mul<f64> for ColourRgbF {
type Output = ColourRgbF<T>; type Output = ColourRgbF;
fn mul(self, rhs: T) -> ColourRgbF<T> { fn mul(self, rhs: f64) -> ColourRgbF {
ColourRgbF { ColourRgbF {
values: self.values * rhs, values: self.values * rhs,
} }
} }
} }
impl<T: Real> Mul<ColourRgbF<T>> for ColourRgbF<T> { impl Mul<ColourRgbF> for ColourRgbF {
type Output = ColourRgbF<T>; type Output = ColourRgbF;
fn mul(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> { fn mul(self, rhs: ColourRgbF) -> ColourRgbF {
ColourRgbF { ColourRgbF {
values: self.values.component_mul(&rhs.values), values: self.values.component_mul(&rhs.values),
} }
@ -117,9 +112,9 @@ 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 + Real> Arbitrary for ColourRgbF<T> { impl Arbitrary for ColourRgbF {
fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF<T> { fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF {
let values = <Vector3<T> as Arbitrary>::arbitrary(g); let values = <Vector3<f64> as Arbitrary>::arbitrary(g);
ColourRgbF { values } ColourRgbF { values }
} }
} }
@ -140,7 +135,7 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn any_colour_multiplied_by_zero_is_black(colour: ColourRgbF<f64>) { fn any_colour_multiplied_by_zero_is_black(colour: ColourRgbF) {
let target = colour * 0.0; let target = colour * 0.0;
assert!(target.red() == 0.0); assert!(target.red() == 0.0);
assert!(target.green() == 0.0); assert!(target.green() == 0.0);
@ -148,17 +143,14 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn red_channel_multiplied_by_scalar_yields_correct_result( fn red_channel_multiplied_by_scalar_yields_correct_result(colour: ColourRgbF, scalar: f64) {
colour: ColourRgbF<f64>,
scalar: f64,
) {
let target = colour * scalar; let target = colour * scalar;
assert!(target.red() == colour.red() * scalar); assert!(target.red() == colour.red() * scalar);
} }
#[quickcheck] #[quickcheck]
fn green_channel_multiplied_by_scalar_yields_correct_result( fn green_channel_multiplied_by_scalar_yields_correct_result(
colour: ColourRgbF<f64>, colour: ColourRgbF,
scalar: f64, scalar: f64,
) { ) {
let target = colour * scalar; let target = colour * scalar;
@ -167,7 +159,7 @@ mod tests {
#[quickcheck] #[quickcheck]
fn blue_channel_multiplied_by_scalar_yields_correct_result( fn blue_channel_multiplied_by_scalar_yields_correct_result(
colour: ColourRgbF<f64>, colour: ColourRgbF,
scalar: f64, scalar: f64,
) { ) {
let target = colour * scalar; let target = colour * scalar;
@ -175,10 +167,7 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn adding_colourrgbf_adds_individual_channels( fn adding_colourrgbf_adds_individual_channels(colour1: ColourRgbF, colour2: ColourRgbF) {
colour1: ColourRgbF<f64>,
colour2: ColourRgbF<f64>,
) {
let target = colour1 + colour2; let target = colour1 + colour2;
assert!(target.red() == colour1.red() + colour2.red()); assert!(target.red() == colour1.red() + colour2.red());
assert!(target.green() == colour1.green() + colour2.green()); assert!(target.green() == colour1.green() + colour2.green());
@ -187,8 +176,8 @@ mod tests {
#[quickcheck] #[quickcheck]
fn multiplying_colourrgbf_adds_individual_channels( fn multiplying_colourrgbf_adds_individual_channels(
colour1: ColourRgbF<f64>, colour1: ColourRgbF,
colour2: ColourRgbF<f64>, colour2: ColourRgbF,
) { ) {
let target = colour1 * colour2; let target = colour1 * colour2;
assert!(target.red() == colour1.red() * colour2.red()); assert!(target.red() == colour1.red() * colour2.red());

View File

@ -7,8 +7,6 @@ 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: usize, width: usize,
@ -95,14 +93,14 @@ impl ImageRgbU8 {
} }
} }
pub struct ImageRgbF<T: Real> { pub struct ImageRgbF {
pixel_data: Vec<T>, pixel_data: Vec<f64>,
width: usize, width: usize,
height: usize, height: usize,
} }
impl<T: Real> ImageRgbF<T> { impl ImageRgbF {
pub fn new(width: usize, height: usize) -> ImageRgbF<T> { pub fn new(width: usize, height: usize) -> ImageRgbF {
ImageRgbF { ImageRgbF {
width, width,
height, height,
@ -110,26 +108,26 @@ impl<T: Real> ImageRgbF<T> {
} }
} }
pub fn clear(&mut self) -> &mut ImageRgbF<T> { pub fn clear(&mut self) -> &mut ImageRgbF {
for elem in self.pixel_data.iter_mut() { for elem in self.pixel_data.iter_mut() {
*elem = T::zero(); *elem = 0.0;
} }
self self
} }
pub fn get_colour(&self, row: usize, column: usize) -> ColourRgbF<T> { pub fn get_colour(&self, row: usize, column: usize) -> ColourRgbF {
assert!(row < self.height && column < self.width); assert!(row < self.height && column < self.width);
let index = self.calculate_index(row, column); let index = self.calculate_index(row, column);
ColourRgbF::from_vector3(&Vector3::from_row_slice(&self.pixel_data[index..index + 3])) ColourRgbF::from_vector3(&Vector3::from_row_slice(&self.pixel_data[index..index + 3]))
} }
pub fn set_colour(&mut self, row: usize, column: usize, colour: ColourRgbF<T>) { pub fn set_colour(&mut self, row: usize, column: usize, colour: ColourRgbF) {
assert!(row < self.height && column < self.width); assert!(row < self.height && column < self.width);
let index = self.calculate_index(row, column); let index = self.calculate_index(row, column);
self.pixel_data[index..index + 3].copy_from_slice(&colour.as_vector3().as_slice()); self.pixel_data[index..index + 3].copy_from_slice(&colour.as_vector3().as_slice());
} }
pub fn get_pixel_data(&self) -> &Vec<T> { pub fn get_pixel_data(&self) -> &Vec<f64> {
&self.pixel_data &self.pixel_data
} }
@ -176,21 +174,21 @@ impl NormalizedAsByte for f64 {
} }
} }
pub trait ToneMapper<T: Real> { pub trait ToneMapper {
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8); fn apply_tone_mapping(&self, image_in: &ImageRgbF, image_out: &mut ImageRgbU8);
} }
#[derive(Default)] #[derive(Default)]
pub struct ClampingToneMapper {} pub struct ClampingToneMapper {}
impl ClampingToneMapper { impl ClampingToneMapper {
fn clamp<T: Real + NormalizedAsByte>(v: &T) -> u8 { fn clamp(v: &f64) -> u8 {
clamp(v, &T::zero(), &T::one()).normalized_to_byte() clamp(v, &0.0, &1.0).normalized_to_byte()
} }
} }
impl<T: Real + NormalizedAsByte> ToneMapper<T> for ClampingToneMapper { impl ToneMapper for ClampingToneMapper {
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8) { fn apply_tone_mapping(&self, image_in: &ImageRgbF, 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());
for column in 0..image_in.get_width() { for column in 0..image_in.get_width() {

View File

@ -5,36 +5,34 @@ 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;
use crate::Real; pub trait Integrator {
pub trait Integrator<T: Real> {
fn integrate( fn integrate(
&self, &self,
sampler: &Sampler<T>, sampler: &Sampler,
info: &IntersectionInfo<T>, info: &IntersectionInfo,
recursion_limit: u16, recursion_limit: u16,
) -> ColourRgbF<T>; ) -> ColourRgbF;
} }
pub struct DirectionalLight<T: Real> { pub struct DirectionalLight {
pub direction: Vector3<T>, pub direction: Vector3<f64>,
pub colour: ColourRgbF<T>, pub colour: ColourRgbF,
} }
pub struct WhittedIntegrator<T: Real> { pub struct WhittedIntegrator {
pub ambient_light: ColourRgbF<T>, pub ambient_light: ColourRgbF,
pub lights: Vec<DirectionalLight<T>>, pub lights: Vec<DirectionalLight>,
} }
// 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: Real> Integrator<T> for WhittedIntegrator<T> { impl Integrator for WhittedIntegrator {
fn integrate( fn integrate(
&self, &self,
sampler: &Sampler<T>, sampler: &Sampler,
info: &IntersectionInfo<T>, info: &IntersectionInfo,
recursion_limit: u16, recursion_limit: u16,
) -> ColourRgbF<T> { ) -> ColourRgbF {
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)
.expect("Normal, tangent and cotangent don't for a valid basis."); .expect("Normal, tangent and cotangent don't for a valid basis.");
@ -79,10 +77,10 @@ impl<T: Real> Integrator<T> for WhittedIntegrator<T> {
), ),
) * world_space_direction.dot(&info.normal).abs() ) * world_space_direction.dot(&info.normal).abs()
} else { } else {
ColourRgbF::new(T::zero(), T::zero(), T::zero()) ColourRgbF::new(0.0, 0.0, 0.0)
} }
} }
None => ColourRgbF::new(T::zero(), T::zero(), T::zero()), None => ColourRgbF::new(0.0, 0.0, 0.0),
} }
}), }),
) )

View File

@ -15,5 +15,3 @@ pub mod scene;
pub mod util; pub mod util;
pub use camera::partial_render_scene; pub use camera::partial_render_scene;
use realtype::Real;

View File

@ -138,7 +138,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
}), }),
)?; )?;
println!("Building BVH..."); println!("Building BVH...");
let model_bvh: Box<dyn Aggregate<_>> = let model_bvh: Box<dyn Aggregate> =
Box::new(BoundingVolumeHierarchy::build(model_object.as_mut_slice())); Box::new(BoundingVolumeHierarchy::build(model_object.as_mut_slice()));
println!("Constructing Scene..."); println!("Constructing Scene...");
@ -153,7 +153,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
colour: ColourRgbF::new(0.55, 0.27, 0.04), colour: ColourRgbF::new(0.55, 0.27, 0.04),
diffuse_strength: 0.1, diffuse_strength: 0.1,
}), }),
)) as Box<dyn Primitive<f64>>, )) as Box<dyn Primitive>,
Box::new(Sphere::new( Box::new(Sphere::new(
Point3::new(-6.25, -0.5, 1.0), Point3::new(-6.25, -0.5, 1.0),
1.0, 1.0,
@ -181,7 +181,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
specular_strength: 1.0, specular_strength: 1.0,
}), }),
)), )),
]) as Box<dyn Aggregate<f64>>, ]) as Box<dyn Aggregate>,
model_bvh, model_bvh,
], ],
}; };

View File

@ -1,32 +1,31 @@
use nalgebra::Vector3; use nalgebra::Vector3;
use crate::colour::ColourRgbF; use crate::colour::ColourRgbF;
use crate::Real;
use super::{Bsdf, Material}; use super::{Bsdf, Material};
use std::fmt::Debug; use std::fmt::Debug;
#[derive(Debug)] #[derive(Debug)]
pub struct LambertianMaterial<T: Real> { pub struct LambertianMaterial {
pub colour: ColourRgbF<T>, pub colour: ColourRgbF,
pub diffuse_strength: T, pub diffuse_strength: f64,
} }
impl<T: Real> LambertianMaterial<T> { impl LambertianMaterial {
pub fn new_dummy() -> LambertianMaterial<T> { pub fn new_dummy() -> LambertianMaterial {
LambertianMaterial { LambertianMaterial {
colour: ColourRgbF::new(T::one(), T::one(), T::one()), colour: ColourRgbF::new(1.0, 1.0, 1.0),
diffuse_strength: T::one(), diffuse_strength: 1.0,
} }
} }
} }
impl<T: Real> Material<T> for LambertianMaterial<T> { impl Material for LambertianMaterial {
fn bsdf(&self) -> Bsdf<T> { fn bsdf(&self) -> Bsdf {
let colour = self.colour * self.diffuse_strength; let colour = self.colour * self.diffuse_strength;
Box::new( Box::new(
move |_w_o: Vector3<T>, _w_i: Vector3<T>, colour_in: ColourRgbF<T>| colour * colour_in, move |_w_o: Vector3<f64>, _w_i: Vector3<f64>, colour_in: ColourRgbF| colour * colour_in,
) )
} }
} }

View File

@ -1,11 +1,10 @@
use nalgebra::Vector3; use nalgebra::Vector3;
use super::colour::ColourRgbF; use super::colour::ColourRgbF;
use crate::Real;
use std::fmt::Debug; use std::fmt::Debug;
type Bsdf<T> = Box<dyn Fn(Vector3<T>, Vector3<T>, ColourRgbF<T>) -> ColourRgbF<T>>; type Bsdf = Box<dyn Fn(Vector3<f64>, Vector3<f64>, ColourRgbF) -> ColourRgbF>;
pub mod lambertian_material; pub mod lambertian_material;
pub use lambertian_material::LambertianMaterial; pub use lambertian_material::LambertianMaterial;
@ -19,10 +18,10 @@ pub use reflective_material::ReflectiveMaterial;
pub mod rgb_sampled_bsdf_material; pub mod rgb_sampled_bsdf_material;
pub use rgb_sampled_bsdf_material::RgbSampledBsdfMaterial; pub use rgb_sampled_bsdf_material::RgbSampledBsdfMaterial;
pub trait Material<T: Real>: Debug + Sync + Send { pub trait Material: Debug + Sync + Send {
fn bsdf(&self) -> Bsdf<T>; fn bsdf(&self) -> Bsdf;
fn sample(&self, _w_o: &Vector3<T>) -> Vec<Vector3<T>> { fn sample(&self, _w_o: &Vector3<f64>) -> Vec<Vector3<f64>> {
vec![] vec![]
} }
} }

View File

@ -1,28 +1,27 @@
use nalgebra::Vector3; use nalgebra::Vector3;
use crate::colour::{ColourRgbF, NamedColour}; use crate::colour::{ColourRgbF, NamedColour};
use crate::Real;
use std::fmt::Debug; use std::fmt::Debug;
use super::{Bsdf, Material}; use super::{Bsdf, Material};
#[derive(Debug)] #[derive(Debug)]
pub struct PhongMaterial<T: Real> { pub struct PhongMaterial {
pub colour: ColourRgbF<T>, pub colour: ColourRgbF,
pub diffuse_strength: T, pub diffuse_strength: f64,
pub specular_strength: T, pub specular_strength: f64,
pub smoothness: T, pub smoothness: f64,
} }
impl<T: Real> Material<T> for PhongMaterial<T> { impl Material for PhongMaterial {
fn bsdf(&self) -> Bsdf<T> { fn bsdf(&self) -> Bsdf {
let smoothness = self.smoothness; let smoothness = self.smoothness;
let specular_strength = self.specular_strength; let specular_strength = self.specular_strength;
let colour = self.colour * self.diffuse_strength; let colour = self.colour * self.diffuse_strength;
Box::new( Box::new(
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| { move |w_o: Vector3<f64>, w_i: Vector3<f64>, colour_in: ColourRgbF| {
if w_i.z < T::zero() || w_o.z < T::zero() { if w_i.z < 0.0 || w_o.z < 0.0 {
ColourRgbF::from_vector3(&Vector3::zeros()) ColourRgbF::from_vector3(&Vector3::zeros())
} else { } else {
let reflection_vector = Vector3::new(-w_i.x, -w_i.y, w_i.z); let reflection_vector = Vector3::new(-w_i.x, -w_i.y, w_i.z);

View File

@ -1,47 +1,44 @@
use nalgebra::{clamp, convert, Vector3}; use nalgebra::{clamp, Vector3};
use crate::colour::ColourRgbF; use crate::colour::ColourRgbF;
use crate::Real;
use std::fmt::Debug; use std::fmt::Debug;
use super::{Bsdf, Material}; use super::{Bsdf, Material};
#[derive(Debug)] #[derive(Debug)]
pub struct ReflectiveMaterial<T: Real> { pub struct ReflectiveMaterial {
pub colour: ColourRgbF<T>, pub colour: ColourRgbF,
pub diffuse_strength: T, pub diffuse_strength: f64,
pub reflection_strength: T, pub reflection_strength: f64,
} }
impl<T: Real> Material<T> for ReflectiveMaterial<T> { impl Material for ReflectiveMaterial {
fn bsdf(&self) -> Bsdf<T> { fn bsdf(&self) -> Bsdf {
let diffuse_colour_factor = self.colour * self.diffuse_strength; let diffuse_colour_factor = self.colour * self.diffuse_strength;
let reflection_strength = self.reflection_strength; let reflection_strength = self.reflection_strength;
Box::new( Box::new(
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| { move |w_o: Vector3<f64>, w_i: Vector3<f64>, colour_in: ColourRgbF| {
if w_i.z <= T::zero() || w_o.z <= T::zero() { if w_i.z <= 0.0 || w_o.z <= 0.0 {
ColourRgbF::new(T::zero(), T::zero(), T::zero()) ColourRgbF::new(0.0, 0.0, 0.0)
} else { } else {
let reflection_vector = Vector3::new(-w_o.x, -w_o.y, w_o.z); let reflection_vector = Vector3::new(-w_o.x, -w_o.y, w_o.z);
let reflection_colour = colour_in * reflection_strength; let reflection_colour = colour_in * reflection_strength;
let diffuse_colour = diffuse_colour_factor * colour_in; let diffuse_colour = diffuse_colour_factor * colour_in;
let sigma: T = convert(0.05); let sigma = 0.05;
let two: T = convert(2.0); let two = 2.0;
// These are normalized vectors, but sometimes rounding errors cause the // These are normalized vectors, but sometimes rounding errors cause the
// dot product to be slightly above 1 or below 0. The call to clamp // dot product to be slightly above 1 or below 0. The call to clamp
// ensures the values stay within the domain of acos, // ensures the values stay within the domain of acos,
let theta = clamp(w_i.dot(&reflection_vector), T::zero(), T::one()) let theta = clamp(w_i.dot(&reflection_vector), 0.0, 1.0).abs().acos();
.abs()
.acos();
let reflection_factor = (-(theta.powf(two)) / (two * sigma * sigma)).exp(); let reflection_factor = (-(theta.powf(two)) / (two * sigma * sigma)).exp();
reflection_colour * reflection_factor reflection_colour * reflection_factor
+ diffuse_colour * (T::one() - reflection_factor) + diffuse_colour * (1.0 - reflection_factor)
} }
}, },
) )
} }
fn sample(&self, w_o: &Vector3<T>) -> Vec<Vector3<T>> { fn sample(&self, w_o: &Vector3<f64>) -> Vec<Vector3<f64>> {
vec![Vector3::new(-w_o.x, -w_o.y, w_o.z)] vec![Vector3::new(-w_o.x, -w_o.y, w_o.z)]
} }
} }

View File

@ -1,8 +1,8 @@
use nalgebra::{convert, Vector3}; use nalgebra::Vector3;
use super::{Bsdf, Material}; use super::{Bsdf, Material};
use crate::colour::ColourRgbF; use crate::colour::ColourRgbF;
use crate::Real; use crate::realtype::NormalizedToU32;
use std::error::Error; use std::error::Error;
use std::fs::File; use std::fs::File;
@ -12,8 +12,8 @@ use std::sync::Arc;
use std::f64::consts::{FRAC_PI_2, PI}; use std::f64::consts::{FRAC_PI_2, PI};
#[derive(Debug)] #[derive(Debug)]
pub struct RgbSampledBsdfMaterial<T: Real> { pub struct RgbSampledBsdfMaterial {
lut: Arc<Vec<Vec<Vec<Vec<Vector3<T>>>>>>, lut: Arc<Vec<Vec<Vec<Vec<Vector3<f64>>>>>>,
} }
fn expand_and_index<T: Clone>(v: &mut Vec<T>, i: usize, default: T) -> &mut T { fn expand_and_index<T: Clone>(v: &mut Vec<T>, i: usize, default: T) -> &mut T {
@ -23,8 +23,8 @@ fn expand_and_index<T: Clone>(v: &mut Vec<T>, i: usize, default: T) -> &mut T {
&mut v[i] &mut v[i]
} }
impl<T: Real> RgbSampledBsdfMaterial<T> { impl RgbSampledBsdfMaterial {
pub fn from_csv_file(filename: &str) -> Result<RgbSampledBsdfMaterial<T>, Box<dyn Error>> { pub fn from_csv_file(filename: &str) -> Result<RgbSampledBsdfMaterial, Box<dyn Error>> {
let csv_file = File::open(filename)?; let csv_file = File::open(filename)?;
let mut reader = csv::Reader::from_reader(BufReader::new(&csv_file)); let mut reader = csv::Reader::from_reader(BufReader::new(&csv_file));
let mut lut = Vec::new(); let mut lut = Vec::new();
@ -49,28 +49,28 @@ impl<T: Real> RgbSampledBsdfMaterial<T> {
), ),
phi_out_index, phi_out_index,
Vector3::zeros(), Vector3::zeros(),
) = Vector3::new(convert(red), convert(green), convert(blue)); ) = Vector3::new(red, green, blue);
} }
let lut = Arc::new(lut); let lut = Arc::new(lut);
Ok(RgbSampledBsdfMaterial { lut }) Ok(RgbSampledBsdfMaterial { lut })
} }
} }
impl<'a, T: Real> Material<T> for RgbSampledBsdfMaterial<T> { impl<'a> Material for RgbSampledBsdfMaterial {
fn bsdf(&self) -> Bsdf<T> { fn bsdf(&self) -> Bsdf {
let lut = Arc::clone(&self.lut); let lut = Arc::clone(&self.lut);
Box::new(move |w_in, w_out, colour_in| { Box::new(move |w_in, w_out, colour_in| {
if w_in.z < T::zero() || w_out.z < T::zero() { if w_in.z < 0.0 || w_out.z < 0.0 {
return ColourRgbF::new(T::zero(), T::zero(), T::zero()); return ColourRgbF::new(0.0, 0.0, 0.0);
} }
let theta_in = w_in.z.acos(); let theta_in = w_in.z.acos();
let theta_in_index = (theta_in / convert(FRAC_PI_2)).normalized_to_u32(4) as usize; let theta_in_index = (theta_in / FRAC_PI_2).normalized_to_u32(4) as usize;
let phi_in = w_in.y.atan2(w_in.x) + convert(PI); let phi_in = w_in.y.atan2(w_in.x) + PI;
let phi_in_index = (phi_in / convert(2.0 * PI)).normalized_to_u32(6) as usize; let phi_in_index = (phi_in / (2.0 * PI)).normalized_to_u32(6) as usize;
let theta_out = w_out.z.acos(); let theta_out = w_out.z.acos();
let theta_out_index = (theta_out / convert(FRAC_PI_2)).normalized_to_u32(4) as usize; let theta_out_index = (theta_out / FRAC_PI_2).normalized_to_u32(4) as usize;
let phi_out = w_out.y.atan2(w_out.x) + convert(PI); let phi_out = w_out.y.atan2(w_out.x) + PI;
let phi_out_index = (phi_out / convert(2.0 * PI)).normalized_to_u32(6) as usize; let phi_out_index = (phi_out / (2.0 * PI)).normalized_to_u32(6) as usize;
ColourRgbF::from_vector3( ColourRgbF::from_vector3(
&colour_in.as_vector3().component_mul( &colour_in.as_vector3().component_mul(
&lut[theta_in_index][phi_in_index][theta_out_index][phi_out_index], &lut[theta_in_index][phi_in_index][theta_out_index][phi_out_index],
@ -79,7 +79,7 @@ impl<'a, T: Real> Material<T> for RgbSampledBsdfMaterial<T> {
}) })
} }
fn sample(&self, w_o: &Vector3<T>) -> Vec<Vector3<T>> { fn sample(&self, w_o: &Vector3<f64>) -> Vec<Vector3<f64>> {
vec![Vector3::new(-w_o.x, -w_o.y, w_o.z)] vec![Vector3::new(-w_o.x, -w_o.y, w_o.z)]
} }
} }

View File

@ -1,11 +1,9 @@
/// Load a model from a Wavefront .obj file /// Load a model from a Wavefront .obj file
mod wavefront_obj { mod wavefront_obj {
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use crate::raycasting::{Primitive, Triangle}; use crate::raycasting::{Primitive, Triangle};
use alga::general::SupersetOf;
use nalgebra::{convert, Point3, Vector3}; use nalgebra::{convert, Point3, Vector3};
use obj::{IndexTuple, Obj, SimplePolygon}; use obj::{IndexTuple, Obj, SimplePolygon};
@ -13,16 +11,13 @@ mod wavefront_obj {
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
fn get_vertex_and_normal<T: Real>( fn get_vertex_and_normal(
index_tuple: &IndexTuple, index_tuple: &IndexTuple,
vertex_positions: &[[f32; 3]], vertex_positions: &[[f32; 3]],
normal_positions: &[[f32; 3]], normal_positions: &[[f32; 3]],
) -> (Point3<T>, Vector3<T>) ) -> (Point3<f64>, Vector3<f64>) {
where
T: SupersetOf<f32>,
{
let &IndexTuple(vertex_index, _, maybe_normal_index) = index_tuple; let &IndexTuple(vertex_index, _, maybe_normal_index) = index_tuple;
let vertex: Point3<T> = convert(Point3::from_slice(&vertex_positions[vertex_index])); let vertex = convert(Point3::from_slice(&vertex_positions[vertex_index]));
let normal = match maybe_normal_index { let normal = match maybe_normal_index {
Some(normal_index) => convert(Vector3::from_row_slice(&normal_positions[normal_index])), Some(normal_index) => convert(Vector3::from_row_slice(&normal_positions[normal_index])),
None => Vector3::zeros(), None => Vector3::zeros(),
@ -30,15 +25,12 @@ mod wavefront_obj {
(vertex, normal.normalize()) (vertex, normal.normalize())
} }
fn get_triangles<T: Real>( fn get_triangles(
polygon: &SimplePolygon, polygon: &SimplePolygon,
vertex_positions: &[[f32; 3]], vertex_positions: &[[f32; 3]],
normal_positions: &[[f32; 3]], normal_positions: &[[f32; 3]],
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
) -> Vec<Triangle<T>> ) -> Vec<Triangle> {
where
T: SupersetOf<f32>,
{
if let Some(v0_index) = polygon.iter().next() { if let Some(v0_index) = polygon.iter().next() {
let (v0_vertex, v0_normal) = let (v0_vertex, v0_normal) =
get_vertex_and_normal(v0_index, &vertex_positions, &normal_positions); get_vertex_and_normal(v0_index, &vertex_positions, &normal_positions);
@ -65,13 +57,10 @@ mod wavefront_obj {
} }
} }
pub fn load_obj<T: Real>( pub fn load_obj(
filename: &Path, filename: &Path,
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
) -> Result<Vec<Arc<dyn Primitive<T>>>> ) -> Result<Vec<Arc<dyn Primitive>>> {
where
T: SupersetOf<f32>,
{
let obj = Obj::<SimplePolygon>::load(filename)?; let obj = Obj::<SimplePolygon>::load(filename)?;
Ok(obj Ok(obj
@ -80,7 +69,7 @@ mod wavefront_obj {
.flat_map(|object| object.groups.iter()) .flat_map(|object| object.groups.iter())
.flat_map(|group| group.polys.iter()) .flat_map(|group| group.polys.iter())
.flat_map(|poly| get_triangles(poly, &obj.position, &obj.normal, material.clone())) .flat_map(|poly| get_triangles(poly, &obj.position, &obj.normal, material.clone()))
.map(|triangle| Arc::new(triangle) as Arc<dyn Primitive<T>>) .map(|triangle| Arc::new(triangle) as Arc<dyn Primitive>)
.collect()) .collect())
} }
} }

View File

@ -1,5 +1,3 @@
use crate::Real;
use crate::util::Interval; use crate::util::Interval;
use super::{IntersectP, Ray}; use super::{IntersectP, Ray};
@ -8,8 +6,8 @@ use itertools::izip;
pub use crate::util::axis_aligned_bounding_box::BoundingBox; pub use crate::util::axis_aligned_bounding_box::BoundingBox;
impl<T: Real> IntersectP<T> for BoundingBox<T> { impl IntersectP for BoundingBox {
fn intersect(&self, ray: &Ray<T>) -> bool { fn intersect(&self, ray: &Ray) -> 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
izip!(ray.origin.iter(), ray.direction.iter(), self.bounds.iter()) izip!(ray.origin.iter(), ray.direction.iter(), self.bounds.iter())
@ -35,7 +33,7 @@ mod tests {
use nalgebra::{Point3, Vector3}; use nalgebra::{Point3, Vector3};
fn wrap_value_in_interval(value: f64, interval: Interval<f64>) -> f64 { fn wrap_value_in_interval(value: f64, interval: Interval) -> f64 {
let distance_from_start = (value - interval.get_min()).abs(); let distance_from_start = (value - interval.get_min()).abs();
let range = interval.get_max() - interval.get_min(); let range = interval.get_max() - interval.get_min();
let multiple_of_range = distance_from_start / range; let multiple_of_range = distance_from_start / range;
@ -48,7 +46,7 @@ mod tests {
interval.contains_value(wrap_value_in_interval(v, interval)) interval.contains_value(wrap_value_in_interval(v, interval))
} }
fn wrap_point_into_bounding_box(point: Point3<f64>, bounds: &BoundingBox<f64>) -> Point3<f64> { fn wrap_point_into_bounding_box(point: Point3<f64>, bounds: &BoundingBox) -> Point3<f64> {
Point3::from(Vector3::from_iterator( Point3::from(Vector3::from_iterator(
point point
.iter() .iter()

View File

@ -2,9 +2,7 @@ use super::{
Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectP, IntersectionInfo, Primitive, Ray, Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectP, IntersectionInfo, Primitive, Ray,
}; };
use crate::Real; use nalgebra::Point3;
use nalgebra::{convert, Point3};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::Arc; use std::sync::Arc;
@ -17,31 +15,27 @@ use std::sync::Arc;
/// Each node knows the overall bounds of all it's children, which means that a ray that /// Each node knows the overall bounds of all it's children, which means that a ray that
/// doesn't intersect the [BoundingBox](BoundingBox) of the node doesn't intersect any of /// doesn't intersect the [BoundingBox](BoundingBox) of the node doesn't intersect any of
/// the primitives stored in it's children. /// the primitives stored in it's children.
pub enum BoundingVolumeHierarchy<T: Real> { pub enum BoundingVolumeHierarchy {
Node { Node {
bounds: BoundingBox<T>, bounds: BoundingBox,
left: Box<BoundingVolumeHierarchy<T>>, left: Box<BoundingVolumeHierarchy>,
right: Box<BoundingVolumeHierarchy<T>>, right: Box<BoundingVolumeHierarchy>,
}, },
Leaf { Leaf {
bounds: BoundingBox<T>, bounds: BoundingBox,
primitives: Vec<Arc<dyn Primitive<T>>>, primitives: Vec<Arc<dyn Primitive>>,
}, },
} }
fn centre<T: Real>(bounds: &BoundingBox<T>) -> Point3<T> { fn centre(bounds: &BoundingBox) -> Point3<f64> {
let two = convert(2.0);
Point3::new( Point3::new(
(bounds.bounds[0].get_min() + bounds.bounds[0].get_max()) / two, (bounds.bounds[0].get_min() + bounds.bounds[0].get_max()) / 2.00,
(bounds.bounds[1].get_min() + bounds.bounds[1].get_max()) / two, (bounds.bounds[1].get_min() + bounds.bounds[1].get_max()) / 2.0,
(bounds.bounds[2].get_min() + bounds.bounds[2].get_max()) / two, (bounds.bounds[2].get_min() + bounds.bounds[2].get_max()) / 2.0,
) )
} }
fn heuristic_split<T: Real>( fn heuristic_split(primitives: &mut [Arc<dyn Primitive>], bounds: &BoundingBox) -> usize {
primitives: &mut [Arc<dyn Primitive<T>>],
bounds: &BoundingBox<T>,
) -> usize {
let largest_dimension = bounds.largest_dimension(); let largest_dimension = bounds.largest_dimension();
primitives.sort_unstable_by(|a, b| { primitives.sort_unstable_by(|a, b| {
centre(&a.bounding_box())[largest_dimension] centre(&a.bounding_box())[largest_dimension]
@ -51,12 +45,12 @@ fn heuristic_split<T: Real>(
primitives.len() / 2 primitives.len() / 2
} }
impl<T: Real> BoundingVolumeHierarchy<T> { impl BoundingVolumeHierarchy {
pub fn build(primitives: &mut [Arc<dyn Primitive<T>>]) -> Self { pub fn build(primitives: &mut [Arc<dyn Primitive>]) -> Self {
BoundingVolumeHierarchy::build_from_slice(primitives) BoundingVolumeHierarchy::build_from_slice(primitives)
} }
pub fn build_from_slice(primitives: &mut [Arc<dyn Primitive<T>>]) -> Self { pub fn build_from_slice(primitives: &mut [Arc<dyn Primitive>]) -> Self {
let bounds = primitives let bounds = primitives
.iter() .iter()
.fold(BoundingBox::empty(), |acc, p| acc.union(&p.bounding_box())); .fold(BoundingBox::empty(), |acc, p| acc.union(&p.bounding_box()));
@ -80,10 +74,10 @@ impl<T: Real> BoundingVolumeHierarchy<T> {
} }
} }
fn closest_intersection<T: Real>( fn closest_intersection(
a: Option<IntersectionInfo<T>>, a: Option<IntersectionInfo>,
b: Option<IntersectionInfo<T>>, b: Option<IntersectionInfo>,
) -> Option<IntersectionInfo<T>> { ) -> Option<IntersectionInfo> {
match a { match a {
None => b, None => b,
Some(a_info) => match b { Some(a_info) => match b {
@ -97,8 +91,8 @@ fn closest_intersection<T: Real>(
} }
} }
impl<T: Real> Intersect<T> for BoundingVolumeHierarchy<T> { impl Intersect for BoundingVolumeHierarchy {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
match self { match self {
BoundingVolumeHierarchy::Node { BoundingVolumeHierarchy::Node {
bounds, bounds,
@ -125,13 +119,13 @@ impl<T: Real> Intersect<T> for BoundingVolumeHierarchy<T> {
} }
} }
impl<T: Real> HasBoundingBox<T> for BoundingVolumeHierarchy<T> { impl HasBoundingBox for BoundingVolumeHierarchy {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox {
BoundingBox::empty() BoundingBox::empty()
} }
} }
impl<T: Real> Aggregate<T> for BoundingVolumeHierarchy<T> {} impl Aggregate for BoundingVolumeHierarchy {}
#[cfg(test)] #[cfg(test)]
mod test {} mod test {}

View File

@ -1,7 +1,6 @@
use nalgebra::{Affine3, Point3, Vector3}; use nalgebra::{Affine3, Point3, Vector3};
use super::materials::Material; use super::materials::Material;
use crate::Real;
use std::sync::Arc; use std::sync::Arc;
@ -27,19 +26,19 @@ pub mod vec_aggregate;
/// This is the basic ray struct used to define things like a line-of-sight /// This is the basic ray struct used to define things like a line-of-sight
/// going out from the camera of a reflection from a surface. /// going out from the camera of a reflection from a surface.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Ray<T: Real> { pub struct Ray {
/// The start point of the ray /// The start point of the ray
pub origin: Point3<T>, pub origin: Point3<f64>,
/// The direction the ray goes in. /// The direction the ray goes in.
/// ///
/// This vector should always be kept normalized /// This vector should always be kept normalized
pub direction: Vector3<T>, pub direction: Vector3<f64>,
} }
impl<T: Real> Ray<T> { impl Ray {
/// Create a new ray /// Create a new ray
pub fn new(origin: Point3<T>, direction: Vector3<T>) -> Ray<T> { pub fn new(origin: Point3<f64>, direction: Vector3<f64>) -> Ray {
Ray { Ray {
origin, origin,
direction: direction.normalize(), direction: direction.normalize(),
@ -47,7 +46,7 @@ impl<T: Real> Ray<T> {
} }
/// Return the point on the ray that is `t` units from the start /// Return the point on the ray that is `t` units from the start
pub fn point_at(&self, t: T) -> Point3<T> { pub fn point_at(&self, t: f64) -> Point3<f64> {
self.origin + self.direction * t self.origin + self.direction * t
} }
@ -56,7 +55,7 @@ impl<T: Real> Ray<T> {
/// `amount` is normally a very small number. This function is useful for ensuring /// `amount` is normally a very small number. This function is useful for ensuring
/// that rounding-errors don;t cause a reflection ray doesn't intersect with the point /// that rounding-errors don;t cause a reflection ray doesn't intersect with the point
/// it's reflected from. /// it's reflected from.
pub fn bias(&self, amount: T) -> Ray<T> { pub fn bias(&self, amount: f64) -> Ray {
Ray::new(self.origin + self.direction * amount, self.direction) Ray::new(self.origin + self.direction * amount, self.direction)
} }
} }
@ -66,42 +65,42 @@ impl<T: Real> Ray<T> {
/// This struct is returned by [intersect()](Intersect::intersect) and contatins all the /// This struct is returned by [intersect()](Intersect::intersect) and contatins all the
/// information needed to evaluate the rendering function for that intersection. /// information needed to evaluate the rendering function for that intersection.
#[derive(Debug)] #[derive(Debug)]
pub struct IntersectionInfo<T: Real> { pub struct IntersectionInfo {
/// The distance between the ray origin and the intersection point /// The distance between the ray origin and the intersection point
pub distance: T, pub distance: f64,
/// The intersection point /// The intersection point
pub location: Point3<T>, pub location: Point3<f64>,
/// The surface normal at the intersection point /// The surface normal at the intersection point
pub normal: Vector3<T>, pub normal: Vector3<f64>,
/// The surface tangent at the intersection point /// The surface tangent at the intersection point
/// ///
/// Which surface tangent direction returned is dependent on the [Primitive](Primitive) /// Which surface tangent direction returned is dependent on the [Primitive](Primitive)
/// but should generally be smooth over any given surface /// but should generally be smooth over any given surface
pub tangent: Vector3<T>, pub tangent: Vector3<f64>,
/// Another surface tangent, perpendicular to `tangent` /// Another surface tangent, perpendicular to `tangent`
/// ///
/// The cross product or `normal` and `tangent` /// The cross product or `normal` and `tangent`
pub cotangent: Vector3<T>, pub cotangent: Vector3<f64>,
/// The direction from the intersection point back towards the ray /// The direction from the intersection point back towards the ray
/// ///
/// Equal to `-ray.direction` /// Equal to `-ray.direction`
pub retro: Vector3<T>, pub retro: Vector3<f64>,
/// The [Material](crate::materials::Material) which describes the optical /// The [Material](crate::materials::Material) which describes the optical
/// properties of the intersected surface /// properties of the intersected surface
pub material: Arc<dyn Material<T>>, pub material: Arc<dyn Material>,
} }
/// A geometric object that has a [Material](crate::materials::Material) and can be /// A geometric object that has a [Material](crate::materials::Material) and can be
/// intersected with a [Ray](Ray) /// intersected with a [Ray](Ray)
pub trait Intersect<T: Real>: Send + Sync { pub trait Intersect: 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) -> Option<IntersectionInfo>;
} }
/// A geometric object that can be intersected with a ray /// A geometric object that can be intersected with a ray
@ -109,35 +108,35 @@ pub trait Intersect<T: Real>: Send + Sync {
/// This is useful for objects that don't have materials (such as [BoundingBox](BoundingBox)) /// This is useful for objects that don't have materials (such as [BoundingBox](BoundingBox))
/// and as a (possibly) faster alternative to [Intersect](Intersect) when only a simple /// and as a (possibly) faster alternative to [Intersect](Intersect) when only a simple
/// intersection test is needed. /// intersection test is needed.
pub trait IntersectP<T: Real>: Send + Sync { pub trait IntersectP: 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) -> bool;
} }
/// Any geometric object for which a [BoundingBox](BoundingBox) can be calculated /// Any geometric object for which a [BoundingBox](BoundingBox) can be calculated
pub trait HasBoundingBox<T: Real>: Send + Sync { pub trait HasBoundingBox: Send + Sync {
/// The axis-aligned bounding box of the object /// The axis-aligned bounding box of the object
/// ///
/// The object must fit entirely inside this box. /// The object must fit entirely inside this box.
fn bounding_box(&self) -> BoundingBox<T>; fn bounding_box(&self) -> BoundingBox;
} }
/// Any geometric object which can have an affine transformation applied to it /// Any geometric object which can have an affine transformation applied to it
/// ///
/// Used for moving, rotating or scaling primitives /// Used for moving, rotating or scaling primitives
pub trait Transform<T: Real> { pub trait Transform {
/// Create a new object by applying the transformation to this object. /// Create a new object by applying the transformation to this object.
fn transform(&self, transformation: &Affine3<T>) -> Self; fn transform(&self, transformation: &Affine3<f64>) -> Self;
} }
/// A basic geometric primitive such as a sphere or a triangle /// A basic geometric primitive such as a sphere or a triangle
pub trait Primitive<T: Real>: Intersect<T> + HasBoundingBox<T> { pub trait Primitive: Intersect + HasBoundingBox {
// / Create a new object by applying the transformation to this object. // / Create a new object by applying the transformation to this object.
//fn transform(&self, transformation: &Affine3<T>) -> dyn Primitive<T>; //fn transform(&self, transformation: &Affine3) -> dyn Primitive;
} }
/// Either a primitive or a collection of primitives /// Either a primitive or a collection of primitives
pub trait Aggregate<T: Real>: Intersect<T> + HasBoundingBox<T> {} pub trait Aggregate: Intersect + HasBoundingBox {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -145,26 +144,26 @@ mod tests {
use super::*; use super::*;
use quickcheck::{Arbitrary, Gen}; use quickcheck::{Arbitrary, Gen};
impl<T: Arbitrary + Real> Arbitrary for Ray<T> { impl Arbitrary for Ray {
fn arbitrary<G: Gen>(g: &mut G) -> Ray<T> { fn arbitrary<G: Gen>(g: &mut G) -> Ray {
let origin = <Point3<T> as Arbitrary>::arbitrary(g); let origin = <Point3<f64> as Arbitrary>::arbitrary(g);
let direction = <Vector3<T> as Arbitrary>::arbitrary(g); let direction = <Vector3<f64> as Arbitrary>::arbitrary(g);
return Ray::new(origin, direction); return Ray::new(origin, direction);
} }
} }
#[quickcheck] #[quickcheck]
fn t0_is_origin(ray: Ray<f64>) -> bool { fn t0_is_origin(ray: Ray) -> bool {
ray.point_at(0.0) == ray.origin ray.point_at(0.0) == ray.origin
} }
#[quickcheck] #[quickcheck]
fn t1_is_origin_plus_direction(ray: Ray<f64>) -> bool { fn t1_is_origin_plus_direction(ray: Ray) -> bool {
ray.point_at(1.0) == ray.origin + ray.direction ray.point_at(1.0) == ray.origin + ray.direction
} }
#[quickcheck] #[quickcheck]
fn points_are_colinear(ray: Ray<f64>, t1: f64, t2: f64, t3: f64) -> bool { fn points_are_colinear(ray: Ray, t1: f64, t2: f64, t3: f64) -> bool {
let p1 = ray.point_at(t1); let p1 = ray.point_at(t1);
let p2 = ray.point_at(t2); let p2 = ray.point_at(t2);
let p3 = ray.point_at(t3); let p3 = ray.point_at(t3);
@ -177,7 +176,7 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn t_is_distance(ray: Ray<f64>, t: f64) -> bool { fn t_is_distance(ray: Ray, t: f64) -> bool {
(ray.point_at(t) - ray.origin).norm() - t.abs() < 0.0000000001 (ray.point_at(t) - ray.origin).norm() - t.abs() < 0.0000000001
} }
} }

View File

@ -1,30 +1,29 @@
use nalgebra::{convert, Affine3, Point3, Vector3}; use nalgebra::{Affine3, Point3, Vector3};
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
use std::sync::Arc; use std::sync::Arc;
#[derive(Clone)] #[derive(Clone)]
pub struct Plane<T: Real> { pub struct Plane {
normal: Vector3<T>, normal: Vector3<f64>,
tangent: Vector3<T>, tangent: Vector3<f64>,
cotangent: Vector3<T>, cotangent: Vector3<f64>,
distance_from_origin: T, distance_from_origin: f64,
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
} }
impl<T: Real> Plane<T> { impl Plane {
pub fn new( pub fn new(
normal: Vector3<T>, normal: Vector3<f64>,
distance_from_origin: T, distance_from_origin: f64,
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
) -> Plane<T> { ) -> Plane {
let normal = normal.normalize(); let normal = normal.normalize();
let mut axis_closest_to_tangent = Vector3::zeros(); let mut axis_closest_to_tangent = Vector3::zeros();
axis_closest_to_tangent[normal.iamin()] = T::one(); axis_closest_to_tangent[normal.iamin()] = 1.0;
let cotangent = normal.cross(&axis_closest_to_tangent).normalize(); let cotangent = normal.cross(&axis_closest_to_tangent).normalize();
let tangent = normal.cross(&cotangent); let tangent = normal.cross(&cotangent);
Plane { Plane {
@ -37,8 +36,8 @@ impl<T: Real> Plane<T> {
} }
} }
impl<T: Real> Transform<T> for Plane<T> { impl Transform for Plane {
fn transform(&self, transformation: &Affine3<T>) -> Self { fn transform(&self, transformation: &Affine3<f64>) -> Self {
Plane { Plane {
normal: transformation.transform_vector(&self.normal).normalize(), normal: transformation.transform_vector(&self.normal).normalize(),
cotangent: transformation.transform_vector(&self.cotangent).normalize(), cotangent: transformation.transform_vector(&self.cotangent).normalize(),
@ -51,21 +50,21 @@ impl<T: Real> Transform<T> for Plane<T> {
} }
} }
impl<T: Real> Intersect<T> for Plane<T> { impl Intersect for Plane {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
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;
let point_on_plane_minus_ray_origin_dot_normal = let point_on_plane_minus_ray_origin_dot_normal =
(point_on_plane - ray.origin.coords).dot(&self.normal); (point_on_plane - ray.origin.coords).dot(&self.normal);
if ray_direction_dot_plane_normal == convert(0.0) { if ray_direction_dot_plane_normal == 0.0 {
//Ray is parallel to plane //Ray is parallel to plane
if point_on_plane_minus_ray_origin_dot_normal != convert(0.0) { if point_on_plane_minus_ray_origin_dot_normal != 0.0 {
//Ray is not in plane //Ray is not in plane
return None; return None;
} }
} }
let t = point_on_plane_minus_ray_origin_dot_normal / ray_direction_dot_plane_normal; let t = point_on_plane_minus_ray_origin_dot_normal / ray_direction_dot_plane_normal;
if t < convert(0.0) { if t < 0.0 {
return None; return None;
} }
Some(IntersectionInfo { Some(IntersectionInfo {
@ -80,18 +79,14 @@ impl<T: Real> Intersect<T> for Plane<T> {
} }
} }
impl<T: Real> HasBoundingBox<T> for Plane<T> { impl HasBoundingBox for Plane {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox {
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<f64>| {
let infinity: T = convert(std::f64::INFINITY); Vector3::from_iterator(
Vector3::from_iterator(v.iter().map(|&elem| { v.iter()
if elem == T::zero() { .map(|&elem| if elem == 0.0 { 0.0 } else { std::f64::INFINITY }),
T::zero() )
} else {
infinity
}
}))
}; };
let tangent = f(self.tangent); let tangent = f(self.tangent);
let cotangent = f(self.cotangent); let cotangent = f(self.cotangent);
@ -103,7 +98,7 @@ impl<T: Real> HasBoundingBox<T> for Plane<T> {
} }
} }
impl<T: Real> Primitive<T> for Plane<T> {} impl Primitive for Plane {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,21 +1,20 @@
use nalgebra::{convert, Affine3, Point3, Vector3}; use nalgebra::{Affine3, Point3, Vector3};
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
use std::sync::Arc; use std::sync::Arc;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Sphere<T: Real> { pub struct Sphere {
centre: Point3<T>, centre: Point3<f64>,
radius: T, radius: f64,
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
} }
impl<T: Real> Sphere<T> { impl Sphere {
pub fn new(centre: Point3<T>, radius: T, material: Arc<dyn Material<T>>) -> Sphere<T> { pub fn new(centre: Point3<f64>, radius: f64, material: Arc<dyn Material>) -> Sphere {
Sphere { Sphere {
centre, centre,
radius, radius,
@ -24,54 +23,52 @@ impl<T: Real> Sphere<T> {
} }
} }
impl<T: Real> Transform<T> for Sphere<T> { impl Transform for Sphere {
fn transform(&self, transformation: &Affine3<T>) -> Self { fn transform(&self, transformation: &Affine3<f64>) -> Self {
Sphere { Sphere {
centre: transformation.transform_point(&self.centre), centre: transformation.transform_point(&self.centre),
// This is not the most efficient way of calculating the radius, // This is not the most efficient way of calculating the radius,
//but will work as long as the resulting shape is still a sphere. //but will work as long as the resulting shape is still a sphere.
radius: transformation radius: transformation
.transform_vector(&Vector3::new(self.radius, T::zero(), T::zero())) .transform_vector(&Vector3::new(self.radius, 0.0, 0.0))
.norm(), .norm(),
material: Arc::clone(&self.material), material: Arc::clone(&self.material),
} }
} }
} }
impl<T: Real> Intersect<T> for Sphere<T> { impl Intersect for Sphere {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
let two: T = convert(2.0);
let four: T = convert(4.0);
let r_o = ray.origin.coords; let r_o = ray.origin.coords;
let centre_coords = self.centre.coords; let centre_coords = self.centre.coords;
let a = ray let a = ray
.direction .direction
.component_mul(&ray.direction) .component_mul(&ray.direction)
.iter() .iter()
.fold(T::zero(), |a, b| a + *b); .fold(0.0, |a, b| a + *b);
let b = ((r_o.component_mul(&ray.direction) - centre_coords.component_mul(&ray.direction)) let b = ((r_o.component_mul(&ray.direction) - centre_coords.component_mul(&ray.direction))
* two) * 2.0)
.iter() .iter()
.fold(T::zero(), |a, b| a + *b); .fold(0.0, |a, b| a + *b);
let c = (r_o.component_mul(&r_o) + centre_coords.component_mul(&centre_coords) let c = (r_o.component_mul(&r_o) + centre_coords.component_mul(&centre_coords)
- centre_coords.component_mul(&r_o) * two) - centre_coords.component_mul(&r_o) * 2.0)
.iter() .iter()
.fold(T::zero(), |a, b| a + *b) .fold(0.0, |a, b| a + *b)
- self.radius * self.radius; - self.radius * self.radius;
let delta_squared: T = b * b - four * a * c; let delta_squared = b * b - 4.0 * a * c;
if delta_squared < T::zero() { if delta_squared < 0.0 {
None None
} else { } else {
let delta = delta_squared.sqrt(); let delta = delta_squared.sqrt();
let one_over_2_a = T::one() / (two * a); let one_over_2_a = 1.0 / (2.0 * a);
let t1 = (-b - delta) * one_over_2_a; let t1 = (-b - delta) * one_over_2_a;
let t2 = (-b + delta) * one_over_2_a; let t2 = (-b + delta) * one_over_2_a;
let distance = if t1 < T::zero() || (t2 >= T::zero() && t1 >= t2) { let distance = if t1 < 0.0 || (t2 >= 0.0 && t1 >= t2) {
t2 t2
} else { } else {
t1 t1
}; };
if distance <= T::zero() { if distance <= 0.0 {
None None
} else { } else {
let location = ray.point_at(distance); let location = ray.point_at(distance);
@ -93,14 +90,14 @@ impl<T: Real> Intersect<T> for Sphere<T> {
} }
} }
impl<T: Real> HasBoundingBox<T> for Sphere<T> { impl HasBoundingBox for Sphere {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox {
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: Real> Primitive<T> for Sphere<T> {} impl Primitive for Sphere {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -1,5 +1,4 @@
use crate::materials::Material; use crate::materials::Material;
use crate::Real;
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform}; use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
use nalgebra::{Affine3, Point3, Vector2, Vector3}; use nalgebra::{Affine3, Point3, Vector2, Vector3};
@ -7,14 +6,14 @@ use nalgebra::{Affine3, Point3, Vector2, Vector3};
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Triangle<T: Real> { pub struct Triangle {
pub vertices: [Point3<T>; 3], pub vertices: [Point3<f64>; 3],
pub normals: [Vector3<T>; 3], pub normals: [Vector3<f64>; 3],
pub material: Arc<dyn Material<T>>, pub material: Arc<dyn Material>,
} }
impl<T: Real> Transform<T> for Triangle<T> { impl Transform for Triangle {
fn transform(&self, transformation: &Affine3<T>) -> Self { fn transform(&self, transformation: &Affine3<f64>) -> Self {
let normal_transformation = let normal_transformation =
Affine3::from_matrix_unchecked(transformation.inverse().matrix().transpose()); Affine3::from_matrix_unchecked(transformation.inverse().matrix().transpose());
Triangle { Triangle {
@ -33,13 +32,13 @@ impl<T: Real> Transform<T> for Triangle<T> {
} }
} }
impl<T: Real> Intersect<T> for Triangle<T> { impl Intersect for Triangle {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
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);
let permuted_ray_direction = permute_vector_elements(&ray.direction, &indices); let permuted_ray_direction = permute_vector_elements(&ray.direction, &indices);
let shear_slopes = calculate_shear_to_z_axis(&permuted_ray_direction); let shear_slopes = calculate_shear_to_z_axis(&permuted_ray_direction);
let transformed_vertices: Vec<Vector3<T>> = self let transformed_vertices: Vec<Vector3<f64>> = self
.vertices .vertices
.iter() .iter()
.map(|elem| { .map(|elem| {
@ -60,17 +59,17 @@ impl<T: Real> Intersect<T> for Triangle<T> {
.iter() .iter()
.zip(transformed_vertices.iter()) .zip(transformed_vertices.iter())
.map(|(&coord, vertex)| vertex.z * coord) .map(|(&coord, vertex)| vertex.z * coord)
.fold(T::zero(), |acc, z| acc + z); .fold(0.0, |acc, z| acc + z);
if transformed_z.is_positive() != permuted_ray_direction.z.is_positive() { if transformed_z.is_sign_positive() != permuted_ray_direction.z.is_sign_positive() {
return None; return None;
} }
let location: Point3<T> = barycentric_coordinates let location = barycentric_coordinates
.iter() .iter()
.zip(self.vertices.iter()) .zip(self.vertices.iter())
.map(|(&barycentric_coord, vertex)| vertex.coords * barycentric_coord) .map(|(&barycentric_coord, vertex)| vertex.coords * barycentric_coord)
.fold(Point3::new(T::zero(), T::zero(), T::zero()), |a, e| a + e); .fold(Point3::new(0.0, 0.0, 0.0), |a, e| a + e);
let distance = (ray.origin - location).norm(); let distance = (ray.origin - location).norm();
let normal: Vector3<T> = barycentric_coordinates let normal: Vector3<f64> = barycentric_coordinates
.iter() .iter()
.zip(self.normals.iter()) .zip(self.normals.iter())
.fold(Vector3::zeros(), |acc, (&coord, vertex)| { .fold(Vector3::zeros(), |acc, (&coord, vertex)| {
@ -98,15 +97,15 @@ impl<T: Real> Intersect<T> for Triangle<T> {
} }
} }
impl<T: Real> HasBoundingBox<T> for Triangle<T> { impl HasBoundingBox for Triangle {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox {
BoundingBox::from_points(&self.vertices) BoundingBox::from_points(&self.vertices)
} }
} }
impl<T: Real> Primitive<T> for Triangle<T> {} impl Primitive for Triangle {}
fn indices_with_index_of_largest_element_last<T: Real>(v: &Vector3<T>) -> [usize; 3] { fn indices_with_index_of_largest_element_last(v: &Vector3<f64>) -> [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]
@ -126,24 +125,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: Real>(v: &Vector3<T>, indices: &[usize; 3]) -> Vector3<T> { fn permute_vector_elements(v: &Vector3<f64>, indices: &[usize; 3]) -> Vector3<f64> {
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: Real>(v: &Vector3<T>) -> Vector2<T> { fn calculate_shear_to_z_axis(v: &Vector3<f64>) -> Vector2<f64> {
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: Real>(v: &Vector3<T>, s: &Vector2<T>) -> Vector3<T> { fn apply_shear_to_z_axis(v: &Vector3<f64>, s: &Vector2<f64>) -> Vector3<f64> {
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: Real>(a: &Vector3<T>, b: &Vector3<T>) -> T { fn signed_edge_function(a: &Vector3<f64>, b: &Vector3<f64>) -> f64 {
a.x * b.y - b.x * a.y a.x * b.y - b.x * a.y
} }
fn signed_edge_functions<T: Real>(vertices: &[Vector3<T>]) -> Vector3<T> { fn signed_edge_functions(vertices: &[Vector3<f64>]) -> Vector3<f64> {
// 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(
@ -157,8 +156,8 @@ fn signed_edge_functions<T: Real>(vertices: &[Vector3<T>]) -> Vector3<T> {
) )
} }
fn barycentric_coordinates_from_signed_edge_functions<T: Real>(e: Vector3<T>) -> Vector3<T> { fn barycentric_coordinates_from_signed_edge_functions(e: Vector3<f64>) -> Vector3<f64> {
e * (T::one() / e.iter().fold(T::zero(), |a, &b| a + b)) e * (1.0 / e.iter().fold(0.0, |a, &b| a + b))
} }
#[cfg(test)] #[cfg(test)]
@ -175,12 +174,12 @@ mod tests {
#[quickcheck] #[quickcheck]
fn transform_by_identity_does_not_change_values( fn transform_by_identity_does_not_change_values(
v0: Point3<f32>, v0: Point3<f64>,
v1: Point3<f32>, v1: Point3<f64>,
v2: Point3<f32>, v2: Point3<f64>,
n0: Vector3<f32>, n0: Vector3<f64>,
n1: Vector3<f32>, n1: Vector3<f64>,
n2: Vector3<f32>, n2: Vector3<f64>,
) -> bool { ) -> bool {
let n0 = n0.normalize(); let n0 = n0.normalize();
let n1 = n1.normalize(); let n1 = n1.normalize();
@ -201,13 +200,13 @@ mod tests {
#[quickcheck] #[quickcheck]
fn translate_does_not_change_normals( fn translate_does_not_change_normals(
v0: Point3<f32>, v0: Point3<f64>,
v1: Point3<f32>, v1: Point3<f64>,
v2: Point3<f32>, v2: Point3<f64>,
n0: Vector3<f32>, n0: Vector3<f64>,
n1: Vector3<f32>, n1: Vector3<f64>,
n2: Vector3<f32>, n2: Vector3<f64>,
translation: Vector3<f32>, translation: Vector3<f64>,
) -> bool { ) -> bool {
let n0 = n0.normalize(); let n0 = n0.normalize();
let n1 = n1.normalize(); let n1 = n1.normalize();
@ -224,13 +223,13 @@ mod tests {
#[quickcheck] #[quickcheck]
fn translate_translates_vertices( fn translate_translates_vertices(
v0: Point3<f32>, v0: Point3<f64>,
v1: Point3<f32>, v1: Point3<f64>,
v2: Point3<f32>, v2: Point3<f64>,
n0: Vector3<f32>, n0: Vector3<f64>,
n1: Vector3<f32>, n1: Vector3<f64>,
n2: Vector3<f32>, n2: Vector3<f64>,
translation: Vector3<f32>, translation: Vector3<f64>,
) -> bool { ) -> bool {
let n0 = n0.normalize(); let n0 = n0.normalize();
let n1 = n1.normalize(); let n1 = n1.normalize();
@ -253,43 +252,43 @@ mod tests {
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
#[quickcheck] #[quickcheck]
fn result_is_valid_permutation(v: Vector3<f32>) -> bool { fn result_is_valid_permutation(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
is_valid_permutation(&indices) is_valid_permutation(&indices)
} }
#[quickcheck] #[quickcheck]
fn result_includes_x(v: Vector3<f32>) -> bool { fn result_includes_x(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
indices.iter().any(|&i| i == 0) indices.iter().any(|&i| i == 0)
} }
#[quickcheck] #[quickcheck]
fn result_includes_y(v: Vector3<f32>) -> bool { fn result_includes_y(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
indices.iter().any(|&i| i == 1) indices.iter().any(|&i| i == 1)
} }
#[quickcheck] #[quickcheck]
fn result_includes_z(v: Vector3<f32>) -> bool { fn result_includes_z(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
indices.iter().any(|&i| i == 2) indices.iter().any(|&i| i == 2)
} }
#[quickcheck] #[quickcheck]
fn last_index_is_greater_than_or_equal_to_x(v: Vector3<f32>) -> bool { fn last_index_is_greater_than_or_equal_to_x(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
v[indices[2]] >= v.x v[indices[2]] >= v.x
} }
#[quickcheck] #[quickcheck]
fn last_index_is_greater_than_or_equal_to_y(v: Vector3<f32>) -> bool { fn last_index_is_greater_than_or_equal_to_y(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
v[indices[2]] >= v.y v[indices[2]] >= v.y
} }
#[quickcheck] #[quickcheck]
fn last_index_is_greater_than_or_equal_to_z(v: Vector3<f32>) -> bool { fn last_index_is_greater_than_or_equal_to_z(v: Vector3<f64>) -> bool {
let indices = indices_with_index_of_largest_element_last(&v); let indices = indices_with_index_of_largest_element_last(&v);
v[indices[2]] >= v.z v[indices[2]] >= v.z
} }
@ -300,19 +299,19 @@ mod tests {
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
#[quickcheck] #[quickcheck]
fn last_index_is_greater_than_or_equal_to_x(v: Vector3<f32>) -> bool { fn last_index_is_greater_than_or_equal_to_x(v: Vector3<f64>) -> bool {
let p = permute_vector_elements(&v, &indices_with_index_of_largest_element_last(&v)); let p = permute_vector_elements(&v, &indices_with_index_of_largest_element_last(&v));
p.z >= v.x p.z >= v.x
} }
#[quickcheck] #[quickcheck]
fn last_index_is_greater_than_or_equal_to_y(v: Vector3<f32>) -> bool { fn last_index_is_greater_than_or_equal_to_y(v: Vector3<f64>) -> bool {
let p = permute_vector_elements(&v, &indices_with_index_of_largest_element_last(&v)); let p = permute_vector_elements(&v, &indices_with_index_of_largest_element_last(&v));
p.z >= v.y p.z >= v.y
} }
#[quickcheck] #[quickcheck]
fn last_index_is_greater_than_or_equal_to_z(v: Vector3<f32>) -> bool { fn last_index_is_greater_than_or_equal_to_z(v: Vector3<f64>) -> bool {
let p = permute_vector_elements(&v, &indices_with_index_of_largest_element_last(&v)); let p = permute_vector_elements(&v, &indices_with_index_of_largest_element_last(&v));
p.z >= v.z p.z >= v.z
} }
@ -323,19 +322,19 @@ mod tests {
use quickcheck_macros::quickcheck; use quickcheck_macros::quickcheck;
#[quickcheck] #[quickcheck]
fn shear_to_z_axis_makes_x_zero(v: Vector3<f32>) -> bool { fn shear_to_z_axis_makes_x_zero(v: Vector3<f64>) -> bool {
let s = calculate_shear_to_z_axis(&v); let s = calculate_shear_to_z_axis(&v);
apply_shear_to_z_axis(&v, &s).x.abs() < 0.00001 apply_shear_to_z_axis(&v, &s).x.abs() < 0.00001
} }
#[quickcheck] #[quickcheck]
fn shear_to_z_axis_makes_y_zero(v: Vector3<f32>) -> bool { fn shear_to_z_axis_makes_y_zero(v: Vector3<f64>) -> bool {
let s = calculate_shear_to_z_axis(&v); let s = calculate_shear_to_z_axis(&v);
apply_shear_to_z_axis(&v, &s).y.abs() < 0.00001 apply_shear_to_z_axis(&v, &s).y.abs() < 0.00001
} }
#[quickcheck] #[quickcheck]
fn shear_to_z_axis_leaves_z_unchanged(v: Vector3<f32>) -> bool { fn shear_to_z_axis_leaves_z_unchanged(v: Vector3<f64>) -> bool {
let s = calculate_shear_to_z_axis(&v); let s = calculate_shear_to_z_axis(&v);
apply_shear_to_z_axis(&v, &s).z == v.z apply_shear_to_z_axis(&v, &s).z == v.z
} }
@ -348,8 +347,8 @@ mod tests {
#[quickcheck] #[quickcheck]
fn sign_of_signed_edge_function_matches_winding( fn sign_of_signed_edge_function_matches_winding(
a: Vector3<f32>, a: Vector3<f64>,
b: Vector3<f32>, b: Vector3<f64>,
) -> TestResult { ) -> TestResult {
let a_2d = Vector2::new(a.x, a.y); let a_2d = Vector2::new(a.x, a.y);
let b_2d = Vector2::new(b.x, b.y); let b_2d = Vector2::new(b.x, b.y);
@ -366,9 +365,9 @@ mod tests {
#[quickcheck] #[quickcheck]
fn signed_edge_functions_has_same_result_as_signed_edge_function( fn signed_edge_functions_has_same_result_as_signed_edge_function(
a: Vector3<f32>, a: Vector3<f64>,
b: Vector3<f32>, b: Vector3<f64>,
c: Vector3<f32>, c: Vector3<f64>,
) -> bool { ) -> bool {
let es = signed_edge_functions(&vec![a, b, c]); let es = signed_edge_functions(&vec![a, b, c]);
es[0] == signed_edge_function(&b, &c) es[0] == signed_edge_function(&b, &c)
@ -499,7 +498,7 @@ mod tests {
} }
fn intersect_with_centroid_and_test_result< fn intersect_with_centroid_and_test_result<
F: Fn(Option<IntersectionInfo<f64>>, Point3<f64>) -> bool, F: Fn(Option<IntersectionInfo>, Point3<f64>) -> bool,
>( >(
vertex0: Point3<f64>, vertex0: Point3<f64>,
vertex1: Point3<f64>, vertex1: Point3<f64>,
@ -675,7 +674,7 @@ mod tests {
} }
fn intersect_with_barycentric_and_test_result< fn intersect_with_barycentric_and_test_result<
F: Fn(Option<IntersectionInfo<f64>>, Point3<f64>) -> bool, F: Fn(Option<IntersectionInfo>, Point3<f64>) -> bool,
>( >(
vertex0: Point3<f64>, vertex0: Point3<f64>,
vertex1: Point3<f64>, vertex1: Point3<f64>,

View File

@ -1,16 +1,15 @@
use super::{Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; use super::{Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use crate::Real;
impl<T: Real> HasBoundingBox<T> for Vec<Box<dyn Primitive<T>>> { impl HasBoundingBox for Vec<Box<dyn Primitive>> {
fn bounding_box(&self) -> BoundingBox<T> { fn bounding_box(&self) -> BoundingBox {
self.iter().fold(BoundingBox::empty(), |acc, elem| { self.iter().fold(BoundingBox::empty(), |acc, elem| {
acc.union(&elem.bounding_box()) acc.union(&elem.bounding_box())
}) })
} }
} }
impl<T: Real> Intersect<T> for Vec<Box<dyn Primitive<T>>> { impl Intersect for Vec<Box<dyn Primitive>> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
self.iter() self.iter()
.flat_map(|primitive| primitive.intersect(&ray)) .flat_map(|primitive| primitive.intersect(&ray))
.min_by( .min_by(
@ -22,19 +21,18 @@ impl<T: Real> Intersect<T> for Vec<Box<dyn Primitive<T>>> {
} }
} }
impl<T: Real> Aggregate<T> for Vec<Box<dyn Primitive<T>>> {} impl Aggregate for Vec<Box<dyn Primitive>> {}
impl HasBoundingBox for Vec<Box<dyn Aggregate>> {
impl<T: Real> HasBoundingBox<T> for Vec<Box<dyn Aggregate<T>>> { fn bounding_box(&self) -> BoundingBox {
fn bounding_box(&self) -> BoundingBox<T> {
self.iter().fold(BoundingBox::empty(), |acc, elem| { self.iter().fold(BoundingBox::empty(), |acc, elem| {
acc.union(&elem.bounding_box()) acc.union(&elem.bounding_box())
}) })
} }
} }
impl<T: Real> Intersect<T> for Vec<Box<dyn Aggregate<T>>> { impl Intersect for Vec<Box<dyn Aggregate>> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> { fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
self.iter() self.iter()
.flat_map(|aggregate| aggregate.intersect(&ray)) .flat_map(|aggregate| aggregate.intersect(&ray))
.min_by( .min_by(
@ -46,4 +44,4 @@ impl<T: Real> Intersect<T> for Vec<Box<dyn Aggregate<T>>> {
} }
} }
impl<T: Real> Aggregate<T> for Vec<Box<dyn Aggregate<T>>> {} impl Aggregate for Vec<Box<dyn Aggregate>> {}

View File

@ -1,12 +1,7 @@
use nalgebra::RealField;
use simba::scalar::{SubsetOf, SupersetOf};
pub trait NormalizedToU32 { pub trait NormalizedToU32 {
fn normalized_to_u32(self, num_bits: usize) -> u32; fn normalized_to_u32(self, num_bits: usize) -> u32;
} }
pub trait Real: RealField + SupersetOf<f32> + SubsetOf<f32> + NormalizedToU32 + PartialOrd {}
impl NormalizedToU32 for f32 { impl NormalizedToU32 for f32 {
fn normalized_to_u32(self, num_bits: usize) -> u32 { fn normalized_to_u32(self, num_bits: usize) -> u32 {
let scale = (num_bits as f32).exp2() - 1.0; let scale = (num_bits as f32).exp2() - 1.0;
@ -21,9 +16,6 @@ impl NormalizedToU32 for f64 {
} }
} }
impl Real for f32 {}
impl Real for f64 {}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

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

View File

@ -1,9 +1,8 @@
use nalgebra::{Point3}; use nalgebra::Point3;
use crate::raycasting::Aggregate; use crate::raycasting::Aggregate;
use crate::Real;
pub struct Scene<T: Real> { pub struct Scene {
pub camera_location: Point3<T>, pub camera_location: Point3<f64>,
pub objects: Vec<Box<dyn Aggregate<T>>>, pub objects: Vec<Box<dyn Aggregate>>,
} }

View File

@ -1,13 +1,15 @@
use nalgebra::{Matrix3, Vector3}; use nalgebra::{Matrix3, Vector3};
use crate::Real; pub fn try_change_of_basis_matrix(
x: &Vector3<f64>,
pub fn try_change_of_basis_matrix<T: Real>( y: &Vector3<f64>,
x: &Vector3<T>, z: &Vector3<f64>,
y: &Vector3<T>, ) -> Option<Matrix3<f64>> {
z: &Vector3<T>, Some(Matrix3::from_rows(&[
) -> Option<Matrix3<T>> { x.transpose(),
Some(Matrix3::from_rows(&[x.transpose(), y.transpose(), z.transpose()])) y.transpose(),
z.transpose(),
]))
} }
#[cfg(test)] #[cfg(test)]
@ -21,7 +23,7 @@ mod tests {
#[test] #[test]
fn produces_isentity_when_passed_axes() { fn produces_isentity_when_passed_axes() {
let target: Matrix3<f32> = try_change_of_basis_matrix( let target: Matrix3<f64> = try_change_of_basis_matrix(
&Vector3::x_axis(), &Vector3::x_axis(),
&Vector3::y_axis(), &Vector3::y_axis(),
&Vector3::z_axis(), &Vector3::z_axis(),
@ -31,8 +33,8 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn swap_xy_does_not_change_z(v: Vector3<f32>) { fn swap_xy_does_not_change_z(v: Vector3<f64>) {
let target: Matrix3<f32> = try_change_of_basis_matrix( let target: Matrix3<f64> = try_change_of_basis_matrix(
&Vector3::y_axis(), &Vector3::y_axis(),
&Vector3::x_axis(), &Vector3::x_axis(),
&Vector3::z_axis(), &Vector3::z_axis(),
@ -43,8 +45,8 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn swap_xy_copies_y_to_x(v: Vector3<f32>) { fn swap_xy_copies_y_to_x(v: Vector3<f64>) {
let target: Matrix3<f32> = try_change_of_basis_matrix( let target: Matrix3<f64> = try_change_of_basis_matrix(
&Vector3::y_axis(), &Vector3::y_axis(),
&Vector3::x_axis(), &Vector3::x_axis(),
&Vector3::z_axis(), &Vector3::z_axis(),
@ -55,8 +57,8 @@ mod tests {
} }
#[quickcheck] #[quickcheck]
fn swap_xy_copies_x_to_y(v: Vector3<f32>) { fn swap_xy_copies_x_to_y(v: Vector3<f64>) {
let target: Matrix3<f32> = try_change_of_basis_matrix( let target: Matrix3<f64> = try_change_of_basis_matrix(
&Vector3::y_axis(), &Vector3::y_axis(),
&Vector3::x_axis(), &Vector3::x_axis(),
&Vector3::z_axis(), &Vector3::z_axis(),

View File

@ -1,17 +1,16 @@
use nalgebra::Point3; 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: Real> { pub struct BoundingBox {
pub bounds: [Interval<T>; 3], pub bounds: [Interval; 3],
} }
impl<T: Real> BoundingBox<T> { impl BoundingBox {
pub fn from_corners(a: Point3<T>, b: Point3<T>) -> Self { pub fn from_corners(a: Point3<f64>, b: Point3<f64>) -> Self {
let mut result = BoundingBox { let mut result = BoundingBox {
bounds: [Interval::infinite(); 3], bounds: [Interval::infinite(); 3],
}; };
@ -27,7 +26,7 @@ impl<T: Real> BoundingBox<T> {
} }
} }
pub fn from_point(p: Point3<T>) -> Self { pub fn from_point(p: Point3<f64>) -> Self {
BoundingBox { BoundingBox {
bounds: [ bounds: [
Interval::degenerate(p.x), Interval::degenerate(p.x),
@ -39,14 +38,14 @@ impl<T: Real> BoundingBox<T> {
pub fn from_points<'a, I>(points: I) -> Self pub fn from_points<'a, I>(points: I) -> Self
where where
I: IntoIterator<Item = &'a Point3<T>>, I: IntoIterator<Item = &'a Point3<f64>>,
{ {
points points
.into_iter() .into_iter()
.fold(BoundingBox::empty(), |acc, p| acc.expand_to_point(p)) .fold(BoundingBox::empty(), |acc, p| acc.expand_to_point(p))
} }
pub fn expand_to_point(&self, p: &Point3<T>) -> Self { pub fn expand_to_point(&self, p: &Point3<f64>) -> Self {
BoundingBox { BoundingBox {
bounds: [ bounds: [
self.bounds[0].expand_to_value(p.x), self.bounds[0].expand_to_value(p.x),
@ -56,14 +55,14 @@ impl<T: Real> BoundingBox<T> {
} }
} }
pub fn contains_point(&self, p: Point3<T>) -> bool { pub fn contains_point(&self, p: Point3<f64>) -> bool {
self.bounds self.bounds
.iter() .iter()
.zip(p.iter()) .zip(p.iter())
.all(|(interval, &value)| interval.contains_value(value)) .all(|(interval, &value)| interval.contains_value(value))
} }
pub fn union(&self, other: &BoundingBox<T>) -> BoundingBox<T> { pub fn union(&self, other: &BoundingBox) -> BoundingBox {
BoundingBox { BoundingBox {
bounds: [ bounds: [
self.bounds[0].union(other.bounds[0]), self.bounds[0].union(other.bounds[0]),
@ -82,13 +81,13 @@ impl<T: Real> BoundingBox<T> {
( (
index, index,
if elem.is_degenerate() { if elem.is_degenerate() {
-T::one() -1.0
} else { } else {
elem.get_max() - elem.get_min() elem.get_max() - elem.get_min()
}, },
) )
}) })
.fold((0, T::zero()), |(acc, acc_size), (elem, elem_size)| { .fold((0, 0.0), |(acc, acc_size), (elem, elem_size)| {
if elem_size > acc_size { if elem_size > acc_size {
(elem, elem_size) (elem, elem_size)
} else { } else {

View File

@ -1,15 +1,11 @@
use nalgebra::convert;
use crate::Real;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Interval<T: Real> { pub struct Interval {
min: T, min: f64,
max: T, max: f64,
} }
impl<T: Real> Interval<T> { impl Interval {
pub fn new(a: T, b: T) -> Self { pub fn new(a: f64, b: f64) -> Self {
if a > b { if a > b {
Interval { min: b, max: a } Interval { min: b, max: a }
} else { } else {
@ -19,30 +15,30 @@ impl<T: Real> Interval<T> {
pub fn empty() -> Self { pub fn empty() -> Self {
Interval { Interval {
min: convert(std::f64::INFINITY), min: std::f64::INFINITY,
max: convert(std::f64::NEG_INFINITY), max: std::f64::NEG_INFINITY,
} }
} }
pub fn infinite() -> Self { pub fn infinite() -> Self {
Interval { Interval {
min: convert(std::f64::NEG_INFINITY), min: std::f64::NEG_INFINITY,
max: convert(std::f64::INFINITY), max: std::f64::INFINITY,
} }
} }
pub fn degenerate(value: T) -> Self { pub fn degenerate(value: f64) -> Self {
Interval { Interval {
min: value, min: value,
max: value, max: value,
} }
} }
pub fn get_min(&self) -> T { pub fn get_min(&self) -> f64 {
self.min self.min
} }
pub fn get_max(&self) -> T { pub fn get_max(&self) -> f64 {
self.max self.max
} }
@ -54,7 +50,7 @@ impl<T: Real> Interval<T> {
self.min > self.max self.min > self.max
} }
pub fn contains_value(&self, value: T) -> bool { pub fn contains_value(&self, value: f64) -> bool {
value >= self.min && value <= self.max value >= self.min && value <= self.max
} }
@ -78,7 +74,7 @@ impl<T: Real> Interval<T> {
} }
} }
pub fn expand_to_value(self, v: T) -> Self { pub fn expand_to_value(self, v: f64) -> Self {
if self.is_empty() { if self.is_empty() {
Interval::degenerate(v) Interval::degenerate(v)
} else { } else {
@ -120,7 +116,7 @@ mod tests {
#[test] #[test]
fn empty_is_empty() { fn empty_is_empty() {
let target: Interval<f64> = Interval::empty(); let target: Interval = Interval::empty();
assert!(target.is_empty()); assert!(target.is_empty());
} }
@ -240,7 +236,7 @@ mod tests {
#[test] #[test]
fn intersection_with_infinite_is_self() { fn intersection_with_infinite_is_self() {
let target = Interval::new(5f32, 10f32); let target = Interval::new(5.0, 10.0);
let result = target.intersection(Interval::infinite()); let result = target.intersection(Interval::infinite());
assert!(target.min == result.min); assert!(target.min == result.min);
assert!(target.max == result.max); assert!(target.max == result.max);

View File

@ -1,9 +1,6 @@
use crate::realtype::NormalizedToU32;
use nalgebra::Point3; use nalgebra::Point3;
use crate::Real;
//use std::cmp::Ordering;
fn spread_bits(v: u32) -> u32 { fn spread_bits(v: u32) -> u32 {
let mut result = 0; let mut result = 0;
for power in 0..9 { for power in 0..9 {
@ -12,7 +9,7 @@ fn spread_bits(v: u32) -> u32 {
result result
} }
pub fn morton_order_value_3d<T: Real>(p: Point3<T>) -> u32 { pub fn morton_order_value_3d(p: Point3<f64>) -> u32 {
let x = p.x.normalized_to_u32(10); let x = p.x.normalized_to_u32(10);
let y = p.y.normalized_to_u32(10); let y = p.y.normalized_to_u32(10);
let z = p.z.normalized_to_u32(10); let z = p.z.normalized_to_u32(10);

View File

@ -1,41 +1,39 @@
use super::axis_aligned_bounding_box::BoundingBox; use super::axis_aligned_bounding_box::BoundingBox;
use super::Interval; use super::Interval;
use crate::Real;
use nalgebra::{clamp, Point3}; use nalgebra::{clamp, Point3};
use itertools::izip; use itertools::izip;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct RealNormalizer<T: Real> { pub struct RealNormalizer {
min: T, min: f64,
range: T, range: f64,
} }
impl<T: Real> RealNormalizer<T> { impl RealNormalizer {
pub fn new(interval: Interval<T>) -> Self { pub fn new(interval: Interval) -> Self {
let min = interval.get_min(); let min = interval.get_min();
let range = interval.get_max() - min; let range = interval.get_max() - min;
Self { min, range } Self { min, range }
} }
pub fn normalize(&self, value: T) -> T { pub fn normalize(&self, value: f64) -> f64 {
(value - self.min) / self.range (value - self.min) / self.range
} }
pub fn normalize_and_clamp(&self, value: T) -> T { pub fn normalize_and_clamp(&self, value: f64) -> f64 {
clamp((value - self.min) / self.range, T::zero(), T::one()) clamp((value - self.min) / self.range, 0.0, 1.0)
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Point3Normalizer<T: Real> { pub struct Point3Normalizer {
dimension_normalizers: [RealNormalizer<T>; 3], dimension_normalizers: [RealNormalizer; 3],
} }
impl<T: Real> Point3Normalizer<T> { impl Point3Normalizer {
pub fn new(bounds: BoundingBox<T>) -> Self { pub fn new(bounds: BoundingBox) -> Self {
let mut normalizer = Point3Normalizer { let mut normalizer = Point3Normalizer {
dimension_normalizers: [RealNormalizer::new(Interval::empty()); 3], dimension_normalizers: [RealNormalizer::new(Interval::empty()); 3],
}; };
@ -49,8 +47,8 @@ impl<T: Real> Point3Normalizer<T> {
normalizer normalizer
} }
pub fn normalize(&self, point: Point3<T>) -> Point3<T> { pub fn normalize(&self, point: Point3<f64>) -> Point3<f64> {
let mut result = Point3::new(T::zero(), T::zero(), T::zero()); let mut result = Point3::new(0.0, 0.0, 0.0);
for (value_out, &value_in, normalizer) in izip!( for (value_out, &value_in, normalizer) in izip!(
result.iter_mut(), result.iter_mut(),
point.iter(), point.iter(),
@ -61,8 +59,8 @@ impl<T: Real> Point3Normalizer<T> {
result result
} }
pub fn normalize_and_clamp(&self, point: Point3<T>) -> Point3<T> { pub fn normalize_and_clamp(&self, point: Point3<f64>) -> Point3<f64> {
let mut result = Point3::new(T::zero(), T::zero(), T::zero()); let mut result = Point3::new(0.0, 0.0, 0.0);
for (value_out, &value_in, normalizer) in izip!( for (value_out, &value_in, normalizer) in izip!(
result.iter_mut(), result.iter_mut(),
point.iter(), point.iter(),

View File

@ -3,15 +3,14 @@ use nalgebra::{convert, Point3, Vector3};
use crate::materials::Material; use crate::materials::Material;
use crate::raycasting::Triangle; use crate::raycasting::Triangle;
use crate::Real;
use std::sync::Arc; use std::sync::Arc;
pub fn triangulate_polygon<T: Real>( pub fn triangulate_polygon(
vertices: &Vec<Point3<T>>, vertices: &Vec<Point3<f64>>,
normal: &Vector3<T>, normal: &Vector3<f64>,
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
) -> Vec<Triangle<T>> { ) -> Vec<Triangle> {
assert!(vertices.len() >= 3); assert!(vertices.len() >= 3);
let hinge = vertices[0]; let hinge = vertices[0];
izip!(vertices.iter().skip(1), vertices.iter().skip(2)) izip!(vertices.iter().skip(1), vertices.iter().skip(2))
@ -23,104 +22,102 @@ pub fn triangulate_polygon<T: Real>(
.collect() .collect()
} }
pub fn generate_dodecahedron<T: Real>( pub fn generate_dodecahedron(
centre: Point3<T>, centre: Point3<f64>,
size: T, size: f64,
material: Arc<dyn Material<T>>, material: Arc<dyn Material>,
) -> Vec<Triangle<T>> { ) -> Vec<Triangle> {
let phi = convert((1.0 + (5.0_f64).sqrt()) / 2.0); let phi = convert((1.0 + (5.0_f64).sqrt()) / 2.0);
let phi_inv = T::one() / phi; let phi_inv = 1.0 / phi;
let one = T::one();
let zero = T::zero();
let faces = vec![ let faces = vec![
vec![ vec![
Vector3::new(phi_inv, zero, phi), Vector3::new(phi_inv, 0.0, phi),
Vector3::new(-phi_inv, zero, phi), Vector3::new(-phi_inv, 0.0, phi),
Vector3::new(-one, -one, one), Vector3::new(-1.0, -1.0, 1.0),
Vector3::new(zero, -phi, phi_inv), Vector3::new(0.0, -phi, phi_inv),
Vector3::new(one, -one, one), Vector3::new(1.0, -1.0, 1.0),
], ],
vec![ vec![
Vector3::new(phi_inv, zero, phi), Vector3::new(phi_inv, 0.0, phi),
Vector3::new(-phi_inv, zero, phi), Vector3::new(-phi_inv, 0.0, phi),
Vector3::new(-one, one, one), Vector3::new(-1.0, 1.0, 1.0),
Vector3::new(zero, phi, phi_inv), Vector3::new(0.0, phi, phi_inv),
Vector3::new(one, one, one), Vector3::new(1.0, 1.0, 1.0),
], ],
vec![ vec![
Vector3::new(phi_inv, zero, phi), Vector3::new(phi_inv, 0.0, phi),
Vector3::new(one, -one, one), Vector3::new(1.0, -1.0, 1.0),
Vector3::new(phi, -phi_inv, zero), Vector3::new(phi, -phi_inv, 0.0),
Vector3::new(phi, phi_inv, zero), Vector3::new(phi, phi_inv, 0.0),
Vector3::new(one, one, one), Vector3::new(1.0, 1.0, 1.0),
], ],
vec![ vec![
Vector3::new(-phi_inv, zero, phi), Vector3::new(-phi_inv, 0.0, phi),
Vector3::new(-one, -one, one), Vector3::new(-1.0, -1.0, 1.0),
Vector3::new(-phi, -phi_inv, zero), Vector3::new(-phi, -phi_inv, 0.0),
Vector3::new(-phi, phi_inv, zero), Vector3::new(-phi, phi_inv, 0.0),
Vector3::new(-one, one, one), Vector3::new(-1.0, 1.0, 1.0),
], ],
vec![ vec![
Vector3::new(-one, -one, one), Vector3::new(-1.0, -1.0, 1.0),
Vector3::new(-phi, -phi_inv, zero), Vector3::new(-phi, -phi_inv, 0.0),
Vector3::new(-one, -one, -one), Vector3::new(-1.0, -1.0, -1.0),
Vector3::new(zero, -phi, -phi_inv), Vector3::new(0.0, -phi, -phi_inv),
Vector3::new(zero, -phi, phi_inv), Vector3::new(0.0, -phi, phi_inv),
], ],
vec![ vec![
Vector3::new(zero, -phi, phi_inv), Vector3::new(0.0, -phi, phi_inv),
Vector3::new(zero, -phi, -phi_inv), Vector3::new(0.0, -phi, -phi_inv),
Vector3::new(one, -one, -one), Vector3::new(1.0, -1.0, -1.0),
Vector3::new(phi, -phi_inv, zero), Vector3::new(phi, -phi_inv, 0.0),
Vector3::new(one, -one, one), Vector3::new(1.0, -1.0, 1.0),
], ],
vec![ vec![
Vector3::new(zero, phi, phi_inv), Vector3::new(0.0, phi, phi_inv),
Vector3::new(zero, phi, -phi_inv), Vector3::new(0.0, phi, -phi_inv),
Vector3::new(-one, one, -one), Vector3::new(-1.0, 1.0, -1.0),
Vector3::new(-phi, phi_inv, zero), Vector3::new(-phi, phi_inv, 0.0),
Vector3::new(-one, one, one), Vector3::new(-1.0, 1.0, 1.0),
], ],
vec![ vec![
Vector3::new(one, one, one), Vector3::new(1.0, 1.0, 1.0),
Vector3::new(phi, phi_inv, zero), Vector3::new(phi, phi_inv, 0.0),
Vector3::new(one, one, -one), Vector3::new(1.0, 1.0, -1.0),
Vector3::new(zero, phi, -phi_inv), Vector3::new(0.0, phi, -phi_inv),
Vector3::new(zero, phi, phi_inv), Vector3::new(0.0, phi, phi_inv),
], ],
vec![ vec![
Vector3::new(one, -one, -one), Vector3::new(1.0, -1.0, -1.0),
Vector3::new(zero, -phi, -phi_inv), Vector3::new(0.0, -phi, -phi_inv),
Vector3::new(-one, -one, -one), Vector3::new(-1.0, -1.0, -1.0),
Vector3::new(-phi_inv, zero, -phi), Vector3::new(-phi_inv, 0.0, -phi),
Vector3::new(phi_inv, zero, -phi), Vector3::new(phi_inv, 0.0, -phi),
], ],
vec![ vec![
Vector3::new(one, one, -one), Vector3::new(1.0, 1.0, -1.0),
Vector3::new(zero, phi, -phi_inv), Vector3::new(0.0, phi, -phi_inv),
Vector3::new(-one, one, -one), Vector3::new(-1.0, 1.0, -1.0),
Vector3::new(-phi_inv, zero, -phi), Vector3::new(-phi_inv, 0.0, -phi),
Vector3::new(phi_inv, zero, -phi), Vector3::new(phi_inv, 0.0, -phi),
], ],
vec![ vec![
Vector3::new(one, one, -one), Vector3::new(1.0, 1.0, -1.0),
Vector3::new(phi, phi_inv, zero), Vector3::new(phi, phi_inv, 0.0),
Vector3::new(phi, -phi_inv, zero), Vector3::new(phi, -phi_inv, 0.0),
Vector3::new(one, -one, -one), Vector3::new(1.0, -1.0, -1.0),
Vector3::new(phi_inv, zero, -phi), Vector3::new(phi_inv, 0.0, -phi),
], ],
vec![ vec![
Vector3::new(-one, one, -one), Vector3::new(-1.0, 1.0, -1.0),
Vector3::new(-phi, phi_inv, zero), Vector3::new(-phi, phi_inv, 0.0),
Vector3::new(-phi, -phi_inv, zero), Vector3::new(-phi, -phi_inv, 0.0),
Vector3::new(-one, -one, -one), Vector3::new(-1.0, -1.0, -1.0),
Vector3::new(-phi_inv, zero, -phi), Vector3::new(-phi_inv, 0.0, -phi),
], ],
]; ];
let scale = size * convert(3f64.sqrt() / 2.0); let scale = size * 3f64.sqrt() / 2.0;
faces faces
.iter() .iter()
.flat_map(|face| { .flat_map(|face| {