Big rijiggering of types

This commit is contained in:
Matthew Gordon 2020-06-11 22:13:51 -04:00
parent 01259e1e55
commit 90bb7d84af
10 changed files with 203 additions and 188 deletions

View File

@ -1,14 +1,14 @@
use criterion::{criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use vanrijn::partial_render_scene;
use vanrijn::colour::{ColourRgbF, NamedColour}; use vanrijn::colour::{ColourRgbF, NamedColour};
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial}; use vanrijn::materials::ReflectiveMaterial;
use vanrijn::mesh::load_obj; use vanrijn::mesh::load_obj;
use vanrijn::raycasting::{BoundingVolumeHierarchy, Plane, Primitive, Sphere}; use vanrijn::partial_render_scene;
use vanrijn::raycasting::BoundingVolumeHierarchy;
use vanrijn::scene::Scene; use vanrijn::scene::Scene;
use vanrijn::util::Tile; use vanrijn::util::Tile;
use nalgebra::{Point3, Vector3}; use nalgebra::Point3;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
@ -22,55 +22,17 @@ fn simple_scene(bencher: &mut Criterion) {
let scene = Scene { let scene = Scene {
camera_location: Point3::new(-2.0, 1.0, -5.0), camera_location: Point3::new(-2.0, 1.0, -5.0),
objects: vec![ objects: vec![Box::new(BoundingVolumeHierarchy::build(
Box::new(Plane::new( load_obj(
Vector3::new(0.0, 1.0, 0.0), &model_file_path,
-2.0,
Arc::new(LambertianMaterial {
colour: ColourRgbF::new(0.55, 0.27, 0.04),
diffuse_strength: 0.1,
}),
)) as Box<dyn Primitive<f64>>,
Box::new(Sphere::new(
Point3::new(-6.25, -0.5, 1.0),
1.0,
Arc::new(LambertianMaterial {
colour: ColourRgbF::from_named(NamedColour::Green),
diffuse_strength: 0.1,
}),
)),
Box::new(Sphere::new(
Point3::new(-4.25, -0.5, 2.0),
1.0,
Arc::new(ReflectiveMaterial { Arc::new(ReflectiveMaterial {
colour: ColourRgbF::from_named(NamedColour::Blue), colour: ColourRgbF::from_named(NamedColour::Yellow),
diffuse_strength: 0.01,
reflection_strength: 0.99,
}),
)),
Box::new(Sphere::new(
Point3::new(-5.0, 1.5, 1.0),
1.0,
Arc::new(PhongMaterial {
colour: ColourRgbF::from_named(NamedColour::Red),
diffuse_strength: 0.05, diffuse_strength: 0.05,
smoothness: 100.0, reflection_strength: 0.9,
specular_strength: 1.0,
}), }),
)), )
Box::new(BoundingVolumeHierarchy::build( .unwrap(),
&load_obj( ))],
&model_file_path,
Arc::new(PhongMaterial {
colour: ColourRgbF::from_named(NamedColour::Yellow),
diffuse_strength: 0.05,
smoothness: 100.0,
specular_strength: 1.0,
}),
)
.unwrap(),
)),
],
}; };
bencher.bench_function("simple_scene", |b| { bencher.bench_function("simple_scene", |b| {

View File

@ -13,12 +13,14 @@ use nalgebra::{Point3, Vector3};
use std::path::Path; use std::path::Path;
use std::sync::{mpsc, Arc}; use std::sync::{mpsc, Arc};
use vanrijn::partial_render_scene;
use vanrijn::colour::{ColourRgbF, NamedColour}; use vanrijn::colour::{ColourRgbF, NamedColour};
use vanrijn::image::{ClampingToneMapper, ImageRgbU8, ToneMapper}; use vanrijn::image::{ClampingToneMapper, ImageRgbU8, ToneMapper};
use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial}; use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial};
use vanrijn::mesh::load_obj; use vanrijn::mesh::load_obj;
use vanrijn::raycasting::{BoundingVolumeHierarchy, Plane, Primitive, Sphere}; use vanrijn::partial_render_scene;
use vanrijn::raycasting::{
Aggregate, BoundingVolumeHierarchy, Plane, Primitive, Sphere,
};
use vanrijn::scene::Scene; use vanrijn::scene::Scene;
use vanrijn::util::{Tile, TileIterator}; use vanrijn::util::{Tile, TileIterator};
@ -72,55 +74,56 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Loading object..."); println!("Loading object...");
let model_object = load_obj( let model_object = load_obj(
&model_file_path, &model_file_path,
Arc::new(PhongMaterial { Arc::new(ReflectiveMaterial {
colour: ColourRgbF::from_named(NamedColour::Blue), colour: ColourRgbF::from_named(NamedColour::Yellow),
diffuse_strength: 0.05, diffuse_strength: 0.01,
smoothness: 100.0, reflection_strength: 0.9,
specular_strength: 1.0,
}), }),
)?; )?;
println!("Building BVH..."); println!("Building BVH...");
let model_bvh = Box::new(BoundingVolumeHierarchy::build(&model_object)); let model_bvh: Box<dyn Aggregate<_>> = Box::new(BoundingVolumeHierarchy::build(model_object));
println!("Constructing Scene..."); println!("Constructing Scene...");
let scene = Scene { let scene = Scene {
camera_location: Point3::new(-2.0, 1.0, -5.0), camera_location: Point3::new(-2.0, 1.0, -5.0),
objects: vec![ objects: vec![
Box::new(Plane::new( Box::new(vec![
Vector3::new(0.0, 1.0, 0.0), Box::new(Plane::new(
-2.0, Vector3::new(0.0, 1.0, 0.0),
Arc::new(LambertianMaterial { -2.0,
colour: ColourRgbF::new(0.55, 0.27, 0.04), Arc::new(LambertianMaterial {
diffuse_strength: 0.1, colour: ColourRgbF::new(0.55, 0.27, 0.04),
}), diffuse_strength: 0.1,
)) as Box<dyn Primitive<f64>>, }),
Box::new(Sphere::new( )) as Box<dyn Primitive<f64>>,
Point3::new(-6.25, -0.5, 1.0), Box::new(Sphere::new(
1.0, Point3::new(-6.25, -0.5, 1.0),
Arc::new(LambertianMaterial { 1.0,
colour: ColourRgbF::from_named(NamedColour::Green), Arc::new(LambertianMaterial {
diffuse_strength: 0.1, colour: ColourRgbF::from_named(NamedColour::Green),
}), diffuse_strength: 0.1,
)), }),
Box::new(Sphere::new( )),
Point3::new(-4.25, -0.5, 2.0), Box::new(Sphere::new(
1.0, Point3::new(-4.25, -0.5, 2.0),
Arc::new(ReflectiveMaterial { 1.0,
colour: ColourRgbF::from_named(NamedColour::Blue), Arc::new(ReflectiveMaterial {
diffuse_strength: 0.01, colour: ColourRgbF::from_named(NamedColour::Blue),
reflection_strength: 0.99, diffuse_strength: 0.01,
}), reflection_strength: 0.99,
)), }),
Box::new(Sphere::new( )),
Point3::new(-5.0, 1.5, 1.0), Box::new(Sphere::new(
1.0, Point3::new(-5.0, 1.5, 1.0),
Arc::new(PhongMaterial { 1.0,
colour: ColourRgbF::from_named(NamedColour::Red), Arc::new(PhongMaterial {
diffuse_strength: 0.05, colour: ColourRgbF::from_named(NamedColour::Red),
smoothness: 100.0, diffuse_strength: 0.05,
specular_strength: 1.0, smoothness: 100.0,
}), specular_strength: 1.0,
)), }),
)),
]) as Box<dyn Aggregate<f64>>,
model_bvh, model_bvh,
], ],
}; };

View File

@ -68,7 +68,7 @@ mod wavefront_obj {
pub fn load_obj<T: Real>( pub fn load_obj<T: Real>(
filename: &Path, filename: &Path,
material: Arc<dyn Material<T>>, material: Arc<dyn Material<T>>,
) -> Result<Vec<Arc<dyn Primitive<T>>>> ) -> Result<Vec<Box<dyn Primitive<T>>>>
where where
T: SupersetOf<f32>, T: SupersetOf<f32>,
{ {
@ -80,7 +80,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| Box::new(triangle) as Box<dyn Primitive<T>>)
.collect()) .collect())
} }
} }

