Skip to content

Commit

Permalink
Chap 15: triangles
Browse files Browse the repository at this point in the history
(read wavefront obj file)
  • Loading branch information
fremag committed Feb 9, 2024
1 parent dffda5b commit d4ca25f
Show file tree
Hide file tree
Showing 20 changed files with 213 additions and 124 deletions.
Binary file added img/tea_pot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions ray-tracer-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::scenes::group_scene::GroupScene;
use crate::scenes::patterns_scene::PatternsScene;
use crate::scenes::refraction_sphere_scene::RefractionSphereScene;
use crate::scenes::stripe_pattern_scene::StripePatternScene;
use crate::scenes::teapot_scene::TeaPotScene;

fn main() {
let args: Vec<String> = env::args().collect();
Expand All @@ -26,8 +27,8 @@ fn main() {
}

println!("Start...");
let scene = CloverTriangleScene::new(0.0, 0.0, -9.0);
scene.render(800, 600, "e:\\tmp\\clover_triangle.png");
let scene = TeaPotScene { };
scene.render(800, 600, "e:\\tmp\\tea_pot.png");
println!("Done.")
}

Expand Down
1 change: 1 addition & 0 deletions ray-tracer-cli/src/scenes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ pub mod refraction_sphere_scene;
pub mod patterns_scene;
pub mod stripe_pattern_scene;
pub mod clover_triangles_scene;
pub mod teapot_scene;
43 changes: 43 additions & 0 deletions ray-tracer-cli/src/scenes/teapot_scene.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::env;
use std::fs::File;
use ray_tracer_lib::camera::Camera;
use ray_tracer_lib::colors::Color;
use ray_tracer_lib::core::math::PI;
use ray_tracer_lib::core::transform::{rotation_x, rotation_y, rotation_z, view_transform};
use ray_tracer_lib::core::tuple::{point, vector};
use ray_tracer_lib::lights::point_light::PointLight;
use ray_tracer_lib::obj_reader::ObjReader;
use ray_tracer_lib::object::Object;
use ray_tracer_lib::world::World;
use crate::scene::Scene;

pub struct TeaPotScene {}

impl Scene for TeaPotScene {
fn get_world(&self) -> World {
println!("{}", env::current_dir().unwrap().display());
let file_path = r"./obj/teapot-low.obj";
let file = File::open(file_path).unwrap();

let mut obj_reader = ObjReader::new(file);
obj_reader.read();

let teapot = obj_reader.models;
let teapot_model= teapot.get("Teapot001").unwrap();
let mut obj_teapot = Object::new_triangle(teapot_model.clone());
obj_teapot.set_transformation(&rotation_y(-PI/4.0) * &(&rotation_x(-PI/2.0)*&rotation_z(PI/2.0)));
let mut world = World::new();
world.objects.push(obj_teapot);
let lights = vec!(PointLight::new(point(-100.0, 100.0, -100.0), Color::new(1.0, 1.0, 1.0)));
world.set_lights(lights);
world
}

fn get_camera(&self, h_size: usize, v_size: usize) -> Camera {
let mut camera = Camera::new(h_size, v_size, PI / 3.0);
camera.set_transform(view_transform(point(-25.0, 25.0, -50.0),
point(0.0, 1.0, 0.0),
vector(0.0, 1.0, 0.0)));
camera
}
}
16 changes: 8 additions & 8 deletions ray-tracer-lib/src/core/comps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::object::Object;
use crate::core::ray::Ray;
use crate::core::tuple::{Tuple};

pub struct Comps<'a> {
pub object: &'a Object,
pub struct Comps {
pub object: Object,
pub t: Float,
pub point: Tuple,
pub eyev: Tuple,
Expand All @@ -19,7 +19,7 @@ pub struct Comps<'a> {
pub n2: Float,
}

