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::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 {
|
||||||
|
|
|
||||||
101
src/colour.rs
101
src/colour.rs
|
|
@ -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());
|
||||||
|
|
|
||||||
32
src/image.rs
32
src/image.rs
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
src/mesh.rs
31
src/mesh.rs
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 {}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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(¢re_coords)
|
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()
|
.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 {
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
|
|
@ -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>> {}
|
||||||
|
|
|
||||||
|
|
@ -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::*;
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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| {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue