Change a bunch of generics that used with RealType to just use f64
This commit is contained in:
parent
a98c0c4bca
commit
99cf127c9f
|
|
@ -8,25 +8,23 @@ use super::sampler::Sampler;
|
|||
use super::scene::Scene;
|
||||
use super::util::Tile;
|
||||
|
||||
use crate::Real;
|
||||
|
||||
struct ImageSampler<T: Real> {
|
||||
struct ImageSampler {
|
||||
image_height_pixels: usize,
|
||||
image_width_pixels: usize,
|
||||
|
||||
film_width: T,
|
||||
film_height: T,
|
||||
film_width: f64,
|
||||
film_height: f64,
|
||||
|
||||
camera_location: Point3<T>,
|
||||
film_distance: T,
|
||||
camera_location: Point3<f64>,
|
||||
film_distance: f64,
|
||||
}
|
||||
|
||||
impl<T: Real> ImageSampler<T> {
|
||||
pub fn new(width: usize, height: usize, camera_location: Point3<T>) -> ImageSampler<T> {
|
||||
impl ImageSampler {
|
||||
pub fn new(width: usize, height: usize, camera_location: Point3<f64>) -> ImageSampler {
|
||||
let (film_width, film_height) = {
|
||||
let width: T = convert(width as f64);
|
||||
let height: T = convert(height as f64);
|
||||
let film_size: T = convert(1.0);
|
||||
let width = width as f64;
|
||||
let height = height as f64;
|
||||
let film_size = 1.0;
|
||||
if width > height {
|
||||
(width / height, film_size)
|
||||
} else {
|
||||
|
|
@ -43,22 +41,21 @@ impl<T: Real> ImageSampler<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn scale(i: usize, n: usize, l: T) -> T {
|
||||
let one: T = convert(1.0);
|
||||
let n: T = convert(n as f64);
|
||||
let i: T = convert(i as f64);
|
||||
let pixel_size: T = l * (one / n);
|
||||
(i + convert(0.5)) * pixel_size
|
||||
fn scale(i: usize, n: usize, l: f64) -> f64 {
|
||||
let n = n as f64;
|
||||
let i = i as f64;
|
||||
let pixel_size = l * (1.0 / n);
|
||||
(i + 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(
|
||||
self.camera_location,
|
||||
Vector3::new(
|
||||
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.film_height * convert(0.5),
|
||||
- self.film_height * 0.5,
|
||||
self.film_distance,
|
||||
),
|
||||
)
|
||||
|
|
@ -82,7 +79,7 @@ const RECURSION_LIMIT: u16 = 32;
|
|||
/// # use vanrijn::scene::Scene;
|
||||
/// # use vanrijn::util::TileIterator;
|
||||
/// # 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_height = 480;
|
||||
/// let time_size = 32;
|
||||
|
|
@ -91,19 +88,14 @@ const RECURSION_LIMIT: u16 = 32;
|
|||
/// // display and/or save tile_image
|
||||
/// }
|
||||
/// ```
|
||||
pub fn partial_render_scene<T: Real>(
|
||||
scene: &Scene<T>,
|
||||
tile: Tile,
|
||||
height: usize,
|
||||
width: usize,
|
||||
) -> ImageRgbF<T> {
|
||||
pub fn partial_render_scene(scene: &Scene, tile: Tile, height: usize, width: usize) -> ImageRgbF {
|
||||
let mut output_image_tile = ImageRgbF::new(tile.width(), tile.height());
|
||||
let image_sampler = ImageSampler::new(width, height, scene.camera_location);
|
||||
let ambient_intensity: T = convert(0.0);
|
||||
let directional_intensity1: T = convert(7.0);
|
||||
let directional_intensity2: T = convert(3.0);
|
||||
let directional_intensity3: T = convert(2.0);
|
||||
let integrator = WhittedIntegrator::<T> {
|
||||
let ambient_intensity = 0.0;
|
||||
let directional_intensity1 = 7.0;
|
||||
let directional_intensity2 = 3.0;
|
||||
let directional_intensity3 = 2.0;
|
||||
let integrator = WhittedIntegrator {
|
||||
ambient_light: ColourRgbF::from_named(NamedColour::White) * ambient_intensity,
|
||||
lights: vec![
|
||||
DirectionalLight {
|
||||
|
|
@ -167,7 +159,7 @@ mod tests {
|
|||
let film_plane = Plane::new(
|
||||
Vector3::new(0.0, 0.0, 1.0),
|
||||
target.film_distance,
|
||||
Arc::new(LambertianMaterial::<f64>::new_dummy()),
|
||||
Arc::new(LambertianMaterial::new_dummy()),
|
||||
);
|
||||
let point_on_film_plane = match film_plane.intersect(&ray) {
|
||||
Some(IntersectionInfo {
|
||||
|
|
|
|||
101
src/colour.rs
101
src/colour.rs
|
|
@ -1,61 +1,56 @@
|
|||
use nalgebra::{convert, Vector3};
|
||||
|
||||
use crate::Real;
|
||||
use nalgebra::Vector3;
|
||||
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ColourRgbF<T: Real> {
|
||||
values: Vector3<T>,
|
||||
pub struct ColourRgbF {
|
||||
values: Vector3<f64>,
|
||||
}
|
||||
|
||||
impl<T: Real> ColourRgbF<T> {
|
||||
pub fn new(red: T, green: T, blue: T) -> ColourRgbF<T> {
|
||||
impl ColourRgbF {
|
||||
pub fn new(red: f64, green: f64, blue: f64) -> ColourRgbF {
|
||||
ColourRgbF {
|
||||
values: Vector3::new(red, green, blue),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_named(name: NamedColour) -> ColourRgbF<T> {
|
||||
let zero: T = convert(0.0);
|
||||
let half: T = convert(0.5);
|
||||
let one: T = convert(1.0);
|
||||
pub fn from_named(name: NamedColour) -> ColourRgbF {
|
||||
match name {
|
||||
NamedColour::Black => ColourRgbF::new(zero, zero, zero),
|
||||
NamedColour::White => ColourRgbF::new(one, one, one),
|
||||
NamedColour::Red => ColourRgbF::new(one, zero, zero),
|
||||
NamedColour::Lime => ColourRgbF::new(zero, one, zero),
|
||||
NamedColour::Blue => ColourRgbF::new(zero, zero, one),
|
||||
NamedColour::Yellow => ColourRgbF::new(one, one, zero),
|
||||
NamedColour::Cyan => ColourRgbF::new(zero, one, one),
|
||||
NamedColour::Magenta => ColourRgbF::new(one, zero, one),
|
||||
NamedColour::Gray => ColourRgbF::new(half, half, half),
|
||||
NamedColour::Maroon => ColourRgbF::new(half, zero, zero),
|
||||
NamedColour::Olive => ColourRgbF::new(half, half, zero),
|
||||
NamedColour::Green => ColourRgbF::new(zero, half, zero),
|
||||
NamedColour::Purple => ColourRgbF::new(half, zero, half),
|
||||
NamedColour::Teal => ColourRgbF::new(zero, half, half),
|
||||
NamedColour::Navy => ColourRgbF::new(zero, zero, half),
|
||||
NamedColour::Black => ColourRgbF::new(0.0, 0.0, 0.0),
|
||||
NamedColour::White => ColourRgbF::new(1.0, 1.0, 1.0),
|
||||
NamedColour::Red => ColourRgbF::new(1.0, 0.0, 0.0),
|
||||
NamedColour::Lime => ColourRgbF::new(0.0, 1.0, 0.0),
|
||||
NamedColour::Blue => ColourRgbF::new(0.0, 0.0, 1.0),
|
||||
NamedColour::Yellow => ColourRgbF::new(1.0, 1.0, 0.0),
|
||||
NamedColour::Cyan => ColourRgbF::new(0.0, 1.0, 1.0),
|
||||
NamedColour::Magenta => ColourRgbF::new(1.0, 0.0, 1.0),
|
||||
NamedColour::Gray => ColourRgbF::new(0.5, 0.5, 0.5),
|
||||
NamedColour::Maroon => ColourRgbF::new(0.5, 0.0, 0.0),
|
||||
NamedColour::Olive => ColourRgbF::new(0.5, 0.5, 0.0),
|
||||
NamedColour::Green => ColourRgbF::new(0.0, 0.5, 0.0),
|
||||
NamedColour::Purple => ColourRgbF::new(0.5, 0.0, 0.5),
|
||||
NamedColour::Teal => ColourRgbF::new(0.0, 0.5, 0.5),
|
||||
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 }
|
||||
}
|
||||
|
||||
pub fn red(&self) -> T {
|
||||
pub fn red(&self) -> f64 {
|
||||
self.values[0]
|
||||
}
|
||||
|
||||
pub fn green(&self) -> T {
|
||||
pub fn green(&self) -> f64 {
|
||||
self.values[1]
|
||||
}
|
||||
|
||||
pub fn blue(&self) -> T {
|
||||
pub fn blue(&self) -> f64 {
|
||||
self.values[2]
|
||||
}
|
||||
|
||||
pub fn as_vector3(&self) -> &Vector3<T> {
|
||||
pub fn as_vector3(&self) -> &Vector3<f64> {
|
||||
&self.values
|
||||
}
|
||||
}
|
||||
|
|
@ -82,27 +77,27 @@ pub enum NamedColour {
|
|||
Navy,
|
||||
}
|
||||
|
||||
impl<T: Real> Add<ColourRgbF<T>> for ColourRgbF<T> {
|
||||
type Output = ColourRgbF<T>;
|
||||
fn add(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> {
|
||||
impl Add<ColourRgbF> for ColourRgbF {
|
||||
type Output = ColourRgbF;
|
||||
fn add(self, rhs: ColourRgbF) -> ColourRgbF {
|
||||
ColourRgbF {
|
||||
values: self.values + rhs.values,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Mul<T> for ColourRgbF<T> {
|
||||
type Output = ColourRgbF<T>;
|
||||
fn mul(self, rhs: T) -> ColourRgbF<T> {
|
||||
impl Mul<f64> for ColourRgbF {
|
||||
type Output = ColourRgbF;
|
||||
fn mul(self, rhs: f64) -> ColourRgbF {
|
||||
ColourRgbF {
|
||||
values: self.values * rhs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Mul<ColourRgbF<T>> for ColourRgbF<T> {
|
||||
type Output = ColourRgbF<T>;
|
||||
fn mul(self, rhs: ColourRgbF<T>) -> ColourRgbF<T> {
|
||||
impl Mul<ColourRgbF> for ColourRgbF {
|
||||
type Output = ColourRgbF;
|
||||
fn mul(self, rhs: ColourRgbF) -> ColourRgbF {
|
||||
ColourRgbF {
|
||||
values: self.values.component_mul(&rhs.values),
|
||||
}
|
||||
|
|
@ -117,9 +112,9 @@ mod tests {
|
|||
use super::*;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
use quickcheck_macros::quickcheck;
|
||||
impl<T: Arbitrary + Real> Arbitrary for ColourRgbF<T> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF<T> {
|
||||
let values = <Vector3<T> as Arbitrary>::arbitrary(g);
|
||||
impl Arbitrary for ColourRgbF {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> ColourRgbF {
|
||||
let values = <Vector3<f64> as Arbitrary>::arbitrary(g);
|
||||
ColourRgbF { values }
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +135,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[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;
|
||||
assert!(target.red() == 0.0);
|
||||
assert!(target.green() == 0.0);
|
||||
|
|
@ -148,17 +143,14 @@ mod tests {
|
|||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn red_channel_multiplied_by_scalar_yields_correct_result(
|
||||
colour: ColourRgbF<f64>,
|
||||
scalar: f64,
|
||||
) {
|
||||
fn red_channel_multiplied_by_scalar_yields_correct_result(colour: ColourRgbF, scalar: f64) {
|
||||
let target = colour * scalar;
|
||||
assert!(target.red() == colour.red() * scalar);
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn green_channel_multiplied_by_scalar_yields_correct_result(
|
||||
colour: ColourRgbF<f64>,
|
||||
colour: ColourRgbF,
|
||||
scalar: f64,
|
||||
) {
|
||||
let target = colour * scalar;
|
||||
|
|
@ -167,7 +159,7 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn blue_channel_multiplied_by_scalar_yields_correct_result(
|
||||
colour: ColourRgbF<f64>,
|
||||
colour: ColourRgbF,
|
||||
scalar: f64,
|
||||
) {
|
||||
let target = colour * scalar;
|
||||
|
|
@ -175,10 +167,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn adding_colourrgbf_adds_individual_channels(
|
||||
colour1: ColourRgbF<f64>,
|
||||
colour2: ColourRgbF<f64>,
|
||||
) {
|
||||
fn adding_colourrgbf_adds_individual_channels(colour1: ColourRgbF, colour2: ColourRgbF) {
|
||||
let target = colour1 + colour2;
|
||||
assert!(target.red() == colour1.red() + colour2.red());
|
||||
assert!(target.green() == colour1.green() + colour2.green());
|
||||
|
|
@ -187,8 +176,8 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn multiplying_colourrgbf_adds_individual_channels(
|
||||
colour1: ColourRgbF<f64>,
|
||||
colour2: ColourRgbF<f64>,
|
||||
colour1: ColourRgbF,
|
||||
colour2: ColourRgbF,
|
||||
) {
|
||||
let target = colour1 * colour2;
|
||||
assert!(target.red() == colour1.red() * colour2.red());
|
||||
|
|
|
|||
32
src/image.rs
32
src/image.rs
|
|
@ -7,8 +7,6 @@ use nalgebra::{clamp, convert, Vector3};
|
|||
|
||||
use super::colour::{ColourRgbF, ColourRgbU8};
|
||||
|
||||
use crate::Real;
|
||||
|
||||
pub struct ImageRgbU8 {
|
||||
pixel_data: Vec<u8>,
|
||||
width: usize,
|
||||
|
|
@ -95,14 +93,14 @@ impl ImageRgbU8 {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ImageRgbF<T: Real> {
|
||||
pixel_data: Vec<T>,
|
||||
pub struct ImageRgbF {
|
||||
pixel_data: Vec<f64>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
impl<T: Real> ImageRgbF<T> {
|
||||
pub fn new(width: usize, height: usize) -> ImageRgbF<T> {
|
||||
impl ImageRgbF {
|
||||
pub fn new(width: usize, height: usize) -> ImageRgbF {
|
||||
ImageRgbF {
|
||||
width,
|
||||
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() {
|
||||
*elem = T::zero();
|
||||
*elem = 0.0;
|
||||
}
|
||||
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);
|
||||
let index = self.calculate_index(row, column);
|
||||
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);
|
||||
let index = self.calculate_index(row, column);
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -176,21 +174,21 @@ impl NormalizedAsByte for f64 {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToneMapper<T: Real> {
|
||||
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8);
|
||||
pub trait ToneMapper {
|
||||
fn apply_tone_mapping(&self, image_in: &ImageRgbF, image_out: &mut ImageRgbU8);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ClampingToneMapper {}
|
||||
|
||||
impl ClampingToneMapper {
|
||||
fn clamp<T: Real + NormalizedAsByte>(v: &T) -> u8 {
|
||||
clamp(v, &T::zero(), &T::one()).normalized_to_byte()
|
||||
fn clamp(v: &f64) -> u8 {
|
||||
clamp(v, &0.0, &1.0).normalized_to_byte()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real + NormalizedAsByte> ToneMapper<T> for ClampingToneMapper {
|
||||
fn apply_tone_mapping(&self, image_in: &ImageRgbF<T>, image_out: &mut ImageRgbU8) {
|
||||
impl ToneMapper for ClampingToneMapper {
|
||||
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_height() == image_out.get_height());
|
||||
for column in 0..image_in.get_width() {
|
||||
|
|
|
|||
|
|
@ -5,36 +5,34 @@ use super::raycasting::{IntersectionInfo, Ray};
|
|||
use super::sampler::Sampler;
|
||||
use super::util::algebra_utils::try_change_of_basis_matrix;
|
||||
|
||||
use crate::Real;
|
||||
|
||||
pub trait Integrator<T: Real> {
|
||||
pub trait Integrator {
|
||||
fn integrate(
|
||||
&self,
|
||||
sampler: &Sampler<T>,
|
||||
info: &IntersectionInfo<T>,
|
||||
sampler: &Sampler,
|
||||
info: &IntersectionInfo,
|
||||
recursion_limit: u16,
|
||||
) -> ColourRgbF<T>;
|
||||
) -> ColourRgbF;
|
||||
}
|
||||
|
||||
pub struct DirectionalLight<T: Real> {
|
||||
pub direction: Vector3<T>,
|
||||
pub colour: ColourRgbF<T>,
|
||||
pub struct DirectionalLight {
|
||||
pub direction: Vector3<f64>,
|
||||
pub colour: ColourRgbF,
|
||||
}
|
||||
|
||||
pub struct WhittedIntegrator<T: Real> {
|
||||
pub ambient_light: ColourRgbF<T>,
|
||||
pub lights: Vec<DirectionalLight<T>>,
|
||||
pub struct WhittedIntegrator {
|
||||
pub ambient_light: ColourRgbF,
|
||||
pub lights: Vec<DirectionalLight>,
|
||||
}
|
||||
|
||||
// TODO: Get rid of the magic bias number, which should be calculated base on expected error
|
||||
// bounds and tangent direction
|
||||
impl<T: Real> Integrator<T> for WhittedIntegrator<T> {
|
||||
impl Integrator for WhittedIntegrator {
|
||||
fn integrate(
|
||||
&self,
|
||||
sampler: &Sampler<T>,
|
||||
info: &IntersectionInfo<T>,
|
||||
sampler: &Sampler,
|
||||
info: &IntersectionInfo,
|
||||
recursion_limit: u16,
|
||||
) -> ColourRgbF<T> {
|
||||
) -> ColourRgbF {
|
||||
let world_to_bsdf_space =
|
||||
try_change_of_basis_matrix(&info.tangent, &info.cotangent, &info.normal)
|
||||
.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()
|
||||
} 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),
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,5 +15,3 @@ pub mod scene;
|
|||
pub mod util;
|
||||
|
||||
pub use camera::partial_render_scene;
|
||||
|
||||
use realtype::Real;
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}),
|
||||
)?;
|
||||
println!("Building BVH...");
|
||||
let model_bvh: Box<dyn Aggregate<_>> =
|
||||
let model_bvh: Box<dyn Aggregate> =
|
||||
Box::new(BoundingVolumeHierarchy::build(model_object.as_mut_slice()));
|
||||
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),
|
||||
diffuse_strength: 0.1,
|
||||
}),
|
||||
)) as Box<dyn Primitive<f64>>,
|
||||
)) as Box<dyn Primitive>,
|
||||
Box::new(Sphere::new(
|
||||
Point3::new(-6.25, -0.5, 1.0),
|
||||
1.0,
|
||||
|
|
@ -181,7 +181,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
specular_strength: 1.0,
|
||||
}),
|
||||
)),
|
||||
]) as Box<dyn Aggregate<f64>>,
|
||||
]) as Box<dyn Aggregate>,
|
||||
model_bvh,
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,32 +1,31 @@
|
|||
use nalgebra::Vector3;
|
||||
|
||||
use crate::colour::ColourRgbF;
|
||||
use crate::Real;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LambertianMaterial<T: Real> {
|
||||
pub colour: ColourRgbF<T>,
|
||||
pub diffuse_strength: T,
|
||||
pub struct LambertianMaterial {
|
||||
pub colour: ColourRgbF,
|
||||
pub diffuse_strength: f64,
|
||||
}
|
||||
|
||||
impl<T: Real> LambertianMaterial<T> {
|
||||
pub fn new_dummy() -> LambertianMaterial<T> {
|
||||
impl LambertianMaterial {
|
||||
pub fn new_dummy() -> LambertianMaterial {
|
||||
LambertianMaterial {
|
||||
colour: ColourRgbF::new(T::one(), T::one(), T::one()),
|
||||
diffuse_strength: T::one(),
|
||||
colour: ColourRgbF::new(1.0, 1.0, 1.0),
|
||||
diffuse_strength: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Material<T> for LambertianMaterial<T> {
|
||||
fn bsdf(&self) -> Bsdf<T> {
|
||||
impl Material for LambertianMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let colour = self.colour * self.diffuse_strength;
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use nalgebra::Vector3;
|
||||
|
||||
use super::colour::ColourRgbF;
|
||||
use crate::Real;
|
||||
|
||||
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 use lambertian_material::LambertianMaterial;
|
||||
|
|
@ -19,10 +18,10 @@ pub use reflective_material::ReflectiveMaterial;
|
|||
pub mod rgb_sampled_bsdf_material;
|
||||
pub use rgb_sampled_bsdf_material::RgbSampledBsdfMaterial;
|
||||
|
||||
pub trait Material<T: Real>: Debug + Sync + Send {
|
||||
fn bsdf(&self) -> Bsdf<T>;
|
||||
pub trait Material: Debug + Sync + Send {
|
||||
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![]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,27 @@
|
|||
use nalgebra::Vector3;
|
||||
|
||||
use crate::colour::{ColourRgbF, NamedColour};
|
||||
use crate::Real;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PhongMaterial<T: Real> {
|
||||
pub colour: ColourRgbF<T>,
|
||||
pub diffuse_strength: T,
|
||||
pub specular_strength: T,
|
||||
pub smoothness: T,
|
||||
pub struct PhongMaterial {
|
||||
pub colour: ColourRgbF,
|
||||
pub diffuse_strength: f64,
|
||||
pub specular_strength: f64,
|
||||
pub smoothness: f64,
|
||||
}
|
||||
|
||||
impl<T: Real> Material<T> for PhongMaterial<T> {
|
||||
fn bsdf(&self) -> Bsdf<T> {
|
||||
impl Material for PhongMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let smoothness = self.smoothness;
|
||||
let specular_strength = self.specular_strength;
|
||||
let colour = self.colour * self.diffuse_strength;
|
||||
Box::new(
|
||||
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| {
|
||||
if w_i.z < T::zero() || w_o.z < T::zero() {
|
||||
move |w_o: Vector3<f64>, w_i: Vector3<f64>, colour_in: ColourRgbF| {
|
||||
if w_i.z < 0.0 || w_o.z < 0.0 {
|
||||
ColourRgbF::from_vector3(&Vector3::zeros())
|
||||
} else {
|
||||
let reflection_vector = Vector3::new(-w_i.x, -w_i.y, w_i.z);
|
||||
|
|
|
|||
|
|
@ -1,47 +1,44 @@
|
|||
use nalgebra::{clamp, convert, Vector3};
|
||||
use nalgebra::{clamp, Vector3};
|
||||
|
||||
use crate::colour::ColourRgbF;
|
||||
use crate::Real;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
#[derive(Debug)]
|
||||
pub struct ReflectiveMaterial<T: Real> {
|
||||
pub colour: ColourRgbF<T>,
|
||||
pub diffuse_strength: T,
|
||||
pub reflection_strength: T,
|
||||
pub struct ReflectiveMaterial {
|
||||
pub colour: ColourRgbF,
|
||||
pub diffuse_strength: f64,
|
||||
pub reflection_strength: f64,
|
||||
}
|
||||
|
||||
impl<T: Real> Material<T> for ReflectiveMaterial<T> {
|
||||
fn bsdf(&self) -> Bsdf<T> {
|
||||
impl Material for ReflectiveMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let diffuse_colour_factor = self.colour * self.diffuse_strength;
|
||||
let reflection_strength = self.reflection_strength;
|
||||
Box::new(
|
||||
move |w_o: Vector3<T>, w_i: Vector3<T>, colour_in: ColourRgbF<T>| {
|
||||
if w_i.z <= T::zero() || w_o.z <= T::zero() {
|
||||
ColourRgbF::new(T::zero(), T::zero(), T::zero())
|
||||
move |w_o: Vector3<f64>, w_i: Vector3<f64>, colour_in: ColourRgbF| {
|
||||
if w_i.z <= 0.0 || w_o.z <= 0.0 {
|
||||
ColourRgbF::new(0.0, 0.0, 0.0)
|
||||
} else {
|
||||
let reflection_vector = Vector3::new(-w_o.x, -w_o.y, w_o.z);
|
||||
let reflection_colour = colour_in * reflection_strength;
|
||||
let diffuse_colour = diffuse_colour_factor * colour_in;
|
||||
let sigma: T = convert(0.05);
|
||||
let two: T = convert(2.0);
|
||||
let sigma = 0.05;
|
||||
let two = 2.0;
|
||||
// These are normalized vectors, but sometimes rounding errors cause the
|
||||
// dot product to be slightly above 1 or below 0. The call to clamp
|
||||
// ensures the values stay within the domain of acos,
|
||||
let theta = clamp(w_i.dot(&reflection_vector), T::zero(), T::one())
|
||||
.abs()
|
||||
.acos();
|
||||
let theta = clamp(w_i.dot(&reflection_vector), 0.0, 1.0).abs().acos();
|
||||
let reflection_factor = (-(theta.powf(two)) / (two * sigma * sigma)).exp();
|
||||
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)]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use nalgebra::{convert, Vector3};
|
||||
use nalgebra::Vector3;
|
||||
|
||||
use super::{Bsdf, Material};
|
||||
use crate::colour::ColourRgbF;
|
||||
use crate::Real;
|
||||
use crate::realtype::NormalizedToU32;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
|
|
@ -12,8 +12,8 @@ use std::sync::Arc;
|
|||
use std::f64::consts::{FRAC_PI_2, PI};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RgbSampledBsdfMaterial<T: Real> {
|
||||
lut: Arc<Vec<Vec<Vec<Vec<Vector3<T>>>>>>,
|
||||
pub struct RgbSampledBsdfMaterial {
|
||||
lut: Arc<Vec<Vec<Vec<Vec<Vector3<f64>>>>>>,
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
impl<T: Real> RgbSampledBsdfMaterial<T> {
|
||||
pub fn from_csv_file(filename: &str) -> Result<RgbSampledBsdfMaterial<T>, Box<dyn Error>> {
|
||||
impl RgbSampledBsdfMaterial {
|
||||
pub fn from_csv_file(filename: &str) -> Result<RgbSampledBsdfMaterial, Box<dyn Error>> {
|
||||
let csv_file = File::open(filename)?;
|
||||
let mut reader = csv::Reader::from_reader(BufReader::new(&csv_file));
|
||||
let mut lut = Vec::new();
|
||||
|
|
@ -49,28 +49,28 @@ impl<T: Real> RgbSampledBsdfMaterial<T> {
|
|||
),
|
||||
phi_out_index,
|
||||
Vector3::zeros(),
|
||||
) = Vector3::new(convert(red), convert(green), convert(blue));
|
||||
) = Vector3::new(red, green, blue);
|
||||
}
|
||||
let lut = Arc::new(lut);
|
||||
Ok(RgbSampledBsdfMaterial { lut })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Real> Material<T> for RgbSampledBsdfMaterial<T> {
|
||||
fn bsdf(&self) -> Bsdf<T> {
|
||||
impl<'a> Material for RgbSampledBsdfMaterial {
|
||||
fn bsdf(&self) -> Bsdf {
|
||||
let lut = Arc::clone(&self.lut);
|
||||
Box::new(move |w_in, w_out, colour_in| {
|
||||
if w_in.z < T::zero() || w_out.z < T::zero() {
|
||||
return ColourRgbF::new(T::zero(), T::zero(), T::zero());
|
||||
if w_in.z < 0.0 || w_out.z < 0.0 {
|
||||
return ColourRgbF::new(0.0, 0.0, 0.0);
|
||||
}
|
||||
let theta_in = w_in.z.acos();
|
||||
let theta_in_index = (theta_in / convert(FRAC_PI_2)).normalized_to_u32(4) as usize;
|
||||
let phi_in = w_in.y.atan2(w_in.x) + convert(PI);
|
||||
let phi_in_index = (phi_in / convert(2.0 * PI)).normalized_to_u32(6) 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) + PI;
|
||||
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_index = (theta_out / convert(FRAC_PI_2)).normalized_to_u32(4) as usize;
|
||||
let phi_out = w_out.y.atan2(w_out.x) + convert(PI);
|
||||
let phi_out_index = (phi_out / convert(2.0 * PI)).normalized_to_u32(6) 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) + PI;
|
||||
let phi_out_index = (phi_out / (2.0 * PI)).normalized_to_u32(6) as usize;
|
||||
ColourRgbF::from_vector3(
|
||||
&colour_in.as_vector3().component_mul(
|
||||
&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)]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
src/mesh.rs
31
src/mesh.rs
|
|
@ -1,11 +1,9 @@
|
|||
/// Load a model from a Wavefront .obj file
|
||||
mod wavefront_obj {
|
||||
use crate::materials::Material;
|
||||
use crate::Real;
|
||||
|
||||
use crate::raycasting::{Primitive, Triangle};
|
||||
|
||||
use alga::general::SupersetOf;
|
||||
use nalgebra::{convert, Point3, Vector3};
|
||||
use obj::{IndexTuple, Obj, SimplePolygon};
|
||||
|
||||
|
|
@ -13,16 +11,13 @@ mod wavefront_obj {
|
|||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn get_vertex_and_normal<T: Real>(
|
||||
fn get_vertex_and_normal(
|
||||
index_tuple: &IndexTuple,
|
||||
vertex_positions: &[[f32; 3]],
|
||||
normal_positions: &[[f32; 3]],
|
||||
) -> (Point3<T>, Vector3<T>)
|
||||
where
|
||||
T: SupersetOf<f32>,
|
||||
{
|
||||
) -> (Point3<f64>, Vector3<f64>) {
|
||||
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 {
|
||||
Some(normal_index) => convert(Vector3::from_row_slice(&normal_positions[normal_index])),
|
||||
None => Vector3::zeros(),
|
||||
|
|
@ -30,15 +25,12 @@ mod wavefront_obj {
|
|||
(vertex, normal.normalize())
|
||||
}
|
||||
|
||||
fn get_triangles<T: Real>(
|
||||
fn get_triangles(
|
||||
polygon: &SimplePolygon,
|
||||
vertex_positions: &[[f32; 3]],
|
||||
normal_positions: &[[f32; 3]],
|
||||
material: Arc<dyn Material<T>>,
|
||||
) -> Vec<Triangle<T>>
|
||||
where
|
||||
T: SupersetOf<f32>,
|
||||
{
|
||||
material: Arc<dyn Material>,
|
||||
) -> Vec<Triangle> {
|
||||
if let Some(v0_index) = polygon.iter().next() {
|
||||
let (v0_vertex, v0_normal) =
|
||||
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,
|
||||
material: Arc<dyn Material<T>>,
|
||||
) -> Result<Vec<Arc<dyn Primitive<T>>>>
|
||||
where
|
||||
T: SupersetOf<f32>,
|
||||
{
|
||||
material: Arc<dyn Material>,
|
||||
) -> Result<Vec<Arc<dyn Primitive>>> {
|
||||
let obj = Obj::<SimplePolygon>::load(filename)?;
|
||||
|
||||
Ok(obj
|
||||
|
|
@ -80,7 +69,7 @@ mod wavefront_obj {
|
|||
.flat_map(|object| object.groups.iter())
|
||||
.flat_map(|group| group.polys.iter())
|
||||
.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())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use crate::Real;
|
||||
|
||||
use crate::util::Interval;
|
||||
|
||||
use super::{IntersectP, Ray};
|
||||
|
|
@ -8,8 +6,8 @@ use itertools::izip;
|
|||
|
||||
pub use crate::util::axis_aligned_bounding_box::BoundingBox;
|
||||
|
||||
impl<T: Real> IntersectP<T> for BoundingBox<T> {
|
||||
fn intersect(&self, ray: &Ray<T>) -> bool {
|
||||
impl IntersectP for BoundingBox {
|
||||
fn intersect(&self, ray: &Ray) -> bool {
|
||||
let mut t_interval_in_bounds = Interval::infinite();
|
||||
for (&ray_origin, &ray_direction, bounds) in
|
||||
izip!(ray.origin.iter(), ray.direction.iter(), self.bounds.iter())
|
||||
|
|
@ -35,7 +33,7 @@ mod tests {
|
|||
|
||||
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 range = interval.get_max() - interval.get_min();
|
||||
let multiple_of_range = distance_from_start / range;
|
||||
|
|
@ -48,7 +46,7 @@ mod tests {
|
|||
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(
|
||||
point
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ use super::{
|
|||
Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectP, IntersectionInfo, Primitive, Ray,
|
||||
};
|
||||
|
||||
use crate::Real;
|
||||
|
||||
use nalgebra::{convert, Point3};
|
||||
use nalgebra::Point3;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
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
|
||||
/// doesn't intersect the [BoundingBox](BoundingBox) of the node doesn't intersect any of
|
||||
/// the primitives stored in it's children.
|
||||
pub enum BoundingVolumeHierarchy<T: Real> {
|
||||
pub enum BoundingVolumeHierarchy {
|
||||
Node {
|
||||
bounds: BoundingBox<T>,
|
||||
left: Box<BoundingVolumeHierarchy<T>>,
|
||||
right: Box<BoundingVolumeHierarchy<T>>,
|
||||
bounds: BoundingBox,
|
||||
left: Box<BoundingVolumeHierarchy>,
|
||||
right: Box<BoundingVolumeHierarchy>,
|
||||
},
|
||||
Leaf {
|
||||
bounds: BoundingBox<T>,
|
||||
primitives: Vec<Arc<dyn Primitive<T>>>,
|
||||
bounds: BoundingBox,
|
||||
primitives: Vec<Arc<dyn Primitive>>,
|
||||
},
|
||||
}
|
||||
|
||||
fn centre<T: Real>(bounds: &BoundingBox<T>) -> Point3<T> {
|
||||
let two = convert(2.0);
|
||||
fn centre(bounds: &BoundingBox) -> Point3<f64> {
|
||||
Point3::new(
|
||||
(bounds.bounds[0].get_min() + bounds.bounds[0].get_max()) / two,
|
||||
(bounds.bounds[1].get_min() + bounds.bounds[1].get_max()) / two,
|
||||
(bounds.bounds[2].get_min() + bounds.bounds[2].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()) / 2.0,
|
||||
(bounds.bounds[2].get_min() + bounds.bounds[2].get_max()) / 2.0,
|
||||
)
|
||||
}
|
||||
|
||||
fn heuristic_split<T: Real>(
|
||||
primitives: &mut [Arc<dyn Primitive<T>>],
|
||||
bounds: &BoundingBox<T>,
|
||||
) -> usize {
|
||||
fn heuristic_split(primitives: &mut [Arc<dyn Primitive>], bounds: &BoundingBox) -> usize {
|
||||
let largest_dimension = bounds.largest_dimension();
|
||||
primitives.sort_unstable_by(|a, b| {
|
||||
centre(&a.bounding_box())[largest_dimension]
|
||||
|
|
@ -51,12 +45,12 @@ fn heuristic_split<T: Real>(
|
|||
primitives.len() / 2
|
||||
}
|
||||
|
||||
impl<T: Real> BoundingVolumeHierarchy<T> {
|
||||
pub fn build(primitives: &mut [Arc<dyn Primitive<T>>]) -> Self {
|
||||
impl BoundingVolumeHierarchy {
|
||||
pub fn build(primitives: &mut [Arc<dyn Primitive>]) -> Self {
|
||||
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
|
||||
.iter()
|
||||
.fold(BoundingBox::empty(), |acc, p| acc.union(&p.bounding_box()));
|
||||
|
|
@ -80,10 +74,10 @@ impl<T: Real> BoundingVolumeHierarchy<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn closest_intersection<T: Real>(
|
||||
a: Option<IntersectionInfo<T>>,
|
||||
b: Option<IntersectionInfo<T>>,
|
||||
) -> Option<IntersectionInfo<T>> {
|
||||
fn closest_intersection(
|
||||
a: Option<IntersectionInfo>,
|
||||
b: Option<IntersectionInfo>,
|
||||
) -> Option<IntersectionInfo> {
|
||||
match a {
|
||||
None => b,
|
||||
Some(a_info) => match b {
|
||||
|
|
@ -97,8 +91,8 @@ fn closest_intersection<T: Real>(
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Intersect<T> for BoundingVolumeHierarchy<T> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
impl Intersect for BoundingVolumeHierarchy {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
match self {
|
||||
BoundingVolumeHierarchy::Node {
|
||||
bounds,
|
||||
|
|
@ -125,13 +119,13 @@ impl<T: Real> Intersect<T> for BoundingVolumeHierarchy<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Real> HasBoundingBox<T> for BoundingVolumeHierarchy<T> {
|
||||
fn bounding_box(&self) -> BoundingBox<T> {
|
||||
impl HasBoundingBox for BoundingVolumeHierarchy {
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
BoundingBox::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Aggregate<T> for BoundingVolumeHierarchy<T> {}
|
||||
impl Aggregate for BoundingVolumeHierarchy {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use nalgebra::{Affine3, Point3, Vector3};
|
||||
|
||||
use super::materials::Material;
|
||||
use crate::Real;
|
||||
|
||||
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
|
||||
/// going out from the camera of a reflection from a surface.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Ray<T: Real> {
|
||||
pub struct Ray {
|
||||
/// The start point of the ray
|
||||
pub origin: Point3<T>,
|
||||
pub origin: Point3<f64>,
|
||||
|
||||
/// The direction the ray goes in.
|
||||
///
|
||||
/// 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
|
||||
pub fn new(origin: Point3<T>, direction: Vector3<T>) -> Ray<T> {
|
||||
pub fn new(origin: Point3<f64>, direction: Vector3<f64>) -> Ray {
|
||||
Ray {
|
||||
origin,
|
||||
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
|
||||
pub fn point_at(&self, t: T) -> Point3<T> {
|
||||
pub fn point_at(&self, t: f64) -> Point3<f64> {
|
||||
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
|
||||
/// that rounding-errors don;t cause a reflection ray doesn't intersect with the point
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -66,42 +65,42 @@ impl<T: Real> Ray<T> {
|
|||
/// This struct is returned by [intersect()](Intersect::intersect) and contatins all the
|
||||
/// information needed to evaluate the rendering function for that intersection.
|
||||
#[derive(Debug)]
|
||||
pub struct IntersectionInfo<T: Real> {
|
||||
pub struct IntersectionInfo {
|
||||
/// The distance between the ray origin and the intersection point
|
||||
pub distance: T,
|
||||
pub distance: f64,
|
||||
|
||||
/// The intersection point
|
||||
pub location: Point3<T>,
|
||||
pub location: Point3<f64>,
|
||||
|
||||
/// The surface normal at the intersection point
|
||||
pub normal: Vector3<T>,
|
||||
pub normal: Vector3<f64>,
|
||||
|
||||
/// The surface tangent at the intersection point
|
||||
///
|
||||
/// Which surface tangent direction returned is dependent on the [Primitive](Primitive)
|
||||
/// but should generally be smooth over any given surface
|
||||
pub tangent: Vector3<T>,
|
||||
pub tangent: Vector3<f64>,
|
||||
|
||||
/// Another surface tangent, perpendicular to `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
|
||||
///
|
||||
/// Equal to `-ray.direction`
|
||||
pub retro: Vector3<T>,
|
||||
pub retro: Vector3<f64>,
|
||||
|
||||
/// The [Material](crate::materials::Material) which describes the optical
|
||||
/// 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
|
||||
/// 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.
|
||||
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
|
||||
|
|
@ -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))
|
||||
/// and as a (possibly) faster alternative to [Intersect](Intersect) when only a simple
|
||||
/// 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.
|
||||
fn intersect(&self, ray: &Ray<T>) -> bool;
|
||||
fn intersect(&self, ray: &Ray) -> bool;
|
||||
}
|
||||
|
||||
/// 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 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
|
||||
///
|
||||
/// 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.
|
||||
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
|
||||
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.
|
||||
//fn transform(&self, transformation: &Affine3<T>) -> dyn Primitive<T>;
|
||||
//fn transform(&self, transformation: &Affine3) -> dyn Primitive;
|
||||
}
|
||||
|
||||
/// Either a primitive or a collection of primitives
|
||||
pub trait Aggregate<T: Real>: Intersect<T> + HasBoundingBox<T> {}
|
||||
pub trait Aggregate: Intersect + HasBoundingBox {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
@ -145,26 +144,26 @@ mod tests {
|
|||
|
||||
use super::*;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
impl<T: Arbitrary + Real> Arbitrary for Ray<T> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Ray<T> {
|
||||
let origin = <Point3<T> as Arbitrary>::arbitrary(g);
|
||||
let direction = <Vector3<T> as Arbitrary>::arbitrary(g);
|
||||
impl Arbitrary for Ray {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Ray {
|
||||
let origin = <Point3<f64> as Arbitrary>::arbitrary(g);
|
||||
let direction = <Vector3<f64> as Arbitrary>::arbitrary(g);
|
||||
return Ray::new(origin, direction);
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn t0_is_origin(ray: Ray<f64>) -> bool {
|
||||
fn t0_is_origin(ray: Ray) -> bool {
|
||||
ray.point_at(0.0) == ray.origin
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
#[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 p2 = ray.point_at(t2);
|
||||
let p3 = ray.point_at(t3);
|
||||
|
|
@ -177,7 +176,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,29 @@
|
|||
use nalgebra::{convert, Affine3, Point3, Vector3};
|
||||
use nalgebra::{Affine3, Point3, Vector3};
|
||||
|
||||
use crate::materials::Material;
|
||||
use crate::Real;
|
||||
|
||||
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Plane<T: Real> {
|
||||
normal: Vector3<T>,
|
||||
tangent: Vector3<T>,
|
||||
cotangent: Vector3<T>,
|
||||
distance_from_origin: T,
|
||||
material: Arc<dyn Material<T>>,
|
||||
pub struct Plane {
|
||||
normal: Vector3<f64>,
|
||||
tangent: Vector3<f64>,
|
||||
cotangent: Vector3<f64>,
|
||||
distance_from_origin: f64,
|
||||
material: Arc<dyn Material>,
|
||||
}
|
||||
|
||||
impl<T: Real> Plane<T> {
|
||||
impl Plane {
|
||||
pub fn new(
|
||||
normal: Vector3<T>,
|
||||
distance_from_origin: T,
|
||||
material: Arc<dyn Material<T>>,
|
||||
) -> Plane<T> {
|
||||
normal: Vector3<f64>,
|
||||
distance_from_origin: f64,
|
||||
material: Arc<dyn Material>,
|
||||
) -> Plane {
|
||||
let normal = normal.normalize();
|
||||
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 tangent = normal.cross(&cotangent);
|
||||
Plane {
|
||||
|
|
@ -37,8 +36,8 @@ impl<T: Real> Plane<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Transform<T> for Plane<T> {
|
||||
fn transform(&self, transformation: &Affine3<T>) -> Self {
|
||||
impl Transform for Plane {
|
||||
fn transform(&self, transformation: &Affine3<f64>) -> Self {
|
||||
Plane {
|
||||
normal: transformation.transform_vector(&self.normal).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> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
impl Intersect for Plane {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
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_minus_ray_origin_dot_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
|
||||
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
|
||||
return None;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
Some(IntersectionInfo {
|
||||
|
|
@ -80,18 +79,14 @@ impl<T: Real> Intersect<T> for Plane<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Real> HasBoundingBox<T> for Plane<T> {
|
||||
fn bounding_box(&self) -> BoundingBox<T> {
|
||||
impl HasBoundingBox for Plane {
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
let p0 = Point3::from(self.normal * self.distance_from_origin);
|
||||
let f = |v: Vector3<T>| {
|
||||
let infinity: T = convert(std::f64::INFINITY);
|
||||
Vector3::from_iterator(v.iter().map(|&elem| {
|
||||
if elem == T::zero() {
|
||||
T::zero()
|
||||
} else {
|
||||
infinity
|
||||
}
|
||||
}))
|
||||
let f = |v: Vector3<f64>| {
|
||||
Vector3::from_iterator(
|
||||
v.iter()
|
||||
.map(|&elem| if elem == 0.0 { 0.0 } else { std::f64::INFINITY }),
|
||||
)
|
||||
};
|
||||
let tangent = f(self.tangent);
|
||||
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)]
|
||||
mod tests {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
use nalgebra::{convert, Affine3, Point3, Vector3};
|
||||
use nalgebra::{Affine3, Point3, Vector3};
|
||||
|
||||
use crate::materials::Material;
|
||||
use crate::Real;
|
||||
|
||||
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Sphere<T: Real> {
|
||||
centre: Point3<T>,
|
||||
radius: T,
|
||||
material: Arc<dyn Material<T>>,
|
||||
pub struct Sphere {
|
||||
centre: Point3<f64>,
|
||||
radius: f64,
|
||||
material: Arc<dyn Material>,
|
||||
}
|
||||
|
||||
impl<T: Real> Sphere<T> {
|
||||
pub fn new(centre: Point3<T>, radius: T, material: Arc<dyn Material<T>>) -> Sphere<T> {
|
||||
impl Sphere {
|
||||
pub fn new(centre: Point3<f64>, radius: f64, material: Arc<dyn Material>) -> Sphere {
|
||||
Sphere {
|
||||
centre,
|
||||
radius,
|
||||
|
|
@ -24,54 +23,52 @@ impl<T: Real> Sphere<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Transform<T> for Sphere<T> {
|
||||
fn transform(&self, transformation: &Affine3<T>) -> Self {
|
||||
impl Transform for Sphere {
|
||||
fn transform(&self, transformation: &Affine3<f64>) -> Self {
|
||||
Sphere {
|
||||
centre: transformation.transform_point(&self.centre),
|
||||
// This is not the most efficient way of calculating the radius,
|
||||
//but will work as long as the resulting shape is still a sphere.
|
||||
radius: transformation
|
||||
.transform_vector(&Vector3::new(self.radius, T::zero(), T::zero()))
|
||||
.transform_vector(&Vector3::new(self.radius, 0.0, 0.0))
|
||||
.norm(),
|
||||
material: Arc::clone(&self.material),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Intersect<T> for Sphere<T> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
let two: T = convert(2.0);
|
||||
let four: T = convert(4.0);
|
||||
impl Intersect for Sphere {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
let r_o = ray.origin.coords;
|
||||
let centre_coords = self.centre.coords;
|
||||
let a = ray
|
||||
.direction
|
||||
.component_mul(&ray.direction)
|
||||
.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))
|
||||
* two)
|
||||
* 2.0)
|
||||
.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(¢re_coords)
|
||||
- centre_coords.component_mul(&r_o) * two)
|
||||
- centre_coords.component_mul(&r_o) * 2.0)
|
||||
.iter()
|
||||
.fold(T::zero(), |a, b| a + *b)
|
||||
.fold(0.0, |a, b| a + *b)
|
||||
- self.radius * self.radius;
|
||||
let delta_squared: T = b * b - four * a * c;
|
||||
if delta_squared < T::zero() {
|
||||
let delta_squared = b * b - 4.0 * a * c;
|
||||
if delta_squared < 0.0 {
|
||||
None
|
||||
} else {
|
||||
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 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
|
||||
} else {
|
||||
t1
|
||||
};
|
||||
if distance <= T::zero() {
|
||||
if distance <= 0.0 {
|
||||
None
|
||||
} else {
|
||||
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> {
|
||||
fn bounding_box(&self) -> BoundingBox<T> {
|
||||
impl HasBoundingBox for Sphere {
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
let radius_xyz = Vector3::new(self.radius, self.radius, self.radius);
|
||||
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)]
|
||||
mod tests {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use crate::materials::Material;
|
||||
use crate::Real;
|
||||
|
||||
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray, Transform};
|
||||
use nalgebra::{Affine3, Point3, Vector2, Vector3};
|
||||
|
|
@ -7,14 +6,14 @@ use nalgebra::{Affine3, Point3, Vector2, Vector3};
|
|||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Triangle<T: Real> {
|
||||
pub vertices: [Point3<T>; 3],
|
||||
pub normals: [Vector3<T>; 3],
|
||||
pub material: Arc<dyn Material<T>>,
|
||||
pub struct Triangle {
|
||||
pub vertices: [Point3<f64>; 3],
|
||||
pub normals: [Vector3<f64>; 3],
|
||||
pub material: Arc<dyn Material>,
|
||||
}
|
||||
|
||||
impl<T: Real> Transform<T> for Triangle<T> {
|
||||
fn transform(&self, transformation: &Affine3<T>) -> Self {
|
||||
impl Transform for Triangle {
|
||||
fn transform(&self, transformation: &Affine3<f64>) -> Self {
|
||||
let normal_transformation =
|
||||
Affine3::from_matrix_unchecked(transformation.inverse().matrix().transpose());
|
||||
Triangle {
|
||||
|
|
@ -33,13 +32,13 @@ impl<T: Real> Transform<T> for Triangle<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Intersect<T> for Triangle<T> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
impl Intersect for Triangle {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
let translation = -ray.origin.coords;
|
||||
let indices = indices_with_index_of_largest_element_last(&ray.direction);
|
||||
let permuted_ray_direction = permute_vector_elements(&ray.direction, &indices);
|
||||
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
|
||||
.iter()
|
||||
.map(|elem| {
|
||||
|
|
@ -60,17 +59,17 @@ impl<T: Real> Intersect<T> for Triangle<T> {
|
|||
.iter()
|
||||
.zip(transformed_vertices.iter())
|
||||
.map(|(&coord, vertex)| vertex.z * coord)
|
||||
.fold(T::zero(), |acc, z| acc + z);
|
||||
if transformed_z.is_positive() != permuted_ray_direction.z.is_positive() {
|
||||
.fold(0.0, |acc, z| acc + z);
|
||||
if transformed_z.is_sign_positive() != permuted_ray_direction.z.is_sign_positive() {
|
||||
return None;
|
||||
}
|
||||
let location: Point3<T> = barycentric_coordinates
|
||||
let location = barycentric_coordinates
|
||||
.iter()
|
||||
.zip(self.vertices.iter())
|
||||
.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 normal: Vector3<T> = barycentric_coordinates
|
||||
let normal: Vector3<f64> = barycentric_coordinates
|
||||
.iter()
|
||||
.zip(self.normals.iter())
|
||||
.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> {
|
||||
fn bounding_box(&self) -> BoundingBox<T> {
|
||||
impl HasBoundingBox for Triangle {
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
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.z > v.x {
|
||||
[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))
|
||||
}
|
||||
|
||||
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));
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
// from the twoother elements of the input. ( (y,z) -> x, (z,x) -> y, (x,y) -> z )
|
||||
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> {
|
||||
e * (T::one() / e.iter().fold(T::zero(), |a, &b| a + b))
|
||||
fn barycentric_coordinates_from_signed_edge_functions(e: Vector3<f64>) -> Vector3<f64> {
|
||||
e * (1.0 / e.iter().fold(0.0, |a, &b| a + b))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -175,12 +174,12 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn transform_by_identity_does_not_change_values(
|
||||
v0: Point3<f32>,
|
||||
v1: Point3<f32>,
|
||||
v2: Point3<f32>,
|
||||
n0: Vector3<f32>,
|
||||
n1: Vector3<f32>,
|
||||
n2: Vector3<f32>,
|
||||
v0: Point3<f64>,
|
||||
v1: Point3<f64>,
|
||||
v2: Point3<f64>,
|
||||
n0: Vector3<f64>,
|
||||
n1: Vector3<f64>,
|
||||
n2: Vector3<f64>,
|
||||
) -> bool {
|
||||
let n0 = n0.normalize();
|
||||
let n1 = n1.normalize();
|
||||
|
|
@ -201,13 +200,13 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn translate_does_not_change_normals(
|
||||
v0: Point3<f32>,
|
||||
v1: Point3<f32>,
|
||||
v2: Point3<f32>,
|
||||
n0: Vector3<f32>,
|
||||
n1: Vector3<f32>,
|
||||
n2: Vector3<f32>,
|
||||
translation: Vector3<f32>,
|
||||
v0: Point3<f64>,
|
||||
v1: Point3<f64>,
|
||||
v2: Point3<f64>,
|
||||
n0: Vector3<f64>,
|
||||
n1: Vector3<f64>,
|
||||
n2: Vector3<f64>,
|
||||
translation: Vector3<f64>,
|
||||
) -> bool {
|
||||
let n0 = n0.normalize();
|
||||
let n1 = n1.normalize();
|
||||
|
|
@ -224,13 +223,13 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn translate_translates_vertices(
|
||||
v0: Point3<f32>,
|
||||
v1: Point3<f32>,
|
||||
v2: Point3<f32>,
|
||||
n0: Vector3<f32>,
|
||||
n1: Vector3<f32>,
|
||||
n2: Vector3<f32>,
|
||||
translation: Vector3<f32>,
|
||||
v0: Point3<f64>,
|
||||
v1: Point3<f64>,
|
||||
v2: Point3<f64>,
|
||||
n0: Vector3<f64>,
|
||||
n1: Vector3<f64>,
|
||||
n2: Vector3<f64>,
|
||||
translation: Vector3<f64>,
|
||||
) -> bool {
|
||||
let n0 = n0.normalize();
|
||||
let n1 = n1.normalize();
|
||||
|
|
@ -253,43 +252,43 @@ mod tests {
|
|||
use quickcheck_macros::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);
|
||||
is_valid_permutation(&indices)
|
||||
}
|
||||
|
||||
#[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);
|
||||
indices.iter().any(|&i| i == 0)
|
||||
}
|
||||
|
||||
#[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);
|
||||
indices.iter().any(|&i| i == 1)
|
||||
}
|
||||
|
||||
#[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);
|
||||
indices.iter().any(|&i| i == 2)
|
||||
}
|
||||
|
||||
#[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);
|
||||
v[indices[2]] >= v.x
|
||||
}
|
||||
|
||||
#[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);
|
||||
v[indices[2]] >= v.y
|
||||
}
|
||||
|
||||
#[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);
|
||||
v[indices[2]] >= v.z
|
||||
}
|
||||
|
|
@ -300,19 +299,19 @@ mod tests {
|
|||
use quickcheck_macros::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));
|
||||
p.z >= v.x
|
||||
}
|
||||
|
||||
#[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));
|
||||
p.z >= v.y
|
||||
}
|
||||
|
||||
#[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));
|
||||
p.z >= v.z
|
||||
}
|
||||
|
|
@ -323,19 +322,19 @@ mod tests {
|
|||
use quickcheck_macros::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);
|
||||
apply_shear_to_z_axis(&v, &s).x.abs() < 0.00001
|
||||
}
|
||||
|
||||
#[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);
|
||||
apply_shear_to_z_axis(&v, &s).y.abs() < 0.00001
|
||||
}
|
||||
|
||||
#[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);
|
||||
apply_shear_to_z_axis(&v, &s).z == v.z
|
||||
}
|
||||
|
|
@ -348,8 +347,8 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn sign_of_signed_edge_function_matches_winding(
|
||||
a: Vector3<f32>,
|
||||
b: Vector3<f32>,
|
||||
a: Vector3<f64>,
|
||||
b: Vector3<f64>,
|
||||
) -> TestResult {
|
||||
let a_2d = Vector2::new(a.x, a.y);
|
||||
let b_2d = Vector2::new(b.x, b.y);
|
||||
|
|
@ -366,9 +365,9 @@ mod tests {
|
|||
|
||||
#[quickcheck]
|
||||
fn signed_edge_functions_has_same_result_as_signed_edge_function(
|
||||
a: Vector3<f32>,
|
||||
b: Vector3<f32>,
|
||||
c: Vector3<f32>,
|
||||
a: Vector3<f64>,
|
||||
b: Vector3<f64>,
|
||||
c: Vector3<f64>,
|
||||
) -> bool {
|
||||
let es = signed_edge_functions(&vec![a, b, c]);
|
||||
es[0] == signed_edge_function(&b, &c)
|
||||
|
|
@ -499,7 +498,7 @@ mod tests {
|
|||
}
|
||||
|
||||
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>,
|
||||
vertex1: Point3<f64>,
|
||||
|
|
@ -675,7 +674,7 @@ mod tests {
|
|||
}
|
||||
|
||||
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>,
|
||||
vertex1: Point3<f64>,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
use super::{Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
|
||||
use crate::Real;
|
||||
|
||||
impl<T: Real> HasBoundingBox<T> for Vec<Box<dyn Primitive<T>>> {
|
||||
fn bounding_box(&self) -> BoundingBox<T> {
|
||||
impl HasBoundingBox for Vec<Box<dyn Primitive>> {
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
self.iter().fold(BoundingBox::empty(), |acc, elem| {
|
||||
acc.union(&elem.bounding_box())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Intersect<T> for Vec<Box<dyn Primitive<T>>> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
impl Intersect for Vec<Box<dyn Primitive>> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
self.iter()
|
||||
.flat_map(|primitive| primitive.intersect(&ray))
|
||||
.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<T: Real> HasBoundingBox<T> for Vec<Box<dyn Aggregate<T>>> {
|
||||
fn bounding_box(&self) -> BoundingBox<T> {
|
||||
impl HasBoundingBox for Vec<Box<dyn Aggregate>> {
|
||||
fn bounding_box(&self) -> BoundingBox {
|
||||
self.iter().fold(BoundingBox::empty(), |acc, elem| {
|
||||
acc.union(&elem.bounding_box())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Real> Intersect<T> for Vec<Box<dyn Aggregate<T>>> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
impl Intersect for Vec<Box<dyn Aggregate>> {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
self.iter()
|
||||
.flat_map(|aggregate| aggregate.intersect(&ray))
|
||||
.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>> {}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
use nalgebra::RealField;
|
||||
use simba::scalar::{SubsetOf, SupersetOf};
|
||||
|
||||
pub trait NormalizedToU32 {
|
||||
fn normalized_to_u32(self, num_bits: usize) -> u32;
|
||||
}
|
||||
|
||||
pub trait Real: RealField + SupersetOf<f32> + SubsetOf<f32> + NormalizedToU32 + PartialOrd {}
|
||||
|
||||
impl NormalizedToU32 for f32 {
|
||||
fn normalized_to_u32(self, num_bits: usize) -> u32 {
|
||||
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)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
use super::raycasting::{IntersectionInfo, Ray};
|
||||
use super::scene::Scene;
|
||||
|
||||
use crate::Real;
|
||||
|
||||
pub struct Sampler<'a, T: Real> {
|
||||
pub scene: &'a Scene<T>,
|
||||
pub struct Sampler<'a> {
|
||||
pub scene: &'a Scene,
|
||||
}
|
||||
|
||||
impl<'a, T: Real> Sampler<'a, T> {
|
||||
pub fn sample(&self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
|
||||
impl<'a> Sampler<'a> {
|
||||
pub fn sample(&self, ray: &Ray) -> Option<IntersectionInfo> {
|
||||
self.scene
|
||||
.objects
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use nalgebra::{Point3};
|
||||
use nalgebra::Point3;
|
||||
|
||||
use crate::raycasting::Aggregate;
|
||||
use crate::Real;
|
||||
|
||||
pub struct Scene<T: Real> {
|
||||
pub camera_location: Point3<T>,
|
||||
pub objects: Vec<Box<dyn Aggregate<T>>>,
|
||||
pub struct Scene {
|
||||
pub camera_location: Point3<f64>,
|
||||
pub objects: Vec<Box<dyn Aggregate>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
use nalgebra::{Matrix3, Vector3};
|
||||
|
||||
use crate::Real;
|
||||
|
||||
pub fn try_change_of_basis_matrix<T: Real>(
|
||||
x: &Vector3<T>,
|
||||
y: &Vector3<T>,
|
||||
z: &Vector3<T>,
|
||||
) -> Option<Matrix3<T>> {
|
||||
Some(Matrix3::from_rows(&[x.transpose(), y.transpose(), z.transpose()]))
|
||||
pub fn try_change_of_basis_matrix(
|
||||
x: &Vector3<f64>,
|
||||
y: &Vector3<f64>,
|
||||
z: &Vector3<f64>,
|
||||
) -> Option<Matrix3<f64>> {
|
||||
Some(Matrix3::from_rows(&[
|
||||
x.transpose(),
|
||||
y.transpose(),
|
||||
z.transpose(),
|
||||
]))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -21,7 +23,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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::y_axis(),
|
||||
&Vector3::z_axis(),
|
||||
|
|
@ -31,8 +33,8 @@ mod tests {
|
|||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn swap_xy_does_not_change_z(v: Vector3<f32>) {
|
||||
let target: Matrix3<f32> = try_change_of_basis_matrix(
|
||||
fn swap_xy_does_not_change_z(v: Vector3<f64>) {
|
||||
let target: Matrix3<f64> = try_change_of_basis_matrix(
|
||||
&Vector3::y_axis(),
|
||||
&Vector3::x_axis(),
|
||||
&Vector3::z_axis(),
|
||||
|
|
@ -43,8 +45,8 @@ mod tests {
|
|||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn swap_xy_copies_y_to_x(v: Vector3<f32>) {
|
||||
let target: Matrix3<f32> = try_change_of_basis_matrix(
|
||||
fn swap_xy_copies_y_to_x(v: Vector3<f64>) {
|
||||
let target: Matrix3<f64> = try_change_of_basis_matrix(
|
||||
&Vector3::y_axis(),
|
||||
&Vector3::x_axis(),
|
||||
&Vector3::z_axis(),
|
||||
|
|
@ -55,8 +57,8 @@ mod tests {
|
|||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn swap_xy_copies_x_to_y(v: Vector3<f32>) {
|
||||
let target: Matrix3<f32> = try_change_of_basis_matrix(
|
||||
fn swap_xy_copies_x_to_y(v: Vector3<f64>) {
|
||||
let target: Matrix3<f64> = try_change_of_basis_matrix(
|
||||
&Vector3::y_axis(),
|
||||
&Vector3::x_axis(),
|
||||
&Vector3::z_axis(),
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
use nalgebra::Point3;
|
||||
|
||||
use crate::util::Interval;
|
||||
use crate::Real;
|
||||
|
||||
use itertools::izip;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BoundingBox<T: Real> {
|
||||
pub bounds: [Interval<T>; 3],
|
||||
pub struct BoundingBox {
|
||||
pub bounds: [Interval; 3],
|
||||
}
|
||||
|
||||
impl<T: Real> BoundingBox<T> {
|
||||
pub fn from_corners(a: Point3<T>, b: Point3<T>) -> Self {
|
||||
impl BoundingBox {
|
||||
pub fn from_corners(a: Point3<f64>, b: Point3<f64>) -> Self {
|
||||
let mut result = BoundingBox {
|
||||
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 {
|
||||
bounds: [
|
||||
Interval::degenerate(p.x),
|
||||
|
|
@ -39,14 +38,14 @@ impl<T: Real> BoundingBox<T> {
|
|||
|
||||
pub fn from_points<'a, I>(points: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = &'a Point3<T>>,
|
||||
I: IntoIterator<Item = &'a Point3<f64>>,
|
||||
{
|
||||
points
|
||||
.into_iter()
|
||||
.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 {
|
||||
bounds: [
|
||||
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
|
||||
.iter()
|
||||
.zip(p.iter())
|
||||
.all(|(interval, &value)| interval.contains_value(value))
|
||||
}
|
||||
|
||||
pub fn union(&self, other: &BoundingBox<T>) -> BoundingBox<T> {
|
||||
pub fn union(&self, other: &BoundingBox) -> BoundingBox {
|
||||
BoundingBox {
|
||||
bounds: [
|
||||
self.bounds[0].union(other.bounds[0]),
|
||||
|
|
@ -82,13 +81,13 @@ impl<T: Real> BoundingBox<T> {
|
|||
(
|
||||
index,
|
||||
if elem.is_degenerate() {
|
||||
-T::one()
|
||||
-1.0
|
||||
} else {
|
||||
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 {
|
||||
(elem, elem_size)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
use nalgebra::convert;
|
||||
|
||||
use crate::Real;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Interval<T: Real> {
|
||||
min: T,
|
||||
max: T,
|
||||
pub struct Interval {
|
||||
min: f64,
|
||||
max: f64,
|
||||
}
|
||||
|
||||
impl<T: Real> Interval<T> {
|
||||
pub fn new(a: T, b: T) -> Self {
|
||||
impl Interval {
|
||||
pub fn new(a: f64, b: f64) -> Self {
|
||||
if a > b {
|
||||
Interval { min: b, max: a }
|
||||
} else {
|
||||
|
|
@ -19,30 +15,30 @@ impl<T: Real> Interval<T> {
|
|||
|
||||
pub fn empty() -> Self {
|
||||
Interval {
|
||||
min: convert(std::f64::INFINITY),
|
||||
max: convert(std::f64::NEG_INFINITY),
|
||||
min: std::f64::INFINITY,
|
||||
max: std::f64::NEG_INFINITY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infinite() -> Self {
|
||||
Interval {
|
||||
min: convert(std::f64::NEG_INFINITY),
|
||||
max: convert(std::f64::INFINITY),
|
||||
min: std::f64::NEG_INFINITY,
|
||||
max: std::f64::INFINITY,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn degenerate(value: T) -> Self {
|
||||
pub fn degenerate(value: f64) -> Self {
|
||||
Interval {
|
||||
min: value,
|
||||
max: value,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_min(&self) -> T {
|
||||
pub fn get_min(&self) -> f64 {
|
||||
self.min
|
||||
}
|
||||
|
||||
pub fn get_max(&self) -> T {
|
||||
pub fn get_max(&self) -> f64 {
|
||||
self.max
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +50,7 @@ impl<T: Real> Interval<T> {
|
|||
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
|
||||
}
|
||||
|
||||
|
|
@ -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() {
|
||||
Interval::degenerate(v)
|
||||
} else {
|
||||
|
|
@ -120,7 +116,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn empty_is_empty() {
|
||||
let target: Interval<f64> = Interval::empty();
|
||||
let target: Interval = Interval::empty();
|
||||
assert!(target.is_empty());
|
||||
}
|
||||
|
||||
|
|
@ -240,7 +236,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
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());
|
||||
assert!(target.min == result.min);
|
||||
assert!(target.max == result.max);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
use crate::realtype::NormalizedToU32;
|
||||
use nalgebra::Point3;
|
||||
|
||||
use crate::Real;
|
||||
|
||||
//use std::cmp::Ordering;
|
||||
|
||||
fn spread_bits(v: u32) -> u32 {
|
||||
let mut result = 0;
|
||||
for power in 0..9 {
|
||||
|
|
@ -12,7 +9,7 @@ fn spread_bits(v: u32) -> u32 {
|
|||
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 y = p.y.normalized_to_u32(10);
|
||||
let z = p.z.normalized_to_u32(10);
|
||||
|
|
|
|||
|
|
@ -1,41 +1,39 @@
|
|||
use super::axis_aligned_bounding_box::BoundingBox;
|
||||
use super::Interval;
|
||||
|
||||
use crate::Real;
|
||||
|
||||
use nalgebra::{clamp, Point3};
|
||||
|
||||
use itertools::izip;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RealNormalizer<T: Real> {
|
||||
min: T,
|
||||
range: T,
|
||||
pub struct RealNormalizer {
|
||||
min: f64,
|
||||
range: f64,
|
||||
}
|
||||
|
||||
impl<T: Real> RealNormalizer<T> {
|
||||
pub fn new(interval: Interval<T>) -> Self {
|
||||
impl RealNormalizer {
|
||||
pub fn new(interval: Interval) -> Self {
|
||||
let min = interval.get_min();
|
||||
let range = interval.get_max() - min;
|
||||
Self { min, range }
|
||||
}
|
||||
|
||||
pub fn normalize(&self, value: T) -> T {
|
||||
pub fn normalize(&self, value: f64) -> f64 {
|
||||
(value - self.min) / self.range
|
||||
}
|
||||
|
||||
pub fn normalize_and_clamp(&self, value: T) -> T {
|
||||
clamp((value - self.min) / self.range, T::zero(), T::one())
|
||||
pub fn normalize_and_clamp(&self, value: f64) -> f64 {
|
||||
clamp((value - self.min) / self.range, 0.0, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Point3Normalizer<T: Real> {
|
||||
dimension_normalizers: [RealNormalizer<T>; 3],
|
||||
pub struct Point3Normalizer {
|
||||
dimension_normalizers: [RealNormalizer; 3],
|
||||
}
|
||||
|
||||
impl<T: Real> Point3Normalizer<T> {
|
||||
pub fn new(bounds: BoundingBox<T>) -> Self {
|
||||
impl Point3Normalizer {
|
||||
pub fn new(bounds: BoundingBox) -> Self {
|
||||
let mut normalizer = Point3Normalizer {
|
||||
dimension_normalizers: [RealNormalizer::new(Interval::empty()); 3],
|
||||
};
|
||||
|
|
@ -49,8 +47,8 @@ impl<T: Real> Point3Normalizer<T> {
|
|||
normalizer
|
||||
}
|
||||
|
||||
pub fn normalize(&self, point: Point3<T>) -> Point3<T> {
|
||||
let mut result = Point3::new(T::zero(), T::zero(), T::zero());
|
||||
pub fn normalize(&self, point: Point3<f64>) -> Point3<f64> {
|
||||
let mut result = Point3::new(0.0, 0.0, 0.0);
|
||||
for (value_out, &value_in, normalizer) in izip!(
|
||||
result.iter_mut(),
|
||||
point.iter(),
|
||||
|
|
@ -61,8 +59,8 @@ impl<T: Real> Point3Normalizer<T> {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn normalize_and_clamp(&self, point: Point3<T>) -> Point3<T> {
|
||||
let mut result = Point3::new(T::zero(), T::zero(), T::zero());
|
||||
pub fn normalize_and_clamp(&self, point: Point3<f64>) -> Point3<f64> {
|
||||
let mut result = Point3::new(0.0, 0.0, 0.0);
|
||||
for (value_out, &value_in, normalizer) in izip!(
|
||||
result.iter_mut(),
|
||||
point.iter(),
|
||||
|
|
|
|||
|
|
@ -3,15 +3,14 @@ use nalgebra::{convert, Point3, Vector3};
|
|||
|
||||
use crate::materials::Material;
|
||||
use crate::raycasting::Triangle;
|
||||
use crate::Real;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn triangulate_polygon<T: Real>(
|
||||
vertices: &Vec<Point3<T>>,
|
||||
normal: &Vector3<T>,
|
||||
material: Arc<dyn Material<T>>,
|
||||
) -> Vec<Triangle<T>> {
|
||||
pub fn triangulate_polygon(
|
||||
vertices: &Vec<Point3<f64>>,
|
||||
normal: &Vector3<f64>,
|
||||
material: Arc<dyn Material>,
|
||||
) -> Vec<Triangle> {
|
||||
assert!(vertices.len() >= 3);
|
||||
let hinge = vertices[0];
|
||||
izip!(vertices.iter().skip(1), vertices.iter().skip(2))
|
||||
|
|
@ -23,104 +22,102 @@ pub fn triangulate_polygon<T: Real>(
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub fn generate_dodecahedron<T: Real>(
|
||||
centre: Point3<T>,
|
||||
size: T,
|
||||
material: Arc<dyn Material<T>>,
|
||||
) -> Vec<Triangle<T>> {
|
||||
pub fn generate_dodecahedron(
|
||||
centre: Point3<f64>,
|
||||
size: f64,
|
||||
material: Arc<dyn Material>,
|
||||
) -> Vec<Triangle> {
|
||||
let phi = convert((1.0 + (5.0_f64).sqrt()) / 2.0);
|
||||
let phi_inv = T::one() / phi;
|
||||
let one = T::one();
|
||||
let zero = T::zero();
|
||||
let phi_inv = 1.0 / phi;
|
||||
|
||||
let faces = vec![
|
||||
vec![
|
||||
Vector3::new(phi_inv, zero, phi),
|
||||
Vector3::new(-phi_inv, zero, phi),
|
||||
Vector3::new(-one, -one, one),
|
||||
Vector3::new(zero, -phi, phi_inv),
|
||||
Vector3::new(one, -one, one),
|
||||
Vector3::new(phi_inv, 0.0, phi),
|
||||
Vector3::new(-phi_inv, 0.0, phi),
|
||||
Vector3::new(-1.0, -1.0, 1.0),
|
||||
Vector3::new(0.0, -phi, phi_inv),
|
||||
Vector3::new(1.0, -1.0, 1.0),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(phi_inv, zero, phi),
|
||||
Vector3::new(-phi_inv, zero, phi),
|
||||
Vector3::new(-one, one, one),
|
||||
Vector3::new(zero, phi, phi_inv),
|
||||
Vector3::new(one, one, one),
|
||||
Vector3::new(phi_inv, 0.0, phi),
|
||||
Vector3::new(-phi_inv, 0.0, phi),
|
||||
Vector3::new(-1.0, 1.0, 1.0),
|
||||
Vector3::new(0.0, phi, phi_inv),
|
||||
Vector3::new(1.0, 1.0, 1.0),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(phi_inv, zero, phi),
|
||||
Vector3::new(one, -one, one),
|
||||
Vector3::new(phi, -phi_inv, zero),
|
||||
Vector3::new(phi, phi_inv, zero),
|
||||
Vector3::new(one, one, one),
|
||||
Vector3::new(phi_inv, 0.0, phi),
|
||||
Vector3::new(1.0, -1.0, 1.0),
|
||||
Vector3::new(phi, -phi_inv, 0.0),
|
||||
Vector3::new(phi, phi_inv, 0.0),
|
||||
Vector3::new(1.0, 1.0, 1.0),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(-phi_inv, zero, phi),
|
||||
Vector3::new(-one, -one, one),
|
||||
Vector3::new(-phi, -phi_inv, zero),
|
||||
Vector3::new(-phi, phi_inv, zero),
|
||||
Vector3::new(-one, one, one),
|
||||
Vector3::new(-phi_inv, 0.0, phi),
|
||||
Vector3::new(-1.0, -1.0, 1.0),
|
||||
Vector3::new(-phi, -phi_inv, 0.0),
|
||||
Vector3::new(-phi, phi_inv, 0.0),
|
||||
Vector3::new(-1.0, 1.0, 1.0),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(-one, -one, one),
|
||||
Vector3::new(-phi, -phi_inv, zero),
|
||||
Vector3::new(-one, -one, -one),
|
||||
Vector3::new(zero, -phi, -phi_inv),
|
||||
Vector3::new(zero, -phi, phi_inv),
|
||||
Vector3::new(-1.0, -1.0, 1.0),
|
||||
Vector3::new(-phi, -phi_inv, 0.0),
|
||||
Vector3::new(-1.0, -1.0, -1.0),
|
||||
Vector3::new(0.0, -phi, -phi_inv),
|
||||
Vector3::new(0.0, -phi, phi_inv),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(zero, -phi, phi_inv),
|
||||
Vector3::new(zero, -phi, -phi_inv),
|
||||
Vector3::new(one, -one, -one),
|
||||
Vector3::new(phi, -phi_inv, zero),
|
||||
Vector3::new(one, -one, one),
|
||||
Vector3::new(0.0, -phi, phi_inv),
|
||||
Vector3::new(0.0, -phi, -phi_inv),
|
||||
Vector3::new(1.0, -1.0, -1.0),
|
||||
Vector3::new(phi, -phi_inv, 0.0),
|
||||
Vector3::new(1.0, -1.0, 1.0),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(zero, phi, phi_inv),
|
||||
Vector3::new(zero, phi, -phi_inv),
|
||||
Vector3::new(-one, one, -one),
|
||||
Vector3::new(-phi, phi_inv, zero),
|
||||
Vector3::new(-one, one, one),
|
||||
Vector3::new(0.0, phi, phi_inv),
|
||||
Vector3::new(0.0, phi, -phi_inv),
|
||||
Vector3::new(-1.0, 1.0, -1.0),
|
||||
Vector3::new(-phi, phi_inv, 0.0),
|
||||
Vector3::new(-1.0, 1.0, 1.0),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(one, one, one),
|
||||
Vector3::new(phi, phi_inv, zero),
|
||||
Vector3::new(one, one, -one),
|
||||
Vector3::new(zero, phi, -phi_inv),
|
||||
Vector3::new(zero, phi, phi_inv),
|
||||
Vector3::new(1.0, 1.0, 1.0),
|
||||
Vector3::new(phi, phi_inv, 0.0),
|
||||
Vector3::new(1.0, 1.0, -1.0),
|
||||
Vector3::new(0.0, phi, -phi_inv),
|
||||
Vector3::new(0.0, phi, phi_inv),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(one, -one, -one),
|
||||
Vector3::new(zero, -phi, -phi_inv),
|
||||
Vector3::new(-one, -one, -one),
|
||||
Vector3::new(-phi_inv, zero, -phi),
|
||||
Vector3::new(phi_inv, zero, -phi),
|
||||
Vector3::new(1.0, -1.0, -1.0),
|
||||
Vector3::new(0.0, -phi, -phi_inv),
|
||||
Vector3::new(-1.0, -1.0, -1.0),
|
||||
Vector3::new(-phi_inv, 0.0, -phi),
|
||||
Vector3::new(phi_inv, 0.0, -phi),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(one, one, -one),
|
||||
Vector3::new(zero, phi, -phi_inv),
|
||||
Vector3::new(-one, one, -one),
|
||||
Vector3::new(-phi_inv, zero, -phi),
|
||||
Vector3::new(phi_inv, zero, -phi),
|
||||
Vector3::new(1.0, 1.0, -1.0),
|
||||
Vector3::new(0.0, phi, -phi_inv),
|
||||
Vector3::new(-1.0, 1.0, -1.0),
|
||||
Vector3::new(-phi_inv, 0.0, -phi),
|
||||
Vector3::new(phi_inv, 0.0, -phi),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(one, one, -one),
|
||||
Vector3::new(phi, phi_inv, zero),
|
||||
Vector3::new(phi, -phi_inv, zero),
|
||||
Vector3::new(one, -one, -one),
|
||||
Vector3::new(phi_inv, zero, -phi),
|
||||
Vector3::new(1.0, 1.0, -1.0),
|
||||
Vector3::new(phi, phi_inv, 0.0),
|
||||
Vector3::new(phi, -phi_inv, 0.0),
|
||||
Vector3::new(1.0, -1.0, -1.0),
|
||||
Vector3::new(phi_inv, 0.0, -phi),
|
||||
],
|
||||
vec![
|
||||
Vector3::new(-one, one, -one),
|
||||
Vector3::new(-phi, phi_inv, zero),
|
||||
Vector3::new(-phi, -phi_inv, zero),
|
||||
Vector3::new(-one, -one, -one),
|
||||
Vector3::new(-phi_inv, zero, -phi),
|
||||
Vector3::new(-1.0, 1.0, -1.0),
|
||||
Vector3::new(-phi, phi_inv, 0.0),
|
||||
Vector3::new(-phi, -phi_inv, 0.0),
|
||||
Vector3::new(-1.0, -1.0, -1.0),
|
||||
Vector3::new(-phi_inv, 0.0, -phi),
|
||||
],
|
||||
];
|
||||
|
||||
let scale = size * convert(3f64.sqrt() / 2.0);
|
||||
let scale = size * 3f64.sqrt() / 2.0;
|
||||
faces
|
||||
.iter()
|
||||
.flat_map(|face| {
|
||||
|
|
|
|||
Loading…
Reference in New Issue