diff --git a/benches/simple_scene.rs b/benches/simple_scene.rs index be7c204..2a10d0f 100644 --- a/benches/simple_scene.rs +++ b/benches/simple_scene.rs @@ -1,14 +1,14 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use vanrijn::partial_render_scene; use vanrijn::colour::{ColourRgbF, NamedColour}; -use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial}; +use vanrijn::materials::ReflectiveMaterial; 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::util::Tile; -use nalgebra::{Point3, Vector3}; +use nalgebra::Point3; use std::path::Path; use std::sync::Arc; @@ -22,55 +22,17 @@ fn simple_scene(bencher: &mut Criterion) { let scene = Scene { camera_location: Point3::new(-2.0, 1.0, -5.0), - objects: vec![ - Box::new(Plane::new( - Vector3::new(0.0, 1.0, 0.0), - -2.0, - Arc::new(LambertianMaterial { - colour: ColourRgbF::new(0.55, 0.27, 0.04), - diffuse_strength: 0.1, - }), - )) as Box>, - 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, + objects: vec![Box::new(BoundingVolumeHierarchy::build( + load_obj( + &model_file_path, Arc::new(ReflectiveMaterial { - colour: ColourRgbF::from_named(NamedColour::Blue), - 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), + colour: ColourRgbF::from_named(NamedColour::Yellow), diffuse_strength: 0.05, - smoothness: 100.0, - specular_strength: 1.0, + reflection_strength: 0.9, }), - )), - Box::new(BoundingVolumeHierarchy::build( - &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(), - )), - ], + ) + .unwrap(), + ))], }; bencher.bench_function("simple_scene", |b| { diff --git a/src/main.rs b/src/main.rs index 341c699..fd3197d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,12 +13,14 @@ use nalgebra::{Point3, Vector3}; use std::path::Path; use std::sync::{mpsc, Arc}; -use vanrijn::partial_render_scene; use vanrijn::colour::{ColourRgbF, NamedColour}; use vanrijn::image::{ClampingToneMapper, ImageRgbU8, ToneMapper}; use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial}; 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::util::{Tile, TileIterator}; @@ -72,55 +74,56 @@ pub fn main() -> Result<(), Box> { println!("Loading object..."); let model_object = load_obj( &model_file_path, - Arc::new(PhongMaterial { - colour: ColourRgbF::from_named(NamedColour::Blue), - diffuse_strength: 0.05, - smoothness: 100.0, - specular_strength: 1.0, + Arc::new(ReflectiveMaterial { + colour: ColourRgbF::from_named(NamedColour::Yellow), + diffuse_strength: 0.01, + reflection_strength: 0.9, }), )?; println!("Building BVH..."); - let model_bvh = Box::new(BoundingVolumeHierarchy::build(&model_object)); + let model_bvh: Box> = Box::new(BoundingVolumeHierarchy::build(model_object)); println!("Constructing Scene..."); let scene = Scene { camera_location: Point3::new(-2.0, 1.0, -5.0), objects: vec![ - Box::new(Plane::new( - Vector3::new(0.0, 1.0, 0.0), - -2.0, - Arc::new(LambertianMaterial { - colour: ColourRgbF::new(0.55, 0.27, 0.04), - diffuse_strength: 0.1, - }), - )) as Box>, - 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 { - colour: ColourRgbF::from_named(NamedColour::Blue), - 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, - smoothness: 100.0, - specular_strength: 1.0, - }), - )), + Box::new(vec![ + Box::new(Plane::new( + Vector3::new(0.0, 1.0, 0.0), + -2.0, + Arc::new(LambertianMaterial { + colour: ColourRgbF::new(0.55, 0.27, 0.04), + diffuse_strength: 0.1, + }), + )) as Box>, + 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 { + colour: ColourRgbF::from_named(NamedColour::Blue), + 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, + smoothness: 100.0, + specular_strength: 1.0, + }), + )), + ]) as Box>, model_bvh, ], }; diff --git a/src/mesh.rs b/src/mesh.rs index e9484db..548b2fa 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -68,7 +68,7 @@ mod wavefront_obj { pub fn load_obj( filename: &Path, material: Arc>, - ) -> Result>>> + ) -> Result>>> where T: SupersetOf, { @@ -80,7 +80,7 @@ mod wavefront_obj { .flat_map(|object| object.groups.iter()) .flat_map(|group| group.polys.iter()) .flat_map(|poly| get_triangles(poly, &obj.position, &obj.normal, material.clone())) - .map(|triangle| Arc::new(triangle) as Arc>) + .map(|triangle| Box::new(triangle) as Box>) .collect()) } } diff --git a/src/raycasting/bounding_volume_hierarchy.rs b/src/raycasting/bounding_volume_hierarchy.rs index b8337c7..00f6b13 100644 --- a/src/raycasting/bounding_volume_hierarchy.rs +++ b/src/raycasting/bounding_volume_hierarchy.rs @@ -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::normalizer::Point3Normalizer; @@ -6,7 +8,7 @@ use crate::Real; use nalgebra::{convert, Point3}; -use std::sync::Arc; +use std::mem::swap; /// 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 /// doesn't intersect the [BoundingBox](BoundingBox) of the node doesn't intersect any of /// the primitives stored in it's children. -#[derive(Clone)] pub enum BoundingVolumeHierarchy { Node { bounds: BoundingBox, @@ -25,7 +26,7 @@ pub enum BoundingVolumeHierarchy { }, Leaf { bounds: BoundingBox, - primitive: Arc>, + primitive: Box>, }, None, } @@ -39,17 +40,17 @@ fn centre(bounds: &BoundingBox) -> Point3 { ) } -struct PrimitiveInfo(BoundingBox, Arc>); +struct PrimitiveInfo(BoundingBox, Option>>); impl BoundingVolumeHierarchy { pub fn build<'a, I>(primitives: I) -> Self where - I: IntoIterator>>, + I: IntoIterator>>, { Self::from_node_vec( primitives .into_iter() - .map(|primitive| PrimitiveInfo(primitive.bounding_box(), Arc::clone(primitive))) + .map(|primitive| PrimitiveInfo(primitive.bounding_box(), Some(primitive))) .collect(), ) } @@ -64,14 +65,14 @@ impl BoundingVolumeHierarchy { morton_order_value_3d(normalizer.normalize(centre(a))) .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]) -> Self { + fn from_sorted_nodes(nodes: &mut [PrimitiveInfo]) -> Self { if nodes.len() >= 2 { let midpoint = nodes.len() / 2; - let left = Box::new(Self::from_sorted_nodes(&nodes[..midpoint])); - let right = 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(&mut nodes[midpoint..])); let bounds = left.get_bounds().union(&right.get_bounds()); BoundingVolumeHierarchy::Node { bounds, @@ -79,11 +80,11 @@ impl BoundingVolumeHierarchy { right, } } else if nodes.len() == 1 { - let PrimitiveInfo(bounds, ref primitive) = nodes[0]; - BoundingVolumeHierarchy::Leaf { - bounds, - primitive: Arc::clone(primitive), - } + let PrimitiveInfo(bounds, ref mut primitive_src) = nodes[0]; + let mut primitive = None; + swap(primitive_src, &mut primitive); + let primitive = primitive.unwrap(); + BoundingVolumeHierarchy::Leaf { bounds, primitive } } else { BoundingVolumeHierarchy::None } @@ -167,7 +168,7 @@ impl HasBoundingBox for BoundingVolumeHierarchy { } } -impl Primitive for BoundingVolumeHierarchy {} +impl Aggregate for BoundingVolumeHierarchy {} pub struct FilterIterator<'a, T: Real> { unsearched_subtrees: Vec<&'a BoundingVolumeHierarchy>, @@ -186,8 +187,8 @@ impl<'a, T: Real> FilterIterator<'a, T> { } } -impl Iterator for FilterIterator<'_, T> { - type Item = Arc>; +impl<'a, T: Real> Iterator for FilterIterator<'a, T> { + type Item = &'a dyn Primitive; fn next(&mut self) -> Option { //let mut result = Option::None; @@ -203,9 +204,12 @@ impl Iterator for FilterIterator<'_, T> { self.unsearched_subtrees.push(left); } } - BoundingVolumeHierarchy::Leaf { bounds, primitive } => { + BoundingVolumeHierarchy::Leaf { + bounds, + ref primitive, + } => { if (self.predicate)(bounds) { - return Some(Arc::clone(primitive)); + return Some(&**primitive); } } BoundingVolumeHierarchy::None => {} @@ -226,6 +230,8 @@ mod test { use crate::raycasting::Sphere; use nalgebra::Point3; + use std::sync::Arc; + impl Arbitrary for Sphere { fn arbitrary(g: &mut G) -> Sphere { let centre = as Arbitrary>::arbitrary(g); @@ -234,42 +240,20 @@ mod test { } } - fn sphere_vec_to_primitive_arc_vec( + fn sphere_vec_to_primitive_box_vec( spheres: &Vec>, - ) -> Vec>> { - let mut prims: Vec>> = Vec::with_capacity(spheres.len()); + ) -> Vec>> { + let mut prims: Vec>> = Vec::with_capacity(spheres.len()); for sphere in spheres { - prims.push(Arc::new(sphere.clone())); + prims.push(Box::new(sphere.clone())); } prims } #[quickcheck] fn contains_expected_number_of_primitives(spheres: Vec>) -> bool { - let target = - BoundingVolumeHierarchy::build(sphere_vec_to_primitive_arc_vec(&spheres).iter()); + let target = BoundingVolumeHierarchy::build(sphere_vec_to_primitive_box_vec(&spheres)); target.count_leaves() == spheres.len() } - - #[quickcheck] - fn finds_expected_points(spheres: Vec>, p: Point3) -> bool { - let primitives = sphere_vec_to_primitive_arc_vec(&spheres); - let target = BoundingVolumeHierarchy::build(primitives.iter()); - let expected_hits: Vec>> = primitives - .iter() - .filter(|elem| elem.bounding_box().contains_point(p)) - .cloned() - .collect(); - let found_hits: Vec>> = FilterIterator::new( - &target, - Box::new(move |elem: &BoundingBox| elem.contains_point(p)), - ) - .collect(); - expected_hits.iter().all(|expected_hit| { - found_hits - .iter() - .any(|found_hit| Arc::ptr_eq(found_hit, expected_hit)) - }) - } } diff --git a/src/raycasting/mod.rs b/src/raycasting/mod.rs index 7c6bb91..72dc733 100644 --- a/src/raycasting/mod.rs +++ b/src/raycasting/mod.rs @@ -20,6 +20,8 @@ pub use axis_aligned_bounding_box::BoundingBox; pub mod bounding_volume_hierarchy; pub use bounding_volume_hierarchy::BoundingVolumeHierarchy; +pub mod vec_aggregate; + /// A ray, consisting or a start point and direction /// /// This is the basic ray struct used to define things like a line-of-sight @@ -123,13 +125,19 @@ pub trait HasBoundingBox: Send + Sync { /// Any geometric object which can have an affine transformation applied to it /// /// Used for moving, rotating or scaling primitives -pub trait Transform: Send + Sync { +pub trait Transform { /// Create a new object by applying the transformation to this object. - fn transform(&mut self, transformation: &Affine3) -> &Self; + fn transform(&self, transformation: &Affine3) -> Self; } /// A basic geometric primitive such as a sphere or a triangle -pub trait Primitive: Intersect + HasBoundingBox {} +pub trait Primitive: Intersect + HasBoundingBox { + // / Create a new object by applying the transformation to this object. + //fn transform(&self, transformation: &Affine3) -> dyn Primitive; +} + +/// Either a primitive or a collection of primitives +pub trait Aggregate: Intersect + HasBoundingBox {} #[cfg(test)] mod tests { diff --git a/src/raycasting/plane.rs b/src/raycasting/plane.rs index 64ae562..4298d1e 100644 --- a/src/raycasting/plane.rs +++ b/src/raycasting/plane.rs @@ -7,6 +7,7 @@ use super::{BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, use std::sync::Arc; +#[derive(Clone)] pub struct Plane { normal: Vector3, tangent: Vector3, @@ -37,14 +38,16 @@ impl Plane { } impl Transform for Plane { - fn transform(&mut self, transformation: &Affine3) -> &Self { - self.normal = transformation.transform_vector(&self.normal).normalize(); - self.cotangent = transformation.transform_vector(&self.cotangent).normalize(); - self.cotangent = self.normal.cross(&self.cotangent); - self.distance_from_origin = transformation - .transform_vector(&(self.normal * self.distance_from_origin)) - .norm(); - self + fn transform(&self, transformation: &Affine3) -> Self { + Plane { + normal: transformation.transform_vector(&self.normal).normalize(), + cotangent: transformation.transform_vector(&self.cotangent).normalize(), + tangent: self.normal.cross(&self.cotangent), + distance_from_origin: transformation + .transform_vector(&(self.normal * self.distance_from_origin)) + .norm(), + material: Arc::clone(&self.material), + } } } diff --git a/src/raycasting/sphere.rs b/src/raycasting/sphere.rs index 33e3692..a6304e6 100644 --- a/src/raycasting/sphere.rs +++ b/src/raycasting/sphere.rs @@ -25,14 +25,16 @@ impl Sphere { } impl Transform for Sphere { - fn transform(&mut self, transformation: &Affine3) -> &Self { - self.centre = transformation.transform_point(&self.centre); - // This is not the most efficient way of calculating the radius, - //but will work as long as the resulting shape is still a sphere. - self.radius = transformation - .transform_vector(&Vector3::new(self.radius, T::zero(), T::zero())) - .norm(); - self + fn transform(&self, transformation: &Affine3) -> Self { + Sphere { + centre: transformation.transform_point(&self.centre), + // This is not the most efficient way of calculating the radius, + //but will work as long as the resulting shape is still a sphere. + radius: transformation + .transform_vector(&Vector3::new(self.radius, T::zero(), T::zero())) + .norm(), + material: Arc::clone(&self.material), + } } } @@ -207,7 +209,7 @@ mod tests { if radius <= 0.0 { return TestResult::discard(); }; - let mut sphere = Sphere::new( + let sphere = Sphere::new( sphere_centre, radius, Arc::new(LambertianMaterial::new_dummy()), @@ -215,7 +217,7 @@ mod tests { let expected_centre = sphere.centre + translation_vector; let mut transformation = Affine3::identity(); transformation *= Translation3::from(translation_vector); - sphere.transform(&transformation); + let sphere = sphere.transform(&transformation); TestResult::from_bool(expected_centre == sphere.centre) } @@ -228,7 +230,7 @@ mod tests { if radius <= 0.0 { return TestResult::discard(); }; - let mut sphere = Sphere::new( + let sphere = Sphere::new( sphere_centre, radius, Arc::new(LambertianMaterial::new_dummy()), @@ -236,7 +238,7 @@ mod tests { let expected_radius = sphere.radius; let mut transformation = Affine3::identity(); transformation *= Translation3::from(translation_vector); - sphere.transform(&transformation); + let sphere = sphere.transform(&transformation); TestResult::from_bool(expected_radius == sphere.radius) } @@ -249,7 +251,7 @@ mod tests { if radius <= 0.0 { return TestResult::discard(); }; - let mut sphere = Sphere::new( + let sphere = Sphere::new( sphere_centre, radius, Arc::new(LambertianMaterial::new_dummy()), @@ -259,7 +261,7 @@ mod tests { transformation *= Translation3::from(sphere.centre.coords) * Rotation3::new(rotation_vector) * Translation3::from(-sphere.centre.coords); - sphere.transform(&transformation); + let sphere = sphere.transform(&transformation); TestResult::from_bool(dbg!((expected_centre - sphere.centre).norm() < 0.000000001)) } } diff --git a/src/raycasting/triangle.rs b/src/raycasting/triangle.rs index cce5895..3712f23 100644 --- a/src/raycasting/triangle.rs +++ b/src/raycasting/triangle.rs @@ -6,7 +6,7 @@ use nalgebra::{Affine3, Point3, Vector2, Vector3}; use std::sync::Arc; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Triangle { pub vertices: [Point3; 3], pub normals: [Vector3; 3], @@ -14,18 +14,22 @@ pub struct Triangle { } impl Transform for Triangle { - fn transform(&mut self, transformation: &Affine3) -> &Self { - for vertex in self.vertices.iter_mut() { - *vertex = transformation.transform_point(&vertex); + fn transform(&self, transformation: &Affine3) -> Self { + let normal_transformation = + 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 n1 = n1.normalize(); let n2 = n2.normalize(); - let mut target = Triangle { + let target = Triangle { vertices: [v0, v1, v2], normals: [n0, n1, n2], material: Arc::new(LambertianMaterial::new_dummy()), }; - target.transform(&Affine3::identity()); + let target = target.transform(&Affine3::identity()); target.vertices[0] == v0 && target.vertices[1] == v1 && target.vertices[2] == v2 @@ -208,13 +212,13 @@ mod tests { let n0 = n0.normalize(); let n1 = n1.normalize(); let n2 = n2.normalize(); - let mut target = Triangle { + let target = Triangle { vertices: [v0, v1, v2], normals: [n0, n1, n2], material: Arc::new(LambertianMaterial::new_dummy()), }; 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 } @@ -231,13 +235,13 @@ mod tests { let n0 = n0.normalize(); let n1 = n1.normalize(); let n2 = n2.normalize(); - let mut target = Triangle { + let target = Triangle { vertices: [v0, v1, v2], normals: [n0, n1, n2], material: Arc::new(LambertianMaterial::new_dummy()), }; let transformation = Affine3::identity() * Translation3::from(translation); - target.transform(&transformation); + let target = target.transform(&transformation); target.vertices[0] == v0 + translation && target.vertices[1] == v1 + translation && target.vertices[2] == v2 + translation diff --git a/src/raycasting/vec_aggregate.rs b/src/raycasting/vec_aggregate.rs new file mode 100644 index 0000000..2a42d1d --- /dev/null +++ b/src/raycasting/vec_aggregate.rs @@ -0,0 +1,49 @@ +use super::{Aggregate, BoundingBox, HasBoundingBox, Intersect, IntersectionInfo, Primitive, Ray}; +use crate::Real; + +impl HasBoundingBox for Vec>> { + fn bounding_box(&self) -> BoundingBox { + self.iter().fold(BoundingBox::empty(), |acc, elem| { + acc.union(&elem.bounding_box()) + }) + } +} + +impl Intersect for Vec>> { + fn intersect<'a>(&'a self, ray: &Ray) -> Option> { + 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 Aggregate for Vec>> {} + + +impl HasBoundingBox for Vec>> { + fn bounding_box(&self) -> BoundingBox { + self.iter().fold(BoundingBox::empty(), |acc, elem| { + acc.union(&elem.bounding_box()) + }) + } +} + +impl Intersect for Vec>> { + fn intersect<'a>(&'a self, ray: &Ray) -> Option> { + 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 Aggregate for Vec>> {} diff --git a/src/scene.rs b/src/scene.rs index 660f330..1980293 100644 --- a/src/scene.rs +++ b/src/scene.rs @@ -1,9 +1,9 @@ use nalgebra::{Point3}; -use crate::raycasting::Primitive; +use crate::raycasting::Aggregate; use crate::Real; pub struct Scene { pub camera_location: Point3, - pub objects: Vec>>, + pub objects: Vec>>, }