impl<'a> Comps<'a> {
impl Comps {
pub fn schlick(&self) -> Float {
// find the cosine of the angle between the eye and normal vectors
let mut cos = self.eyev.dot(&self.normalv);
Expand All @@ -43,7 +43,7 @@ impl<'a> Comps<'a> {
}
}

pub fn prepare_computations<'a>(hit: &'a Intersection, ray: &Ray, xs: &Intersections) -> Comps<'a> {
pub fn prepare_computations(hit: &Intersection, ray: &Ray, xs: &Intersections) -> Comps {
// instantiate a data structure for storing some precomputed values
let point = ray.position(hit.t);
let eyev = -ray.direction;
Expand All @@ -61,7 +61,7 @@ pub fn prepare_computations<'a>(hit: &'a Intersection, ray: &Ray, xs: &Intersect
let over_point = point + normalv * EPSILON;
let under_point = point - normalv * EPSILON;

let mut containers: Vec<&Object> = vec!();
let mut containers: Vec<Object> = vec!();
let mut n1 = 0.0;
let mut n2 = 0.0;

Expand All @@ -73,7 +73,7 @@ pub fn prepare_computations<'a>(hit: &'a Intersection, ray: &Ray, xs: &Intersect

let position = containers.iter().position(|object| { object == &intersection.object });
match position {
None => { containers.push(intersection.object); }
None => { containers.push(intersection.object.clone()); }
Some(index) => { containers.remove(index); }
}

Expand All @@ -83,10 +83,10 @@ pub fn prepare_computations<'a>(hit: &'a Intersection, ray: &Ray, xs: &Intersect
}
}

Comps { t: hit.t, object: hit.object, point, eyev, normalv, inside, over_point, under_point, reflectv, n1, n2 }
Comps { t: hit.t, object: hit.object.clone(), point, eyev, normalv, inside, over_point, under_point, reflectv, n1, n2 }
}