View File

@ -1,4 +1,6 @@
use super::{BoundingBox, HasBoundingBox, Intersect, IntersectP, IntersectionInfo, Primitive, Ray}; use super::{
Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectP, IntersectionInfo, Primitive, Ray,
};
use crate::util::morton::morton_order_value_3d; use crate::util::morton::morton_order_value_3d;
use crate::util::normalizer::Point3Normalizer; use crate::util::normalizer::Point3Normalizer;
@ -6,7 +8,7 @@ use crate::Real;
use nalgebra::{convert, Point3}; use nalgebra::{convert, Point3};
use std::sync::Arc; use std::mem::swap;
/// Stores a set of [Primitives](Primitive) and accelerates raycasting /// Stores a set of [Primitives](Primitive) and accelerates raycasting
/// ///
@ -16,7 +18,6 @@ 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.
#[derive(Clone)]
pub enum BoundingVolumeHierarchy<T: Real> { pub enum BoundingVolumeHierarchy<T: Real> {
Node { Node {
bounds: BoundingBox<T>, bounds: BoundingBox<T>,
@ -25,7 +26,7 @@ pub enum BoundingVolumeHierarchy<T: Real> {
}, },
Leaf { Leaf {
bounds: BoundingBox<T>, bounds: BoundingBox<T>,
primitive: Arc<dyn Primitive<T>>, primitive: Box<dyn Primitive<T>>,
}, },
None, None,
} }
@ -39,17 +40,17 @@ fn centre<T: Real>(bounds: &BoundingBox<T>) -> Point3<T> {
) )
} }
struct PrimitiveInfo<T: Real>(BoundingBox<T>, Arc<dyn Primitive<T>>); struct PrimitiveInfo<T: Real>(BoundingBox<T>, Option<Box<dyn Primitive<T>>>);
impl<T: Real> BoundingVolumeHierarchy<T> { impl<T: Real> BoundingVolumeHierarchy<T> {
pub fn build<'a, I>(primitives: I) -> Self pub fn build<'a, I>(primitives: I) -> Self
where where
I: IntoIterator<Item = &'a Arc<dyn Primitive<T>>>, I: IntoIterator<Item = Box<dyn Primitive<T>>>,
{ {
Self::from_node_vec( Self::from_node_vec(
primitives primitives
.into_iter() .into_iter()
.map(|primitive| PrimitiveInfo(primitive.bounding_box(), Arc::clone(primitive))) .map(|primitive| PrimitiveInfo(primitive.bounding_box(), Some(primitive)))
.collect(), .collect(),
) )
} }
@ -64,14 +65,14 @@ impl<T: Real> BoundingVolumeHierarchy<T> {
morton_order_value_3d(normalizer.normalize(centre(a))) morton_order_value_3d(normalizer.normalize(centre(a)))
.cmp(&morton_order_value_3d(normalizer.normalize(centre(b)))) .cmp(&morton_order_value_3d(normalizer.normalize(centre(b))))
}); });
Self::from_sorted_nodes(nodes.as_slice()) Self::from_sorted_nodes(nodes.as_mut_slice())
} }
fn from_sorted_nodes(nodes: &[PrimitiveInfo<T>]) -> Self { fn from_sorted_nodes(nodes: &mut [PrimitiveInfo<T>]) -> Self {
if nodes.len() >= 2 { if nodes.len() >= 2 {
let midpoint = nodes.len() / 2; let midpoint = nodes.len() / 2;
let left = Box::new(Self::from_sorted_nodes(&nodes[..midpoint])); let left = Box::new(Self::from_sorted_nodes(&mut nodes[..midpoint]));
let right = Box::new(Self::from_sorted_nodes(&nodes[midpoint..])); let right = Box::new(Self::from_sorted_nodes(&mut nodes[midpoint..]));
let bounds = left.get_bounds().union(&right.get_bounds()); let bounds = left.get_bounds().union(&right.get_bounds());
BoundingVolumeHierarchy::Node { BoundingVolumeHierarchy::Node {
bounds, bounds,
@ -79,11 +80,11 @@ impl<T: Real> BoundingVolumeHierarchy<T> {
right, right,
} }
} else if nodes.len() == 1 { } else if nodes.len() == 1 {
let PrimitiveInfo(bounds, ref primitive) = nodes[0]; let PrimitiveInfo(bounds, ref mut primitive_src) = nodes[0];
BoundingVolumeHierarchy::Leaf { let mut primitive = None;
bounds, swap(primitive_src, &mut primitive);
primitive: Arc::clone(primitive), let primitive = primitive.unwrap();
} BoundingVolumeHierarchy::Leaf { bounds, primitive }
} else { } else {
BoundingVolumeHierarchy::None BoundingVolumeHierarchy::None
} }
@ -167,7 +168,7 @@ impl<T: Real> HasBoundingBox<T> for BoundingVolumeHierarchy<T> {
} }
} }
impl<T: Real> Primitive<T> for BoundingVolumeHierarchy<T> {} impl<T: Real> Aggregate<T> for BoundingVolumeHierarchy<T> {}
pub struct FilterIterator<'a, T: Real> { pub struct FilterIterator<'a, T: Real> {
unsearched_subtrees: Vec<&'a BoundingVolumeHierarchy<T>>, unsearched_subtrees: Vec<&'a BoundingVolumeHierarchy<T>>,
@ -186,8 +187,8 @@ impl<'a, T: Real> FilterIterator<'a, T> {
} }
} }
impl<T: Real> Iterator for FilterIterator<'_, T> { impl<'a, T: Real> Iterator for FilterIterator<'a, T> {
type Item = Arc<dyn Primitive<T>>; type Item = &'a dyn Primitive<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
//let mut result = Option::None; //let mut result = Option::None;
@ -203,9 +204,12 @@ impl<T: Real> Iterator for FilterIterator<'_, T> {
self.unsearched_subtrees.push(left); self.unsearched_subtrees.push(left);
} }
} }
BoundingVolumeHierarchy::Leaf { bounds, primitive } => { BoundingVolumeHierarchy::Leaf {
bounds,
ref primitive,
} => {
if (self.predicate)(bounds) { if (self.predicate)(bounds) {
return Some(Arc::clone(primitive)); return Some(&**primitive);
} }
} }
BoundingVolumeHierarchy::None => {} BoundingVolumeHierarchy::None => {}
@ -226,6 +230,8 @@ mod test {
use crate::raycasting::Sphere; use crate::raycasting::Sphere;
use nalgebra::Point3; use nalgebra::Point3;
use std::sync::Arc;
impl<T: Arbitrary + Real> Arbitrary for Sphere<T> { impl<T: Arbitrary + Real> Arbitrary for Sphere<T> {
fn arbitrary<G: Gen>(g: &mut G) -> Sphere<T> { fn arbitrary<G: Gen>(g: &mut G) -> Sphere<T> {
let centre = <Point3<T> as Arbitrary>::arbitrary(g); let centre = <Point3<T> as Arbitrary>::arbitrary(g);
@ -234,42 +240,20 @@ mod test {
} }
} }
fn sphere_vec_to_primitive_arc_vec<T: Real>( fn sphere_vec_to_primitive_box_vec<T: Real>(
spheres: &Vec<Sphere<T>>, spheres: &Vec<Sphere<T>>,
) -> Vec<Arc<dyn Primitive<T>>> { ) -> Vec<Box<dyn Primitive<T>>> {
let mut prims: Vec<Arc<dyn Primitive<T>>> = Vec::with_capacity(spheres.len()); let mut prims: Vec<Box<dyn Primitive<T>>> = Vec::with_capacity(spheres.len());
for sphere in spheres { for sphere in spheres {
prims.push(Arc::new(sphere.clone())); prims.push(Box::new(sphere.clone()));
} }
prims prims
} }
#[quickcheck] #[quickcheck]
fn contains_expected_number_of_primitives(spheres: Vec<Sphere<f32>>) -> bool { fn contains_expected_number_of_primitives(spheres: Vec<Sphere<f32>>) -> bool {
let target = let target = BoundingVolumeHierarchy::build(sphere_vec_to_primitive_box_vec(&spheres));
BoundingVolumeHierarchy::build(sphere_vec_to_primitive_arc_vec(&spheres).iter());
target.count_leaves() == spheres.len() target.count_leaves() == spheres.len()
} }
#[quickcheck]
fn finds_expected_points(spheres: Vec<Sphere<f32>>, p: Point3<f32>) -> bool {
let primitives = sphere_vec_to_primitive_arc_vec(&spheres);
let target = BoundingVolumeHierarchy::build(primitives.iter());
let expected_hits: Vec<Arc<dyn Primitive<f32>>> = primitives
.iter()
.filter(|elem| elem.bounding_box().contains_point(p))
.cloned()
.collect();
let found_hits: Vec<Arc<dyn Primitive<f32>>> = FilterIterator::new(
&target,
Box::new(move |elem: &BoundingBox<f32>| elem.contains_point(p)),
)
.collect();
expected_hits.iter().all(|expected_hit| {
found_hits
.iter()
.any(|found_hit| Arc::ptr_eq(found_hit, expected_hit))
})
}
} }

