Moved node & material to own files

This commit is contained in:
STP
2023-11-28 13:22:41 -05:00
parent 6b5f54d14d
commit f2a127ace7
4 changed files with 191 additions and 379 deletions

48
src/material.rs Normal file
View File

@@ -0,0 +1,48 @@
#[allow(dead_code)]
use nalgebra::Vector3;
// MATERIAL -----------------------------------------------------------------
#[derive(Clone)]
pub struct Material {
pub kd: Vector3<f32>,
pub ks: Vector3<f32>,
pub shininess: f32,
}
impl Material {
pub fn new(kd: Vector3<f64>, ks: Vector3<f64>, shininess: f64) -> Material {
let kd = kd.cast();
let ks = ks.cast();
let shininess = shininess as f32;
Material { kd, ks, shininess }
}
pub fn magenta() -> Material {
let kd = Vector3::new(1.0, 0.0, 1.0);
let ks = Vector3::new(1.0, 0.0, 1.0);
let shininess = 0.5;
Material { kd, ks, shininess }
}
pub fn turquoise() -> Material {
let kd = Vector3::new(0.25, 0.3, 0.7);
let ks = Vector3::new(0.25, 0.3, 0.7);
let shininess = 0.5;
Material { kd, ks, shininess }
}
pub fn red() -> Material {
let kd = Vector3::new(0.8, 0.0, 0.3);
let ks = Vector3::new(0.8, 0.3, 0.0);
let shininess = 0.5;
Material { kd, ks, shininess }
}
pub fn blue() -> Material {
let kd = Vector3::new(0.0, 0.3, 0.6);
let ks = Vector3::new(0.3, 0.0, 0.6);
let shininess = 0.5;
Material { kd, ks, shininess }
}
pub fn green() -> Material {
let kd = Vector3::new(0.0, 1.0, 0.0);
let ks = Vector3::new(0.0, 1.0, 0.0);
let shininess = 0.5;
Material { kd, ks, shininess }
}
}

97
src/node.rs Normal file
View File

@@ -0,0 +1,97 @@
use crate::{material::Material, primitive::*};
use nalgebra::{Matrix4, Vector3};
use std::rc::Rc;
#[derive(Clone)]
pub struct Node {
//Primitive
pub primitive: Rc<dyn Primitive>,
pub material: Material,
//Transformations
pub rotation: [f64; 3],
pub scale: [f64; 3],
pub translation: [f64; 3],
//Model matricies
pub model: Matrix4<f64>,
pub inv_model: Matrix4<f64>,
pub active: bool,
}
impl Node {
//New node with no transformations
pub fn new(primitive: Rc<dyn Primitive>, material: Material) -> Node {
Node {
primitive,
material,
rotation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0],
translation: [0.0, 0.0, 0.0],
model: Matrix4::identity(),
inv_model: Matrix4::identity(),
active: true,
}
}
//New node with parent transformations
pub fn child(self, primitive: Rc<dyn Primitive>) -> Node {
let mut child = self.clone();
child.primitive = primitive;
child
}
//Toggle is a mesh is visible or not
pub fn set_active(&mut self, active: bool) {
self.active = active;
}
//Rotate a mesh by adding to its rotation
pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) {
//Convert to radians
let roll = roll.to_radians();
// Convert pitch and yaw to radians
let pitch = pitch.to_radians();
let yaw = yaw.to_radians();
// Add the roll, pitch, and yaw to the current rotation
self.rotation[0] += roll;
self.rotation[1] += pitch;
self.rotation[2] += yaw;
// Recompute the model and inverse model matrices
self.compute();
}
// Translate a mesh by adding to its current position
pub fn translate(&mut self, x: f64, y: f64, z: f64) {
self.translation[0] += x;
self.translation[1] += y;
self.translation[2] += z;
// Recompute the model and inverse model matrices
self.compute();
}
// Scale a mesh by adding to its current scale
pub fn scale(&mut self, x: f64, y: f64, z: f64) {
self.scale[0] += x;
self.scale[1] += y;
self.scale[2] += z;
// Recompute the model and inverse model matrices
self.compute();
}
// This function computes the model and inverse model matrices
pub fn compute(&mut self) {
//Translation matrix
let translation = Vector3::from_row_slice(&self.translation);
let translation_matrix = Matrix4::new_translation(&translation);
// Scale matrix
let scale = &Vector3::from_row_slice(&self.scale);
let scale_matrix = Matrix4::new_nonuniform_scaling(&scale);
// Rotation matrix
let (roll, pitch, yaw) = (self.rotation[0], self.rotation[1], self.rotation[2]);
let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw);
// Compute the model matrix by combining the translation, rotation, and scale matrices
self.model = (translation_matrix * rotation_matrix * scale_matrix).cast();
// Compute the inverse model matrix by inverting the model matrix
self.inv_model = self.model.try_inverse().unwrap();
}
}

