Moved node & material to own files
This commit is contained in:
48
src/material.rs
Normal file
48
src/material.rs
Normal 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
97
src/node.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
333
src/primitive.rs
333
src/primitive.rs
@@ -1,127 +1,19 @@
|
||||
use crate::{
|
||||
bvh::BoundingBox,
|
||||
ray::{Intersection, Ray},
|
||||
{EPSILON, INFINITY},
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
use crate::ray::Ray;
|
||||
use crate::{EPSILON, INFINITY};
|
||||
use nalgebra::{distance, Matrix4, Point3, Vector3};
|
||||
use nalgebra::{distance, Point3, Vector3};
|
||||
use roots::{find_roots_quadratic, find_roots_quartic, Roots};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
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 -----------------------------------------------------------------
|
||||
pub trait Primitive {
|
||||
fn intersect_ray(&self, ray: &Ray) -> Option<Intersection>;
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool;
|
||||
fn get_material(&self) -> Rc<Material>;
|
||||
}
|
||||
|
||||
// SPHERE -----------------------------------------------------------------
|
||||
@@ -130,11 +22,10 @@ pub struct Sphere {
|
||||
position: Point3<f64>,
|
||||
radius: f64,
|
||||
bounding_box: BoundingBox,
|
||||
material: Rc<Material>,
|
||||
}
|
||||
|
||||
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 bln = position - radius_vec;
|
||||
let trf = position + radius_vec;
|
||||
@@ -143,12 +34,11 @@ impl Sphere {
|
||||
position,
|
||||
radius,
|
||||
bounding_box,
|
||||
material,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
|
||||
Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0, material)
|
||||
pub fn unit() -> Rc<dyn Primitive> {
|
||||
Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,16 +74,10 @@ impl Primitive for Sphere {
|
||||
Some(Intersection {
|
||||
point: intersect,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
return self.bounding_box.intersect_bounding_box(ray);
|
||||
}
|
||||
@@ -205,17 +89,11 @@ pub struct Circle {
|
||||
position: Point3<f64>,
|
||||
radius: f64,
|
||||
normal: Vector3<f64>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
impl Circle {
|
||||
pub fn new(
|
||||
position: Point3<f64>,
|
||||
radius: f64,
|
||||
normal: Vector3<f64>,
|
||||
material: Rc<Material>,
|
||||
) -> Rc<dyn Primitive> {
|
||||
pub fn new(position: Point3<f64>, radius: f64, normal: Vector3<f64>) -> Rc<dyn Primitive> {
|
||||
let radius_vec = Vector3::new(radius, radius, radius);
|
||||
let bln = position - radius_vec;
|
||||
let trf = position + radius_vec;
|
||||
@@ -224,16 +102,14 @@ impl Circle {
|
||||
position,
|
||||
radius,
|
||||
normal: normal.normalize(),
|
||||
material,
|
||||
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 normal = Vector3::new(0.0, 1.0, 0.0);
|
||||
let radius = 1.0;
|
||||
let material = material;
|
||||
|
||||
let bln = Point3::new(-radius, 0.0, -EPSILON);
|
||||
let trf = Point3::new(radius, 0.0, EPSILON);
|
||||
@@ -243,7 +119,6 @@ impl Circle {
|
||||
position,
|
||||
normal,
|
||||
radius,
|
||||
material,
|
||||
bounding_box,
|
||||
})
|
||||
}
|
||||
@@ -265,18 +140,12 @@ impl Primitive for Circle {
|
||||
return Some(Intersection {
|
||||
point: intersect,
|
||||
normal: self.normal.normalize(),
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
@@ -289,23 +158,20 @@ pub struct Cylinder {
|
||||
height: f64,
|
||||
base_circle: Rc<dyn Primitive>,
|
||||
top_circle: Rc<dyn Primitive>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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(
|
||||
Point3::new(0.0, 0.0, 0.0),
|
||||
radius,
|
||||
Vector3::new(0.0, -1.0, 0.0),
|
||||
Rc::clone(&material),
|
||||
);
|
||||
let top_circle = Circle::new(
|
||||
Point3::new(0.0, height, 0.0),
|
||||
radius,
|
||||
Vector3::new(0.0, 1.0, 0.0),
|
||||
Rc::clone(&material),
|
||||
);
|
||||
let bln = Point3::new(-radius, 0.0, -radius);
|
||||
let trf = Point3::new(radius, height, radius);
|
||||
@@ -314,7 +180,6 @@ impl Cylinder {
|
||||
height,
|
||||
base_circle,
|
||||
top_circle,
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -356,8 +221,6 @@ impl Primitive for Cylinder {
|
||||
Some(Intersection {
|
||||
point: intersect,
|
||||
normal: normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
} 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 {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
@@ -416,17 +275,15 @@ pub struct Cone {
|
||||
base: f64,
|
||||
apex: f64,
|
||||
circle: Rc<dyn Primitive>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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(
|
||||
Point3::new(0.0, base, 0.0),
|
||||
radius,
|
||||
Vector3::new(0.0, 1.0, 0.0),
|
||||
Rc::clone(&material),
|
||||
);
|
||||
let bln = Point3::new(-radius, base, -radius);
|
||||
let trf = Point3::new(radius, base + apex, radius);
|
||||
@@ -435,12 +292,11 @@ impl Cone {
|
||||
base,
|
||||
apex,
|
||||
circle,
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
|
||||
Cone::new(1.0, 2.0, -1.0, material)
|
||||
pub fn unit() -> Rc<dyn Primitive> {
|
||||
Cone::new(1.0, 2.0, -1.0)
|
||||
}
|
||||
|
||||
pub fn get_normal(&self, intersect: Point3<f64>) -> Vector3<f64> {
|
||||
@@ -489,8 +345,6 @@ impl Primitive for Cone {
|
||||
true => Some(Intersection {
|
||||
point: intersect,
|
||||
normal: self.get_normal(intersect),
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
}),
|
||||
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 {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
@@ -530,7 +380,6 @@ pub struct Rectangle {
|
||||
position: Point3<f64>,
|
||||
normal: Vector3<f64>,
|
||||
width_direction: Vector3<f64>,
|
||||
material: Rc<Material>,
|
||||
width: f64,
|
||||
height: f64,
|
||||
bounding_box: BoundingBox,
|
||||
@@ -543,7 +392,6 @@ impl Rectangle {
|
||||
width_direction: Vector3<f64>,
|
||||
width: f64,
|
||||
height: f64,
|
||||
material: Rc<Material>,
|
||||
) -> Rc<dyn Primitive> {
|
||||
let normal = normal.normalize();
|
||||
let width_direction = width_direction.normalize();
|
||||
@@ -556,18 +404,16 @@ impl Rectangle {
|
||||
width_direction: width_direction.normalize(),
|
||||
width,
|
||||
height,
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
|
||||
pub fn unit() -> Rc<dyn Primitive> {
|
||||
Rectangle::new(
|
||||
Point3::new(0.0, 0.0, 0.0),
|
||||
Vector3::new(0.0, 1.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
2.0,
|
||||
2.0,
|
||||
material,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -595,18 +441,12 @@ impl Primitive for Rectangle {
|
||||
return Some(Intersection {
|
||||
point: intersect,
|
||||
normal: self.normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
@@ -617,24 +457,22 @@ impl Primitive for Rectangle {
|
||||
pub struct Cube {
|
||||
bln: Point3<f64>,
|
||||
trf: Point3<f64>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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 {
|
||||
bln,
|
||||
trf,
|
||||
material,
|
||||
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 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();
|
||||
|
||||
// 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
|
||||
let intersect = ray.at_t(tmin);
|
||||
|
||||
// Check if the intersection is outside 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
|
||||
if intersect.x < bln.x
|
||||
|| intersect.x > trf.x
|
||||
|| intersect.y < bln.y
|
||||
|| intersect.y > trf.y
|
||||
|| intersect.z < bln.z
|
||||
|| intersect.z > trf.z
|
||||
{
|
||||
return None; // Intersection is outside the box
|
||||
}
|
||||
@@ -684,8 +522,6 @@ impl Primitive for Cube {
|
||||
Some(Intersection {
|
||||
point: intersect,
|
||||
normal: normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: tmin,
|
||||
})
|
||||
} else {
|
||||
@@ -696,10 +532,6 @@ impl Primitive for Cube {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
}
|
||||
|
||||
// TRIANGLE -----------------------------------------------------------------
|
||||
@@ -710,17 +542,11 @@ pub struct Triangle {
|
||||
v: Point3<f64>,
|
||||
w: Point3<f64>,
|
||||
normal: Vector3<f64>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
impl Triangle {
|
||||
pub fn new(
|
||||
u: Point3<f64>,
|
||||
v: Point3<f64>,
|
||||
w: Point3<f64>,
|
||||
material: Rc<Material>,
|
||||
) -> Rc<dyn Primitive> {
|
||||
pub fn new(u: Point3<f64>, v: Point3<f64>, w: Point3<f64>) -> Rc<dyn Primitive> {
|
||||
let uv = v - u;
|
||||
let uw = w - u;
|
||||
let normal = uv.cross(&uw).normalize();
|
||||
@@ -732,17 +558,15 @@ impl Triangle {
|
||||
v,
|
||||
w,
|
||||
normal,
|
||||
material,
|
||||
bounding_box,
|
||||
})
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn unit(material: Rc<Material>) -> Rc<dyn Primitive> {
|
||||
let u = Point3::new(-1.0, 0.0, -1.0);
|
||||
let v = Point3::new(0.0, 0.0, 1.0);
|
||||
let w = Point3::new(1.0, 0.0, -1.0);
|
||||
let material = material;
|
||||
Triangle::new(u, v, w, material)
|
||||
pub fn unit() -> Rc<dyn Primitive> {
|
||||
let u = Point3::new(-1.0, -1.0, 0.0);
|
||||
let v = Point3::new(0.0, 1.0, 0.0);
|
||||
let w = Point3::new(1.0, -1.0, 0.0);
|
||||
Triangle::new(u, v, w)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,8 +601,6 @@ impl Primitive for Triangle {
|
||||
Some(Intersection {
|
||||
point: intersect,
|
||||
normal: normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
} 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 {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
@@ -799,17 +617,15 @@ impl Primitive for Triangle {
|
||||
#[derive(Clone)]
|
||||
pub struct Mesh {
|
||||
triangles: Vec<Triangle>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let bounding_box = Mesh::compute_bounding_box(&triangles);
|
||||
Rc::new(Mesh {
|
||||
triangles,
|
||||
material,
|
||||
bounding_box,
|
||||
})
|
||||
}
|
||||
@@ -828,7 +644,7 @@ impl Mesh {
|
||||
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 vertices: Vec<Point3<f64>> = Vec::new();
|
||||
|
||||
@@ -872,13 +688,11 @@ impl Mesh {
|
||||
let bln = u.inf(&v).inf(&w);
|
||||
let trf = u.sup(&v).sup(&w);
|
||||
let bounding_box = BoundingBox { bln, trf };
|
||||
let material = material.clone();
|
||||
triangles.push(Triangle {
|
||||
u,
|
||||
v,
|
||||
w,
|
||||
normal,
|
||||
material,
|
||||
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
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
@@ -927,19 +737,17 @@ impl Primitive for Mesh {
|
||||
pub struct Torus {
|
||||
inner_rad: f64,
|
||||
outer_rad: f64,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let trf = Point3::new(1.0, 1.0, 1.0);
|
||||
let bln = Point3::new(-1.0, -1.0, -1.0);
|
||||
Rc::new(Torus {
|
||||
inner_rad,
|
||||
outer_rad,
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -1042,8 +850,6 @@ impl Primitive for Torus {
|
||||
Some(Intersection {
|
||||
point,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
@@ -1051,10 +857,6 @@ impl Primitive for Torus {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
}
|
||||
|
||||
// GNOMON -----------------------------------------------------------------
|
||||
@@ -1063,28 +865,24 @@ pub struct Gnonom {
|
||||
x_cube: Rc<dyn Primitive>,
|
||||
y_cube: Rc<dyn Primitive>,
|
||||
z_cube: Rc<dyn Primitive>,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
impl Gnonom {
|
||||
const GNONOM_WIDTH: f64 = 0.1;
|
||||
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(
|
||||
Point3::new(0.0, -Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH),
|
||||
Point3::new(Self::GNONOM_LENGTH, Self::GNONOM_WIDTH, Self::GNONOM_WIDTH),
|
||||
material.clone(),
|
||||
);
|
||||
let y_cube = Cube::new(
|
||||
Point3::new(-Self::GNONOM_WIDTH, 0.0, -Self::GNONOM_WIDTH),
|
||||
Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_LENGTH, Self::GNONOM_WIDTH),
|
||||
material.clone(),
|
||||
);
|
||||
let z_cube = Cube::new(
|
||||
Point3::new(-Self::GNONOM_WIDTH, -Self::GNONOM_WIDTH, 0.0),
|
||||
Point3::new(Self::GNONOM_WIDTH, Self::GNONOM_WIDTH, Self::GNONOM_LENGTH),
|
||||
material.clone(),
|
||||
);
|
||||
let bounding_box = BoundingBox {
|
||||
bln: Point3::new(
|
||||
@@ -1102,7 +900,6 @@ impl Gnonom {
|
||||
x_cube,
|
||||
y_cube,
|
||||
z_cube,
|
||||
material,
|
||||
bounding_box,
|
||||
})
|
||||
}
|
||||
@@ -1128,26 +925,20 @@ impl Primitive for Gnonom {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
self.material.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// CROSS CAP ---------
|
||||
#[derive(Clone)]
|
||||
pub struct CrossCap {
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let trf = Point3::new(1.0, 1.0, 1.0);
|
||||
let bln = Point3::new(-1.0, -1.0, -1.0);
|
||||
Rc::new(CrossCap {
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -1219,8 +1010,6 @@ impl Primitive for CrossCap {
|
||||
Some(Intersection {
|
||||
point,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
@@ -1228,10 +1017,6 @@ impl Primitive for CrossCap {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
}
|
||||
|
||||
// CROSS CAP 2 ---------
|
||||
@@ -1239,19 +1024,17 @@ impl Primitive for CrossCap {
|
||||
pub struct CrossCap2 {
|
||||
p: f64,
|
||||
q: f64,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let trf = Point3::new(1.0, 1.0, 1.0);
|
||||
let bln = Point3::new(-1.0, -1.0, -1.0);
|
||||
Rc::new(CrossCap2 {
|
||||
p,
|
||||
q,
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -1348,8 +1131,6 @@ impl Primitive for CrossCap2 {
|
||||
Some(Intersection {
|
||||
point,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
@@ -1357,26 +1138,20 @@ impl Primitive for CrossCap2 {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
}
|
||||
|
||||
// Steiner ---------
|
||||
#[derive(Clone)]
|
||||
pub struct Steiner {
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let trf = Point3::new(1.0, 1.0, 1.0);
|
||||
let bln = Point3::new(-1.0, -1.0, -1.0);
|
||||
Rc::new(Steiner {
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -1437,8 +1212,6 @@ impl Primitive for Steiner {
|
||||
Some(Intersection {
|
||||
point,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
@@ -1446,26 +1219,20 @@ impl Primitive for Steiner {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
}
|
||||
|
||||
// Steiner 2 ---------
|
||||
#[derive(Clone)]
|
||||
pub struct Steiner2 {
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let trf = Point3::new(1.0, 1.0, 1.0);
|
||||
let bln = Point3::new(-1.0, -1.0, -1.0);
|
||||
Rc::new(Steiner2 {
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -1537,8 +1304,6 @@ impl Primitive for Steiner2 {
|
||||
Some(Intersection {
|
||||
point,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
@@ -1546,28 +1311,22 @@ impl Primitive for Steiner2 {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
self.bounding_box.intersect_bounding_box(ray)
|
||||
}
|
||||
|
||||
fn get_material(&self) -> Rc<Material> {
|
||||
Rc::clone(&self.material)
|
||||
}
|
||||
}
|
||||
|
||||
// Roman ---------
|
||||
#[derive(Clone)]
|
||||
pub struct Roman {
|
||||
k: f64,
|
||||
material: Rc<Material>,
|
||||
bounding_box: BoundingBox,
|
||||
}
|
||||
|
||||
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
|
||||
let trf = Point3::new(1.0, 1.0, 1.0);
|
||||
let bln = Point3::new(-1.0, -1.0, -1.0);
|
||||
Rc::new(Roman {
|
||||
k,
|
||||
material,
|
||||
bounding_box: BoundingBox { bln, trf },
|
||||
})
|
||||
}
|
||||
@@ -1656,8 +1415,6 @@ impl Primitive for Roman {
|
||||
Some(Intersection {
|
||||
point,
|
||||
normal,
|
||||
incidence: ray.b,
|
||||
material: Rc::clone(&self.material),
|
||||
distance: t,
|
||||
})
|
||||
}
|
||||
@@ -1665,10 +1422,6 @@ impl Primitive for Roman {
|
||||
fn intersect_bounding_box(&self, ray: &Ray) -> bool {
|
||||
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> {
|
||||
|
||||
92
src/scene.rs
92
src/scene.rs
@@ -1,96 +1,10 @@
|
||||
use crate::camera::Camera;
|
||||
use crate::light::Light;
|
||||
use crate::primitive::*;
|
||||
use nalgebra::{Matrix4, Vector3};
|
||||
use crate::{camera::Camera, light::Light, material::*, node::*};
|
||||
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)]
|
||||
pub struct Scene {
|
||||
pub nodes: HashMap<String, Node>,
|
||||
pub materials: HashMap<String, Rc<Material>>,
|
||||
pub materials: HashMap<String, Material>,
|
||||
pub lights: HashMap<String, Light>,
|
||||
pub cameras: HashMap<String, Camera>,
|
||||
}
|
||||
@@ -110,7 +24,7 @@ impl Scene {
|
||||
self.nodes.insert(label, node);
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
// Adds a light to the scene
|
||||
|
||||
Reference in New Issue
Block a user