View File

@ -20,6 +20,8 @@ pub use axis_aligned_bounding_box::BoundingBox;
pub mod bounding_volume_hierarchy; pub mod bounding_volume_hierarchy;
pub use bounding_volume_hierarchy::BoundingVolumeHierarchy; pub use bounding_volume_hierarchy::BoundingVolumeHierarchy;
pub mod vec_aggregate;
/// A ray, consisting or a start point and direction /// A ray, consisting or a start point and direction
/// ///
/// 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
@ -123,13 +125,19 @@ pub trait HasBoundingBox<T: Real>: Send + Sync {
/// 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>: Send + Sync { pub trait Transform<T: Real> {
/// Create a new object by applying the transformation to this object. /// Create a new object by applying the transformation to this object.
fn transform(&mut self, transformation: &Affine3<T>) -> &Self; fn transform(&self, transformation: &Affine3<T>) -> 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<T: Real>: Intersect<T> + HasBoundingBox<T> {
// / Create a new object by applying the transformation to this object.
//fn transform(&self, transformation: &Affine3<T>) -> dyn Primitive<T>;
}
/// Either a primitive or a collection of primitives
pub trait Aggregate<T: Real>: Intersect<T> + HasBoundingBox<T> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View File

@ -7,6 +7,7 @@ use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive,
use std::sync::Arc; use std::sync::Arc;
#[derive(Clone)]
pub struct Plane<T: Real> { pub struct Plane<T: Real> {
normal: Vector3<T>, normal: Vector3<T>,
tangent: Vector3<T>, tangent: Vector3<T>,
@ -37,14 +38,16 @@ impl<T: Real> Plane<T> {
} }
impl<T: Real> Transform<T> for Plane<T> { impl<T: Real> Transform<T> for Plane<T> {
fn transform(&mut self, transformation: &Affine3<T>) -> &Self { fn transform(&self, transformation: &Affine3<T>) -> Self {
self.normal = transformation.transform_vector(&self.normal).normalize(); Plane {
self.cotangent = transformation.transform_vector(&self.cotangent).normalize(); normal: transformation.transform_vector(&self.normal).normalize(),
self.cotangent = self.normal.cross(&self.cotangent); cotangent: transformation.transform_vector(&self.cotangent).normalize(),
self.distance_from_origin = transformation tangent: self.normal.cross(&self.cotangent),
.transform_vector(&(self.normal * self.distance_from_origin)) distance_from_origin: transformation
.norm(); .transform_vector(&(self.normal * self.distance_from_origin))
self .norm(),
material: Arc::clone(&self.material),
}
} }
} }

View File

@ -25,14 +25,16 @@ impl<T: Real> Sphere<T> {
} }
impl<T: Real> Transform<T> for Sphere<T> { impl<T: Real> Transform<T> for Sphere<T> {
fn transform(&mut self, transformation: &Affine3<T>) -> &Self { fn transform(&self, transformation: &Affine3<T>) -> Self {
self.centre = transformation.transform_point(&self.centre); Sphere {
// This is not the most efficient way of calculating the radius, centre: transformation.transform_point(&self.centre),
//but will work as long as the resulting shape is still a sphere. // This is not the most efficient way of calculating the radius,
self.radius = transformation //but will work as long as the resulting shape is still a sphere.
.transform_vector(&Vector3::new(self.radius, T::zero(), T::zero())) radius: transformation
.norm(); .transform_vector(&Vector3::new(self.radius, T::zero(), T::zero()))
self .norm(),
material: Arc::clone(&self.material),
}
} }
} }
@ -207,7 +209,7 @@ mod tests {
if radius <= 0.0 { if radius <= 0.0 {
return TestResult::discard(); return TestResult::discard();
}; };
let mut sphere = Sphere::new( let sphere = Sphere::new(
sphere_centre, sphere_centre,
radius, radius,
Arc::new(LambertianMaterial::new_dummy()), Arc::new(LambertianMaterial::new_dummy()),
@ -215,7 +217,7 @@ mod tests {
let expected_centre = sphere.centre + translation_vector; let expected_centre = sphere.centre + translation_vector;
let mut transformation = Affine3::identity(); let mut transformation = Affine3::identity();
transformation *= Translation3::from(translation_vector); transformation *= Translation3::from(translation_vector);
sphere.transform(&transformation); let sphere = sphere.transform(&transformation);
TestResult::from_bool(expected_centre == sphere.centre) TestResult::from_bool(expected_centre == sphere.centre)
} }
@ -228,7 +230,7 @@ mod tests {
if radius <= 0.0 { if radius <= 0.0 {
return TestResult::discard(); return TestResult::discard();
}; };
let mut sphere = Sphere::new( let sphere = Sphere::new(
sphere_centre, sphere_centre,
radius, radius,
Arc::new(LambertianMaterial::new_dummy()), Arc::new(LambertianMaterial::new_dummy()),
@ -236,7 +238,7 @@ mod tests {
let expected_radius = sphere.radius; let expected_radius = sphere.radius;
let mut transformation = Affine3::identity(); let mut transformation = Affine3::identity();
transformation *= Translation3::from(translation_vector); transformation *= Translation3::from(translation_vector);
sphere.transform(&transformation); let sphere = sphere.transform(&transformation);
TestResult::from_bool(expected_radius == sphere.radius) TestResult::from_bool(expected_radius == sphere.radius)
} }
@ -249,7 +251,7 @@ mod tests {
if radius <= 0.0 { if radius <= 0.0 {
return TestResult::discard(); return TestResult::discard();
}; };
let mut sphere = Sphere::new( let sphere = Sphere::new(
sphere_centre, sphere_centre,
radius, radius,
Arc::new(LambertianMaterial::new_dummy()), Arc::new(LambertianMaterial::new_dummy()),
@ -259,7 +261,7 @@ mod tests {
transformation *= Translation3::from(sphere.centre.coords) transformation *= Translation3::from(sphere.centre.coords)
* Rotation3::new(rotation_vector) * Rotation3::new(rotation_vector)
* Translation3::from(-sphere.centre.coords); * Translation3::from(-sphere.centre.coords);
sphere.transform(&transformation); let sphere = sphere.transform(&transformation);
TestResult::from_bool(dbg!((expected_centre - sphere.centre).norm() < 0.000000001)) TestResult::from_bool(dbg!((expected_centre - sphere.centre).norm() < 0.000000001))
} }
} }

View File

@ -6,7 +6,7 @@ use nalgebra::{Affine3, Point3, Vector2, Vector3};
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Triangle<T: Real> { pub struct Triangle<T: Real> {
pub vertices: [Point3<T>; 3], pub vertices: [Point3<T>; 3],
pub normals: [Vector3<T>; 3], pub normals: [Vector3<T>; 3],
@ -14,18 +14,22 @@ pub struct Triangle<T: Real> {
} }
impl<T: Real> Transform<T> for Triangle<T> { impl<T: Real> Transform<T> for Triangle<T> {
fn transform(&mut self, transformation: &Affine3<T>) -> &Self { fn transform(&self, transformation: &Affine3<T>) -> Self {
for vertex in self.vertices.iter_mut() { let normal_transformation =
*vertex = transformation.transform_point(&vertex); Affine3::from_matrix_unchecked(transformation.inverse().matrix().transpose());
Triangle {
vertices: [
transformation.transform_point(&self.vertices[0]),
transformation.transform_point(&self.vertices[1]),
transformation.transform_point(&self.vertices[2]),
],
normals: [
normal_transformation.transform_vector(&self.normals[0]),
normal_transformation.transform_vector(&self.normals[1]),
normal_transformation.transform_vector(&self.normals[2]),
],
material: Arc::clone(&self.material),
} }
let normal_transformation = Affine3::from_matrix_unchecked(dbg!(dbg!(transformation)
.inverse()
.matrix()
.transpose()));
for normal in self.normals.iter_mut() {
*normal = dbg!(normal_transformation.transform_vector(dbg!(&normal)));
}
self
} }
} }
@ -181,12 +185,12 @@ mod tests {
let n0 = n0.normalize(); let n0 = n0.normalize();
let n1 = n1.normalize(); let n1 = n1.normalize();
let n2 = n2.normalize(); let n2 = n2.normalize();
let mut target = Triangle { let target = Triangle {
vertices: [v0, v1, v2], vertices: [v0, v1, v2],
normals: [n0, n1, n2], normals: [n0, n1, n2],
material: Arc::new(LambertianMaterial::new_dummy()), material: Arc::new(LambertianMaterial::new_dummy()),
}; };
target.transform(&Affine3::identity()); let target = target.transform(&Affine3::identity());
target.vertices[0] == v0 target.vertices[0] == v0
&& target.vertices[1] == v1 && target.vertices[1] == v1
&& target.vertices[2] == v2 && target.vertices[2] == v2
@ -208,13 +212,13 @@ mod tests {
let n0 = n0.normalize(); let n0 = n0.normalize();
let n1 = n1.normalize(); let n1 = n1.normalize();
let n2 = n2.normalize(); let n2 = n2.normalize();
let mut target = Triangle { let target = Triangle {
vertices: [v0, v1, v2], vertices: [v0, v1, v2],
normals: [n0, n1, n2], normals: [n0, n1, n2],
material: Arc::new(LambertianMaterial::new_dummy()), material: Arc::new(LambertianMaterial::new_dummy()),
}; };
let transformation = Affine3::identity() * Translation3::from(translation); let transformation = Affine3::identity() * Translation3::from(translation);
target.transform(&transformation); let target = target.transform(&transformation);
target.normals[0] == n0 && target.normals[1] == n1 && target.normals[2] == n2 target.normals[0] == n0 && target.normals[1] == n1 && target.normals[2] == n2
} }
@ -231,13 +235,13 @@ mod tests {
let n0 = n0.normalize(); let n0 = n0.normalize();
let n1 = n1.normalize(); let n1 = n1.normalize();
let n2 = n2.normalize(); let n2 = n2.normalize();
let mut target = Triangle { let target = Triangle {
vertices: [v0, v1, v2], vertices: [v0, v1, v2],
normals: [n0, n1, n2], normals: [n0, n1, n2],
material: Arc::new(LambertianMaterial::new_dummy()), material: Arc::new(LambertianMaterial::new_dummy()),
}; };
let transformation = Affine3::identity() * Translation3::from(translation); let transformation = Affine3::identity() * Translation3::from(translation);
target.transform(&transformation); let target = target.transform(&transformation);
target.vertices[0] == v0 + translation target.vertices[0] == v0 + translation
&& target.vertices[1] == v1 + translation && target.vertices[1] == v1 + translation
&& target.vertices[2] == v2 + translation && target.vertices[2] == v2 + translation

View File

@ -0,0 +1,49 @@
use super::{Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray};
use crate::Real;
impl<T: Real> HasBoundingBox<T> for Vec<Box<dyn Primitive<T>>> {
fn bounding_box(&self) -> BoundingBox<T> {
self.iter().fold(BoundingBox::empty(), |acc, elem| {
acc.union(&elem.bounding_box())
})
}
}
impl<T: Real> Intersect<T> for Vec<Box<dyn Primitive<T>>> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
self.iter()
.flat_map(|primitive| primitive.intersect(&ray))
.min_by(
|a, b| match PartialOrd::partial_cmp(&a.distance, &b.distance) {
None => std::cmp::Ordering::Less,
Some(ordering) => ordering,
},
)
}
}
impl<T: Real> Aggregate<T> for Vec<Box<dyn Primitive<T>>> {}
impl<T: Real> HasBoundingBox<T> for Vec<Box<dyn Aggregate<T>>> {
fn bounding_box(&self) -> BoundingBox<T> {
self.iter().fold(BoundingBox::empty(), |acc, elem| {
acc.union(&elem.bounding_box())
})
}
}
impl<T: Real> Intersect<T> for Vec<Box<dyn Aggregate<T>>> {
fn intersect<'a>(&'a self, ray: &Ray<T>) -> Option<IntersectionInfo<T>> {
self.iter()
.flat_map(|aggregate| aggregate.intersect(&ray))
.min_by(
|a, b| match PartialOrd::partial_cmp(&a.distance, &b.distance) {
None => std::cmp::Ordering::Less,
Some(ordering) => ordering,
},
)
}
}
impl<T: Real> Aggregate<T> for Vec<Box<dyn Aggregate<T>>> {}

View File

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