Random changes.
Some checks failed
continuous-integration/drone Build is failing

This commit is contained in:
2022-06-11 17:46:26 -07:00
parent 270a7ec349
commit e574cdb592
5 changed files with 477 additions and 6 deletions

View File

@@ -22,7 +22,7 @@ use crate::{
Float, BLACK,
};
const MAX_DEPTH_RECURSION: usize = 5;
const MAX_DEPTH_RECURSION: usize = 10;
#[derive(Copy, Clone, StructOpt, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]

View File

@@ -50,5 +50,6 @@ pub mod prelude {
transformations::view_transform,
tuples::{point, vector, Color},
world::{World, WorldBuilder},
Float,
};
}

View File

@@ -5,7 +5,7 @@ use derive_builder::Builder;
use crate::{
intersections::Intersections,
materials::{Material, MaterialBuilder},
matrices::Matrix4x4,
matrices::{identity, Matrix4x4},
rays::Ray,
tuples::Tuple,
};
@@ -25,6 +25,8 @@ pub enum Geometry {
Plane,
/// AABB cube at origin from -1,1 in each direction.
Cube,
/// Takes shape from children held within.
Group(Vec<Shape>),
}
impl Default for Geometry {
@@ -41,6 +43,7 @@ impl PartialEq for Geometry {
(Sphere, Sphere) => true,
(Plane, Plane) => true,
(Cube, Cube) => true,
(Group(v1), Group(v2)) => v1 == v2,
_ => false,
}
}
@@ -50,10 +53,9 @@ impl PartialEq for Geometry {
/// many different shapes based on the value of it's geometry field. Users chose the shape by
/// calling the appropriate constructor, i.e. [Shape::sphere].
#[derive(Builder, Debug, Clone, PartialEq)]
#[builder(default, pattern = "owned")]
#[builder(default, pattern = "owned", build_fn(skip))]
pub struct Shape {
transform: Matrix4x4,
#[builder(private, default = "self.default_inverse_transform()?")]
inverse_transform: Matrix4x4,
pub material: Material,
geometry: Geometry,
@@ -78,6 +80,11 @@ pub fn cube() -> ShapeBuilder {
ShapeBuilder::cube()
}
/// Short hand for creating a ShapeBuilder with a group geometry.
pub fn group() -> ShapeBuilder {
ShapeBuilder::group()
}
/// Helper for producing a sphere with a glassy material.
pub fn glass_sphere() -> ShapeBuilder {
ShapeBuilder::sphere().material(
@@ -107,9 +114,38 @@ impl ShapeBuilder {
pub fn cube() -> ShapeBuilder {
ShapeBuilder::default().geometry(Geometry::Cube)
}
/// Short hand for creating a ShapeBuilder with a group geometry.
pub fn group() -> ShapeBuilder {
ShapeBuilder::default().geometry(Geometry::Group(Vec::new()))
}
fn default_inverse_transform(&self) -> Result<Matrix4x4, String> {
Ok(self.transform.unwrap_or(Matrix4x4::identity()).inverse())
/// Add child shapes, only valid for Group::Geometry.
pub fn add_child(mut self, child: Shape) -> ShapeBuilder {
if let Some(Geometry::Group(ref mut children)) = &mut self.geometry {
children.push(child);
}
self
}
pub fn build(&self) -> Result<Shape, ShapeBuilderError> {
let mut s = Shape::default();
if let Some(transform) = &self.transform {
s.set_transform(transform.clone());
}
if let Some(material) = &self.material {
s.material = material.clone();
};
if let Some(geometry) = &self.geometry {
s.geometry = geometry.clone();
};
let transform = s.transform().clone();
if let Geometry::Group(ref mut children) = s.geometry {
children
.into_iter()
.for_each(|c| c.set_transform(transform * c.transform()));
}
Ok(s)
}
}
@@ -158,6 +194,14 @@ impl Shape {
geometry: Geometry::Cube,
}
}
pub fn group() -> Shape {
Shape {
transform: Matrix4x4::identity(),
inverse_transform: Matrix4x4::identity(),
material: Material::default(),
geometry: Geometry::Group(Vec::new()),
}
}
/// Find the normal at the point on the sphere.
pub fn normal_at(&self, world_point: Tuple) -> Tuple {
let object_point = self.inverse_transform * world_point;
@@ -166,6 +210,7 @@ impl Shape {
Geometry::Plane => Tuple::vector(0., 1., 0.),
Geometry::TestShape(_) => object_point,
Geometry::Cube => cube::local_normal_at(object_point),
Geometry::Group(_) => todo!("normal_at"),
};
let mut world_normal = self.inverse_transform.transpose() * object_normal;
world_normal.w = 0.;
@@ -197,6 +242,7 @@ pub fn intersect<'s>(shape: &'s Shape, ray: &Ray) -> Intersections<'s> {
Geometry::Plane => plane::intersect(shape, &local_ray),
Geometry::TestShape(_) => test_shape::intersect(shape, &local_ray),
Geometry::Cube => cube::intersect(shape, &local_ray),
Geometry::Group(_) => group::intersect(shape, &ray),
}
}
@@ -380,6 +426,83 @@ mod cube {
}
}
mod group {
use crate::{intersections::Intersections, rays::Ray, shapes::Shape};
use super::Geometry;
pub fn intersect<'s>(shape: &'s Shape, ray: &Ray) -> Intersections<'s> {
if let Geometry::Group(children) = &shape.geometry {
let mut intersections: Vec<_> = children
.iter()
.map(|c| super::intersect(c, ray))
.flatten()
.collect();
intersections.sort_by(|a, b| {
a.t.partial_cmp(&b.t)
.expect("an intersection has a t value that is NaN")
});
return Intersections::new(intersections);
}
unreachable!();
}
#[cfg(test)]
mod tests {
use crate::{
matrices::{scaling, translation},
rays::Ray,
shapes::{intersect, Shape, ShapeBuilder},
tuples::{point, vector},
};
#[test]
fn intersecting_empty_group() {
let g = Shape::group();
let r = Ray::new(point(0., 0., 0.), vector(0., 0., 1.));
let xs = intersect(&g, &r);
assert_eq!(xs.len(), 0);
}
#[test]
fn intersecting_nonempty_group() -> Result<(), Box<dyn std::error::Error>> {
let s1 = Shape::sphere();
let s2 = ShapeBuilder::sphere()
.transform(translation(0., 0., -3.))
.build()?;
let s3 = ShapeBuilder::sphere()
.transform(translation(5., 0., 0.))
.build()?;
let g = ShapeBuilder::group()
.add_child(s1.clone())
.add_child(s2.clone())
.add_child(s3.clone())
.build()?;
let r = Ray::new(point(0., 0., -5.), vector(0., 0., 1.));
let xs = intersect(&g, &r);
assert_eq!(xs.len(), 4);
assert_eq!(xs[0].object, &s2);
assert_eq!(xs[1].object, &s2);
assert_eq!(xs[2].object, &s1);
assert_eq!(xs[3].object, &s1);
Ok(())
}
#[test]
fn intersecting_transformed_group() -> Result<(), Box<dyn std::error::Error>> {
let g = ShapeBuilder::group()
.transform(scaling(2., 2., 2.))
.add_child(
ShapeBuilder::sphere()
.transform(translation(5., 0., 0.))
.build()?,
)
.build()?;
let r = Ray::new(point(10., 0., -10.), vector(0., 0., 1.));
let xs = intersect(&g, &r);
assert_eq!(xs.len(), 2);
Ok(())
}
}
}
#[cfg(test)]
mod tests {
mod shape_builder {