fn get_refractive_index(containers: &Vec<&Object>) -> Float {
fn get_refractive_index(containers: &Vec<Object>) -> Float {
let n: Float;
if containers.is_empty() {
n = 1.0;
Expand Down
18 changes: 9 additions & 9 deletions ray-tracer-lib/src/core/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ use std::cmp::Ordering;
use crate::core::math::Float;
use crate::object::Object;

#[derive(Debug, Copy, Clone)]
pub struct Intersection<'a> {
#[derive(Debug, Clone)]
pub struct Intersection {
pub t : Float,
pub object: &'a Object
pub object: Object
}

impl<'a> Intersection<'a> {
pub fn new(t : Float, object : &'a Object) -> Self {
impl Intersection {
pub fn new(t : Float, object : Object) -> Self {
let inter = Intersection {t, object};
inter
}
}

impl<'a> PartialEq for Intersection<'a> {
impl PartialEq for Intersection {
fn eq(&self, other: &Self) -> bool {
if self.t != other.t {
return false;
Expand All @@ -25,17 +25,17 @@ impl<'a> PartialEq for Intersection<'a> {
}
}

impl<'a> Eq for Intersection<'a> {
impl Eq for Intersection {

}

impl<'a> PartialOrd for Intersection<'a> {
impl PartialOrd for Intersection {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl<'a> Ord for Intersection<'a> {
impl Ord for Intersection {
fn cmp(&self, other: &Self) -> Ordering {
if self.t.is_nan() {
Ordering::Greater
Expand Down
10 changes: 5 additions & 5 deletions ray-tracer-lib/src/core/intersections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use std::ops::Index;
use crate::core::intersection::Intersection;
use sorted_vec::SortedVec;

pub struct Intersections<'a> {
pub intersections: SortedVec<Intersection<'a>>
pub struct Intersections {
pub intersections: SortedVec<Intersection>
}

pub fn intersections<'a>(intersections: Vec<Intersection>) -> Intersections {
Intersections{ intersections: SortedVec::from_unsorted(intersections) }
}

impl<'a> Intersections<'a> {
impl Intersections {
pub fn hit(&self) -> Option<&Intersection> {
for inter in self.intersections.iter() {
if inter.t >= 0.0 {
Expand All @@ -24,8 +24,8 @@ impl<'a> Intersections<'a> {
}
}

impl<'a> Index<usize> for Intersections<'a> {
type Output = Intersection<'a>;
impl Index<usize> for Intersections {
type Output = Intersection;

fn index(&self, index: usize) -> &Self::Output {
&(self.intersections[index])
Expand Down
2 changes: 1 addition & 1 deletion ray-tracer-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub mod tests;
pub mod world;
pub mod canvas;
pub mod camera;
mod obj_reader;
pub mod obj_reader;



11 changes: 7 additions & 4 deletions ray-tracer-lib/src/obj_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ impl<T> ObjReader<T> where T: std::io::Read {
self.vertices.push(point(x, y, z));
}
if line.starts_with("f ") {
let items: Vec<&str> = line.split(' ').collect();
let items: Vec<&str> = line.split(' ').filter(|item| ! item.is_empty()).collect();
for i in 1..items.len()-2 {
let i1 = items[1].parse::<usize>().unwrap() - 1;
let i2 = items[i+1].parse::<usize>().unwrap() - 1;
let i3 = items[i+2].parse::<usize>().unwrap() - 1;
let s1 : Vec<&str> = items[1].split('/').collect();
let s2 : Vec<&str> = items[i+1].split('/').collect();
let s3 : Vec<&str> = items[i+2].split('/').collect();
let i1 = s1[0].parse::<usize>().unwrap() - 1;
let i2 = s2[0].parse::<usize>().unwrap() - 1;
let i3 = s3[0].parse::<usize>().unwrap() - 1;
let v1 = &self.vertices[i1];
let v2 = &self.vertices[i2];
let v3 = &self.vertices[i3];
Expand Down
22 changes: 20 additions & 2 deletions ray-tracer-lib/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ impl Object {
pub fn intersect(&self, ray: &Ray) -> Intersections {
let ray2 = ray.transform(&self.transformation_inverse);
return match &self.object_type {
ObjectShape(shape) => intersections(shape.intersect(&ray2).iter().map(|t| Intersection::new(*t, &self )).collect()),
ObjectShape(shape) => intersections(shape.intersect(&ray2).iter().map(|t| Intersection::new(*t, self.clone() )).collect()),
ObjectGroup(group) => group.intersect(&ray2),
TriangleGroup(model) => model.intersect(&ray2),
TriangleGroup(model) => {

let v = model.intersect(&ray2).iter().map(|(t, triangle)| {
let mut obj = Object::new(Shape::Triangle(*triangle));
obj.set_material(self.material);
Intersection::new(*t, obj)
}).collect();
intersections(v)
},
};
}

Expand Down Expand Up @@ -129,6 +137,16 @@ impl Object {
transformation_inverse_transpose: Matrix::<4>::identity(),
}
}

pub fn new_triangle(model: TriangleModel) -> Object {
Object {
object_type: TriangleGroup(model),
material: Material::new(),
transformation: Matrix::<4>::identity(),
transformation_inverse: Matrix::<4>::identity(),
transformation_inverse_transpose: Matrix::<4>::identity(),
}
}
}

impl PartialEq for Object {
Expand Down
2 changes: 1 addition & 1 deletion ray-tracer-lib/src/shapes/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Group {
for child in self.children.iter() {
let child_xs = child.intersect(ray);
for x in child_xs.intersections.iter() {
xs.intersections.push(*x);
xs.intersections.push(x.clone());
}
}

Expand Down
50 changes: 42 additions & 8 deletions ray-tracer-lib/src/shapes/triangle_model.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,62 @@
use crate::core::bounds::Bounds;
use crate::core::intersections::Intersections;
use crate::core::math::Float;
use crate::core::matrix::Matrix;
use crate::core::ray::Ray;
use crate::shapes::triangle::Triangle;

#[derive(Debug, Clone)]
pub struct TriangleModel {
pub triangles : Vec<Triangle>
pub triangles : Vec<Triangle>,
pub bounds : Bounds,
}

impl TriangleModel {
pub fn new(triangles : Vec<Triangle>) -> Self {
Self {triangles}
let mut model = Self {triangles, bounds: Bounds::new()};
model.init();
model
}

pub(crate) fn set_transformation(&self, transformation: Matrix<4>) {
todo!()
pub(crate) fn set_transformation(& mut self, transformation: Matrix<4>) {
let transformed_triangles = self.triangles.iter().map( |t| Triangle::new(
&transformation * &t.p1,
&transformation * &t.p2,
&transformation * &t.p3
)).collect();
self.triangles = transformed_triangles;
self.init();
}

pub(crate) fn bounds(&self) -> Bounds {
todo!()
self.bounds.clone()
}

pub(crate) fn intersect(&self, ray: &Ray) -> Intersections {
todo!()
pub(crate) fn intersect(&self, ray: &Ray) -> Vec<(Float, Triangle)> {
if self.bounds.intersect(&ray).is_empty() {
return vec![];
}
let mut xs = vec![];

for triangle in self.triangles.iter() {
let child_xs = triangle.intersect(ray);
for t in child_xs.iter() {
let x = (*t, triangle.clone());
xs.push(x);
}
}

xs
}

fn init(&mut self) {
let mut bounds = Bounds::new();

for triangle in self.triangles.iter() {
bounds.add(&triangle.p1);
bounds.add(&triangle.p2);
bounds.add(&triangle.p3);
}

self.bounds = bounds;
}
}
16 changes: 8 additions & 8 deletions ray-tracer-lib/src/tests/comps_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ mod tests {
pub fn precomputing_the_state_of_an_intersection_test() {
let r = ray(point(0.0, 0.0, -5.0), vector(0.0, 0.0, 1.0));
let shape = build_sphere();
let i = Intersection { t: 4.0, object: &shape };
let comps = prepare_computations(&i, &r, &intersections(vec!(i)));
let i = Intersection { t: 4.0, object: shape };
let comps = prepare_computations(&i, &r, &intersections(vec!(i.clone())));
assert_eq!(comps.t, i.t);
assert_eq!(comps.object, i.object);
assert_eq!(comps.point, point(0.0, 0.0, -1.0));
Expand All @@ -25,18 +25,18 @@ mod tests {
pub fn the_hit_when_an_intersection_occurs_on_the_outside_test() {
let r = ray(point(0.0, 0.0, -5.0), vector(0.0, 0.0, 1.0));
let shape = build_sphere();
let i = Intersection { t: 4.0, object: &shape };
let i = Intersection { t: 4.0, object: shape };

let comps = prepare_computations(&i, &r, &intersections(vec!(i)));
let comps = prepare_computations(&i, &r, &intersections(vec!(i.clone())));
assert_eq!(comps.inside, false);
}

#[test]
fn the_hit_when_an_intersection_occurs_on_the_inside_test() {
let r = ray(point(0.0, 0.0, 0.0), vector(0.0, 0.0, 1.0));
let shape = build_sphere();
let i = Intersection { t: 1.0, object: &shape };
let comps = prepare_computations(&i, &r, &intersections(vec!(i)));
let i = Intersection { t: 1.0, object: shape };
let comps = prepare_computations(&i, &r, &intersections(vec!(i.clone())));
assert_eq!(comps.point, point(0.0, 0.0, 1.0));
assert_eq!(comps.eyev, vector(0.0, 0.0, -1.0));
assert_eq!(comps.inside, true);
Expand All @@ -48,8 +48,8 @@ mod tests {
fn precomputing_the_reflection_vector_test() {
let shape = build_plane();
let r = ray(point(0.0, 1.0, -1.0), vector(0.0, -SQRT2 / 2.0, SQRT2 / 2.0));
let i = Intersection { t: SQRT2, object: &shape };
let comps = prepare_computations(&i, &r, &intersections(vec!(i)));
let i = Intersection { t: SQRT2, object: shape };
let comps = prepare_computations(&i, &r, &intersections(vec!(i.clone())));
assert_eq!(comps.reflectv, vector(0.0, SQRT2 / 2.0, SQRT2 / 2.0));
}
}
Loading

0 comments on commit d4ca25f

Please sign in to comment.