View File

@@ -1,127 +1,19 @@
use crate::{
bvh::BoundingBox,
ray::{Intersection, Ray},
{EPSILON, INFINITY},
};
#[allow(dead_code)] #[allow(dead_code)]
use crate::ray::Ray; use nalgebra::{distance, Point3, Vector3};
use crate::{EPSILON, INFINITY};
use nalgebra::{distance, Matrix4, Point3, Vector3};
use roots::{find_roots_quadratic, find_roots_quartic, Roots}; use roots::{find_roots_quadratic, find_roots_quartic, Roots};
use std::fs::File; use std::fs::File;
use std::io::{BufRead, BufReader}; use std::io::{BufRead, BufReader};
use std::rc::Rc; use std::rc::Rc;
// MATERIAL -----------------------------------------------------------------
#[derive(Clone)]
pub struct Material {
pub kd: Vector3<f32>,
pub ks: Vector3<f32>,
pub shininess: f32,
}
impl Material {
pub fn new(kd: Vector3<f64>, ks: Vector3<f64>, shininess: f64) -> Rc<Self> {
let kd = kd.cast();
let ks = ks.cast();
let shininess = shininess as f32;
Rc::new(Material { kd, ks, shininess })
}
pub fn magenta() -> Rc<Self> {
let kd = Vector3::new(1.0, 0.0, 1.0);
let ks = Vector3::new(1.0, 0.0, 1.0);
let shininess = 0.5;
Rc::new(Material { kd, ks, shininess })
}
pub fn turquoise() -> Rc<Self> {
let kd = Vector3::new(0.25, 0.3, 0.7);
let ks = Vector3::new(0.25, 0.3, 0.7);
let shininess = 0.5;
Rc::new(Material { kd, ks, shininess })
}
pub fn red() -> Rc<Self> {
let kd = Vector3::new(0.8, 0.0, 0.3);
let ks = Vector3::new(0.8, 0.3, 0.0);
let shininess = 0.5;
Rc::new(Material { kd, ks, shininess })
}
pub fn blue() -> Rc<Self> {
let kd = Vector3::new(0.0, 0.3, 0.6);
let ks = Vector3::new(0.3, 0.0, 0.6);
let shininess = 0.5;
Rc::new(Material { kd, ks, shininess })
}
pub fn green() -> Rc<Self> {
let kd = Vector3::new(0.0, 1.0, 0.0);
let ks = Vector3::new(0.0, 1.0, 0.0);
let shininess = 0.5;
Rc::new(Material { kd, ks, shininess })
}
}
// INTERSECTION -----------------------------------------------------------------
pub struct Intersection {
// Information about an intersection
pub point: Point3<f64>,
pub normal: Vector3<f64>,
pub incidence: Vector3<f64>,
pub material: Rc<Material>,
pub distance: f64,
}
impl Intersection {
pub fn transform(&self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) -> Intersection {
Intersection {
point: trans.transform_point(&self.point),
normal: inv_trans.transpose().transform_vector(&self.normal),
incidence: trans.transform_vector(&self.incidence),
material: self.material.clone(),
distance: self.distance,
}
}
}
// BOUNDING BOX -----------------------------------------------------------------
#[derive(Clone)]
struct BoundingBox {
bln: Point3<f64>,
trf: Point3<f64>,
}
impl BoundingBox {
fn new(bln: Point3<f64>, trf: Point3<f64>) -> Self {
let bln = bln + Vector3::new(EPSILON, EPSILON, EPSILON);
let trf = trf - Vector3::new(EPSILON, EPSILON, EPSILON);
BoundingBox { bln, trf }
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
let bln = &self.bln;
let trf = &self.trf;
let t1 = (bln - ray.a).component_div(&ray.b);
let t2 = (trf - ray.a).component_div(&ray.b);
let tmin = t1.inf(&t2).min();
let tmax = t1.sup(&t2).max();
if tmax >= tmin {
let intersect = ray.at_t(tmin);
// Check if the intersection is inside the box
if intersect.x > bln.x - EPSILON
|| intersect.x < trf.x + EPSILON
|| intersect.y > bln.y - EPSILON
|| intersect.y < trf.y + EPSILON
|| intersect.z > bln.z - EPSILON
|| intersect.z < trf.z + EPSILON
{
return true; // Intersection is outside the box
}
}
false
}
#[allow(dead_code)]
fn get_centroid(&self) -> Point3<f64> {
self.bln + (self.trf - self.bln) / 2.0
}
}
// PRIMITIVE TRAIT ----------------------------------------------------------------- // PRIMITIVE TRAIT -----------------------------------------------------------------
pub trait Primitive { pub trait Primitive {
fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>; fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>;
fn intersect_bounding_box(&self, ray: &Ray) -> bool; fn intersect_bounding_box(&self, ray: &Ray) -> bool;
fn get_material(&self) -> Rc<Material>;
} }
// SPHERE ----------------------------------------------------------------- // SPHERE -----------------------------------------------------------------
@@ -130,11 +22,10 @@ pub struct Sphere {
position: Point3<f64>, position: Point3<f64>,
radius: f64, radius: f64,
bounding_box: BoundingBox, bounding_box: BoundingBox,
material: Rc<Material>,
} }
impl Sphere { impl Sphere {
pub fn new(position: Point3<f64>, radius: f64, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(position: Point3<f64>, radius: f64) -> Rc<dyn Primitive> {
let radius_vec = Vector3::new(radius, radius, radius); let radius_vec = Vector3::new(radius, radius, radius);
let bln = position - radius_vec; let bln = position - radius_vec;
let trf = position + radius_vec; let trf = position + radius_vec;
@@ -143,12 +34,11 @@ impl Sphere {
position, position,
radius, radius,
bounding_box, bounding_box,
material,
}) })
} }
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn unit() -> Rc<dyn Primitive> {
Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0, material) Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0)
} }
} }
@@ -184,16 +74,10 @@ impl Primitive for Sphere {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
return self.bounding_box.intersect_bounding_box(ray); return self.bounding_box.intersect_bounding_box(ray);
} }
@@ -205,17 +89,11 @@ pub struct Circle {
position: Point3<f64>, position: Point3<f64>,
radius: f64, radius: f64,
normal: Vector3<f64>, normal: Vector3<f64>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Circle { impl Circle {
pub fn new( pub fn new(position: Point3<f64>, radius: f64, normal: Vector3<f64>) -> Rc<dyn Primitive> {
position: Point3<f64>,
radius: f64,
normal: Vector3<f64>,
material: Rc<Material>,
) -> Rc<dyn Primitive> {
let radius_vec = Vector3::new(radius, radius, radius); let radius_vec = Vector3::new(radius, radius, radius);
let bln = position - radius_vec; let bln = position - radius_vec;
let trf = position + radius_vec; let trf = position + radius_vec;
@@ -224,16 +102,14 @@ impl Circle {
position, position,
radius, radius,
normal: normal.normalize(), normal: normal.normalize(),
material,
bounding_box, bounding_box,
}) })
} }
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn unit() -> Rc<dyn Primitive> {
let position = Point3::new(0.0, 0.0, 0.0); let position = Point3::new(0.0, 0.0, 0.0);
let normal = Vector3::new(0.0, 1.0, 0.0); let normal = Vector3::new(0.0, 1.0, 0.0);
let radius = 1.0; let radius = 1.0;
let material = material;
let bln = Point3::new(-radius, 0.0, -EPSILON); let bln = Point3::new(-radius, 0.0, -EPSILON);
let trf = Point3::new(radius, 0.0, EPSILON); let trf = Point3::new(radius, 0.0, EPSILON);
@@ -243,7 +119,6 @@ impl Circle {
position, position,
normal, normal,
radius, radius,
material,
bounding_box, bounding_box,
}) })
} }
@@ -265,18 +140,12 @@ impl Primitive for Circle {
return Some(Intersection { return Some(Intersection {
point: intersect, point: intersect,
normal: self.normal.normalize(), normal: self.normal.normalize(),
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
} }
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
@@ -289,23 +158,20 @@ pub struct Cylinder {
height: f64, height: f64,
base_circle: Rc<dyn Primitive>, base_circle: Rc<dyn Primitive>,
top_circle: Rc<dyn Primitive>, top_circle: Rc<dyn Primitive>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Cylinder { impl Cylinder {
pub fn new(radius: f64, height: f64, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(radius: f64, height: f64) -> Rc<dyn Primitive> {
let base_circle = Circle::new( let base_circle = Circle::new(
Point3::new(0.0, 0.0, 0.0), Point3::new(0.0, 0.0, 0.0),
radius, radius,
Vector3::new(0.0, -1.0, 0.0), Vector3::new(0.0, -1.0, 0.0),
Rc::clone(&material),
); );
let top_circle = Circle::new( let top_circle = Circle::new(
Point3::new(0.0, height, 0.0), Point3::new(0.0, height, 0.0),
radius, radius,
Vector3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0),
Rc::clone(&material),
); );
let bln = Point3::new(-radius, 0.0, -radius); let bln = Point3::new(-radius, 0.0, -radius);
let trf = Point3::new(radius, height, radius); let trf = Point3::new(radius, height, radius);
@@ -314,7 +180,6 @@ impl Cylinder {
height, height,
base_circle, base_circle,
top_circle, top_circle,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -356,8 +221,6 @@ impl Primitive for Cylinder {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: normal, normal: normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} else { } else {
@@ -400,10 +263,6 @@ impl Primitive for Cylinder {
} }
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
@@ -416,17 +275,15 @@ pub struct Cone {
base: f64, base: f64,
apex: f64, apex: f64,
circle: Rc<dyn Primitive>, circle: Rc<dyn Primitive>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Cone { impl Cone {
pub fn new(radius: f64, apex: f64, base: f64, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(radius: f64, apex: f64, base: f64) -> Rc<dyn Primitive> {
let circle = Circle::new( let circle = Circle::new(
Point3::new(0.0, base, 0.0), Point3::new(0.0, base, 0.0),
radius, radius,
Vector3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0),
Rc::clone(&material),
); );
let bln = Point3::new(-radius, base, -radius); let bln = Point3::new(-radius, base, -radius);
let trf = Point3::new(radius, base + apex, radius); let trf = Point3::new(radius, base + apex, radius);
@@ -435,12 +292,11 @@ impl Cone {
base, base,
apex, apex,
circle, circle,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn unit() -> Rc<dyn Primitive> {
Cone::new(1.0, 2.0, -1.0, material) Cone::new(1.0, 2.0, -1.0)
} }
pub fn get_normal(&self, intersect: Point3<f64>) -> Vector3<f64> { pub fn get_normal(&self, intersect: Point3<f64>) -> Vector3<f64> {
@@ -489,8 +345,6 @@ impl Primitive for Cone {
true => Some(Intersection { true => Some(Intersection {
point: intersect, point: intersect,
normal: self.get_normal(intersect), normal: self.get_normal(intersect),
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}), }),
false => None, false => None,
@@ -515,10 +369,6 @@ impl Primitive for Cone {
} }
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
@@ -530,7 +380,6 @@ pub struct Rectangle {
position: Point3<f64>, position: Point3<f64>,
normal: Vector3<f64>, normal: Vector3<f64>,
width_direction: Vector3<f64>, width_direction: Vector3<f64>,
material: Rc<Material>,
width: f64, width: f64,
height: f64, height: f64,
bounding_box: BoundingBox, bounding_box: BoundingBox,
@@ -543,7 +392,6 @@ impl Rectangle {
width_direction: Vector3<f64>, width_direction: Vector3<f64>,
width: f64, width: f64,
height: f64, height: f64,
material: Rc<Material>,
) -> Rc<dyn Primitive> { ) -> Rc<dyn Primitive> {
let normal = normal.normalize(); let normal = normal.normalize();
let width_direction = width_direction.normalize(); let width_direction = width_direction.normalize();
@@ -556,18 +404,16 @@ impl Rectangle {
width_direction: width_direction.normalize(), width_direction: width_direction.normalize(),
width, width,
height, height,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn unit() -> Rc<dyn Primitive> {
Rectangle::new( Rectangle::new(
Point3::new(0.0, 0.0, 0.0), Point3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0),
Vector3::new(1.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0),
2.0, 2.0,
2.0, 2.0,
material,
) )
} }
} }
@@ -595,18 +441,12 @@ impl Primitive for Rectangle {
return Some(Intersection { return Some(Intersection {
point: intersect, point: intersect,
normal: self.normal, normal: self.normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}); });
} }
None None
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
@@ -617,24 +457,22 @@ impl Primitive for Rectangle {
pub struct Cube { pub struct Cube {
bln: Point3<f64>, bln: Point3<f64>,
trf: Point3<f64>, trf: Point3<f64>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Cube { impl Cube {
pub fn new(bln: Point3<f64>, trf: Point3<f64>, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(bln: Point3<f64>, trf: Point3<f64>) -> Rc<dyn Primitive> {
Rc::new(Cube { Rc::new(Cube {
bln, bln,
trf, trf,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn unit() -> Rc<dyn Primitive> {
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
Cube::new(bln, trf, material) Cube::new(bln, trf)
} }
} }
@@ -651,17 +489,17 @@ impl Primitive for Cube {
let tmax = t1.sup(&t2).min(); let tmax = t1.sup(&t2).min();
// Check if there's an intersection between tmin and tmax // Check if there's an intersection between tmin and tmax
if tmax >= tmin { if tmax >= tmin && tmin > EPSILON {
// The ray intersects the box, and tmin is the entry point, tmax is the exit point // The ray intersects the box, and tmin is the entry point, tmax is the exit point
let intersect = ray.at_t(tmin); let intersect = ray.at_t(tmin);
// Check if the intersection is outside the box // Check if the intersection is outside the box
if intersect.x < bln.x - EPSILON if intersect.x < bln.x
|| intersect.x > trf.x + EPSILON || intersect.x > trf.x
|| intersect.y < bln.y - EPSILON || intersect.y < bln.y
|| intersect.y > trf.y + EPSILON || intersect.y > trf.y
|| intersect.z < bln.z - EPSILON || intersect.z < bln.z
|| intersect.z > trf.z + EPSILON || intersect.z > trf.z
{ {
return None; // Intersection is outside the box return None; // Intersection is outside the box
} }
@@ -684,8 +522,6 @@ impl Primitive for Cube {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: normal, normal: normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: tmin, distance: tmin,
}) })
} else { } else {
@@ -696,10 +532,6 @@ impl Primitive for Cube {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
// TRIANGLE ----------------------------------------------------------------- // TRIANGLE -----------------------------------------------------------------
@@ -710,17 +542,11 @@ pub struct Triangle {
v: Point3<f64>, v: Point3<f64>,
w: Point3<f64>, w: Point3<f64>,
normal: Vector3<f64>, normal: Vector3<f64>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Triangle { impl Triangle {
pub fn new( pub fn new(u: Point3<f64>, v: Point3<f64>, w: Point3<f64>) -> Rc<dyn Primitive> {
u: Point3<f64>,
v: Point3<f64>,
w: Point3<f64>,
material: Rc<Material>,
) -> Rc<dyn Primitive> {
let uv = v - u; let uv = v - u;
let uw = w - u; let uw = w - u;
let normal = uv.cross(&uw).normalize(); let normal = uv.cross(&uw).normalize();
@@ -732,17 +558,15 @@ impl Triangle {
v, v,
w, w,
normal, normal,
material,
bounding_box, bounding_box,
}) })
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn unit() -> Rc<dyn Primitive> {
let u = Point3::new(-1.0, 0.0, -1.0); let u = Point3::new(-1.0, -1.0, 0.0);
let v = Point3::new(0.0, 0.0, 1.0); let v = Point3::new(0.0, 1.0, 0.0);
let w = Point3::new(1.0, 0.0, -1.0); let w = Point3::new(1.0, -1.0, 0.0);
let material = material; Triangle::new(u, v, w)
Triangle::new(u, v, w, material)
} }
} }
@@ -777,8 +601,6 @@ impl Primitive for Triangle {
Some(Intersection { Some(Intersection {
point: intersect, point: intersect,
normal: normal, normal: normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} else { } else {
@@ -786,10 +608,6 @@ impl Primitive for Triangle {
} }
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
@@ -799,17 +617,15 @@ impl Primitive for Triangle {
#[derive(Clone)] #[derive(Clone)]
pub struct Mesh { pub struct Mesh {
triangles: Vec<Triangle>, triangles: Vec<Triangle>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Mesh { impl Mesh {
pub fn new(triangles: Vec<Triangle>, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(triangles: Vec<Triangle>) -> Rc<dyn Primitive> {
// Calculate the bounding box for the entire mesh based on the bounding boxes of individual triangles // Calculate the bounding box for the entire mesh based on the bounding boxes of individual triangles
let bounding_box = Mesh::compute_bounding_box(&triangles); let bounding_box = Mesh::compute_bounding_box(&triangles);
Rc::new(Mesh { Rc::new(Mesh {
triangles, triangles,
material,
bounding_box, bounding_box,
}) })
} }
@@ -828,7 +644,7 @@ impl Mesh {
BoundingBox { bln, trf } BoundingBox { bln, trf }
} }
pub fn from_file(filename: &str, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn from_file(filename: &str) -> Rc<dyn Primitive> {
let mut triangles: Vec<Triangle> = Vec::new(); let mut triangles: Vec<Triangle> = Vec::new();
let mut vertices: Vec<Point3<f64>> = Vec::new(); let mut vertices: Vec<Point3<f64>> = Vec::new();
@@ -872,13 +688,11 @@ impl Mesh {
let bln = u.inf(&v).inf(&w); let bln = u.inf(&v).inf(&w);
let trf = u.sup(&v).sup(&w); let trf = u.sup(&v).sup(&w);
let bounding_box = BoundingBox { bln, trf }; let bounding_box = BoundingBox { bln, trf };
let material = material.clone();
triangles.push(Triangle { triangles.push(Triangle {
u, u,
v, v,
w, w,
normal, normal,
material,
bounding_box, bounding_box,
}); });
} }
@@ -888,7 +702,7 @@ impl Mesh {
} }
} }
} }
Mesh::new(triangles, material) Mesh::new(triangles)
} }
} }
@@ -913,10 +727,6 @@ impl Primitive for Mesh {
closest_intersect closest_intersect
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
@@ -927,19 +737,17 @@ impl Primitive for Mesh {
pub struct Torus { pub struct Torus {
inner_rad: f64, inner_rad: f64,
outer_rad: f64, outer_rad: f64,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Torus { impl Torus {
pub fn new(inner_rad: f64, outer_rad: f64, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(inner_rad: f64, outer_rad: f64) -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
Rc::new(Torus { Rc::new(Torus {
inner_rad, inner_rad,
outer_rad, outer_rad,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -1042,8 +850,6 @@ impl Primitive for Torus {
Some(Intersection { Some(Intersection {
point, point,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
@@ -1051,10 +857,6 @@ impl Primitive for Torus {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
// GNOMON ----------------------------------------------------------------- // GNOMON -----------------------------------------------------------------
@@ -1063,28 +865,24 @@ pub struct Gnonom {
x_cube: Rc<dyn Primitive>, x_cube: Rc<dyn Primitive>,
y_cube: Rc<dyn Primitive>, y_cube: Rc<dyn Primitive>,
z_cube: Rc<dyn Primitive>, z_cube: Rc<dyn Primitive>,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Gnonom { impl Gnonom {
const GNONOM_WIDTH: f64 = 0.1; const GNONOM_WIDTH: f64 = 0.1;
const GNONOM_LENGTH: f64 = 2.0; const GNONOM_LENGTH: f64 = 2.0;
pub fn new(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new() -> Rc<dyn Primitive> {
let x_cube = Cube::new( let x_cube = Cube::new(
Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH), Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH),
Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH), Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH),
material.clone(),
); );
let y_cube = Cube::new( let y_cube = Cube::new(
Point3::new(-Self::GNONOM_WIDTH, 0.0, -Self::GNONOM_WIDTH), Point3::new(-Self::GNONOM_WIDTH, 0.0, -Self::GNONOM_WIDTH),
Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_LENGTH, Self::GNONOM_WIDTH), Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_LENGTH, Self::GNONOM_WIDTH),
material.clone(),
); );
let z_cube = Cube::new( let z_cube = Cube::new(
Point3::new(-Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH, 0.0), Point3::new(-Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH, 0.0),
Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_WIDTH, Self::GNONOM_LENGTH), Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_WIDTH, Self::GNONOM_LENGTH),
material.clone(),
); );
let bounding_box = BoundingBox { let bounding_box = BoundingBox {
bln: Point3::new( bln: Point3::new(
@@ -1102,7 +900,6 @@ impl Gnonom {
x_cube, x_cube,
y_cube, y_cube,
z_cube, z_cube,
material,
bounding_box, bounding_box,
}) })
} }
@@ -1128,26 +925,20 @@ impl Primitive for Gnonom {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
self.material.clone()
}
} }
// CROSS CAP --------- // CROSS CAP ---------
#[derive(Clone)] #[derive(Clone)]
pub struct CrossCap { pub struct CrossCap {
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl CrossCap { impl CrossCap {
pub fn new(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new() -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
Rc::new(CrossCap { Rc::new(CrossCap {
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -1219,8 +1010,6 @@ impl Primitive for CrossCap {
Some(Intersection { Some(Intersection {
point, point,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
@@ -1228,10 +1017,6 @@ impl Primitive for CrossCap {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
// CROSS CAP 2 --------- // CROSS CAP 2 ---------
@@ -1239,19 +1024,17 @@ impl Primitive for CrossCap {
pub struct CrossCap2 { pub struct CrossCap2 {
p: f64, p: f64,
q: f64, q: f64,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl CrossCap2 { impl CrossCap2 {
pub fn new(p: f64, q: f64, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(p: f64, q: f64) -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
Rc::new(CrossCap2 { Rc::new(CrossCap2 {
p, p,
q, q,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -1348,8 +1131,6 @@ impl Primitive for CrossCap2 {
Some(Intersection { Some(Intersection {
point, point,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
@@ -1357,26 +1138,20 @@ impl Primitive for CrossCap2 {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
// Steiner --------- // Steiner ---------
#[derive(Clone)] #[derive(Clone)]
pub struct Steiner { pub struct Steiner {
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Steiner { impl Steiner {
pub fn new(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new() -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
Rc::new(Steiner { Rc::new(Steiner {
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -1437,8 +1212,6 @@ impl Primitive for Steiner {
Some(Intersection { Some(Intersection {
point, point,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
@@ -1446,26 +1219,20 @@ impl Primitive for Steiner {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
// Steiner 2 --------- // Steiner 2 ---------
#[derive(Clone)] #[derive(Clone)]
pub struct Steiner2 { pub struct Steiner2 {
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Steiner2 { impl Steiner2 {
pub fn new(material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new() -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
Rc::new(Steiner2 { Rc::new(Steiner2 {
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -1537,8 +1304,6 @@ impl Primitive for Steiner2 {
Some(Intersection { Some(Intersection {
point, point,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
@@ -1546,28 +1311,22 @@ impl Primitive for Steiner2 {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
// Roman --------- // Roman ---------
#[derive(Clone)] #[derive(Clone)]
pub struct Roman { pub struct Roman {
k: f64, k: f64,
material: Rc<Material>,
bounding_box: BoundingBox, bounding_box: BoundingBox,
} }
impl Roman { impl Roman {
pub fn new(k: f64, material: Rc<Material>) -> Rc<dyn Primitive> { pub fn new(k: f64) -> Rc<dyn Primitive> {
// I need to find the bounding box for this shape // I need to find the bounding box for this shape
let trf = Point3::new(1.0, 1.0, 1.0); let trf = Point3::new(1.0, 1.0, 1.0);
let bln = Point3::new(-1.0, -1.0, -1.0); let bln = Point3::new(-1.0, -1.0, -1.0);
Rc::new(Roman { Rc::new(Roman {
k, k,
material,
bounding_box: BoundingBox { bln, trf }, bounding_box: BoundingBox { bln, trf },
}) })
} }
@@ -1656,8 +1415,6 @@ impl Primitive for Roman {
Some(Intersection { Some(Intersection {
point, point,
normal, normal,
incidence: ray.b,
material: Rc::clone(&self.material),
distance: t, distance: t,
}) })
} }
@@ -1665,10 +1422,6 @@ impl Primitive for Roman {
fn intersect_bounding_box(&self, ray: &Ray) -> bool { fn intersect_bounding_box(&self, ray: &Ray) -> bool {
self.bounding_box.intersect_bounding_box(ray) self.bounding_box.intersect_bounding_box(ray)
} }
fn get_material(&self) -> Rc<Material> {
Rc::clone(&self.material)
}
} }
fn smallest_non_zero(arr: &[f64]) -> Option<f64> { fn smallest_non_zero(arr: &[f64]) -> Option<f64> {

View File

@@ -1,96 +1,10 @@
use crate::camera::Camera; use crate::{camera::Camera, light::Light, material::*, node::*};
use crate::light::Light;
use crate::primitive::*;
use nalgebra::{Matrix4, Vector3};
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc;
#[derive(Clone)]
pub struct Node {
//Primitive
pub primitive: Rc<dyn Primitive>,
//Transformations
pub rotation: [f64; 3],
pub scale: [f64; 3],
pub translation: [f64; 3],
//Model matricies
pub model: Matrix4<f64>,
pub inv_model: Matrix4<f64>,
}
impl Node {
//New node with no transformations
pub fn new(primitive: Rc<dyn Primitive>) -> Node {
Node {
primitive,
rotation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0],
translation: [0.0, 0.0, 0.0],
model: Matrix4::identity(),
inv_model: Matrix4::identity(),
}
}
//New node with parent transformations
pub fn child(self, primitive: Rc<dyn Primitive>) -> Node {
let mut child = self.clone();
child.primitive = primitive;
child
}
//Rotate a mesh by adding to its rotation
pub fn rotate(&mut self, roll: f64, pitch: f64, yaw: f64) {
//Convert to radians
let roll = roll.to_radians();
// Convert pitch and yaw to radians
let pitch = pitch.to_radians();
let yaw = yaw.to_radians();
// Add the roll, pitch, and yaw to the current rotation
self.rotation[0] += roll;
self.rotation[1] += pitch;
self.rotation[2] += yaw;
// Recompute the model and inverse model matrices
self.compute();
}
// Translate a mesh by adding to its current position
pub fn translate(&mut self, x: f64, y: f64, z: f64) {
self.translation[0] += x;
self.translation[1] += y;
self.translation[2] += z;
// Recompute the model and inverse model matrices
self.compute();
}
// Scale a mesh by adding to its current scale
pub fn scale(&mut self, x: f64, y: f64, z: f64) {
self.scale[0] += x;
self.scale[1] += y;
self.scale[2] += z;
// Recompute the model and inverse model matrices
self.compute();
}
// This function computes the model and inverse model matrices
pub fn compute(&mut self) {
//Translation matrix
let translation = Vector3::from_row_slice(&self.translation);
let translation_matrix = Matrix4::new_translation(&translation);
// Scale matrix
let scale = &Vector3::from_row_slice(&self.scale);
let scale_matrix = Matrix4::new_nonuniform_scaling(&scale);
// Rotation matrix
let (roll, pitch, yaw) = (self.rotation[0], self.rotation[1], self.rotation[2]);
let rotation_matrix = Matrix4::from_euler_angles(roll, pitch, yaw);
// Compute the model matrix by combining the translation, rotation, and scale matrices
self.model = (translation_matrix * rotation_matrix * scale_matrix).cast();
// Compute the inverse model matrix by inverting the model matrix
self.inv_model = self.model.try_inverse().unwrap();
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Scene { pub struct Scene {
pub nodes: HashMap<String, Node>, pub nodes: HashMap<String, Node>,
pub materials: HashMap<String, Rc<Material>>, pub materials: HashMap<String, Material>,
pub lights: HashMap<String, Light>, pub lights: HashMap<String, Light>,
pub cameras: HashMap<String, Camera>, pub cameras: HashMap<String, Camera>,
} }
@@ -110,7 +24,7 @@ impl Scene {
self.nodes.insert(label, node); self.nodes.insert(label, node);
} }
// Adds a material to the scene // Adds a material to the scene
pub fn add_material(&mut self, label: String, material: Rc<Material>) { pub fn add_material(&mut self, label: String, material: Material) {
self.materials.insert(label, material); self.materials.insert(label, material);
} }
// Adds a light to the scene // Adds a light to the scene