diff --git a/Cargo.toml b/Cargo.toml index fa14faf..010ae82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -sdl2 = "0.32" +alga = "0.9" +itertools = "0.8" +obj = "0.9" quickcheck = "0.9" quickcheck_macros = "0.8" +sdl2 = "0.32" [dependencies.nalgebra] version = "0.19" diff --git a/src/main.rs b/src/main.rs index a2cceaf..d48cadb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,15 +7,17 @@ use std::time::Duration; use nalgebra::{Point3, Vector3}; + use std::cmp::min; use std::rc::Rc; +use std::path::Path; use vanrijn::camera::partial_render_scene; use vanrijn::colour::{ColourRgbF, NamedColour}; use vanrijn::image::{ClampingToneMapper, ImageRgbF, ImageRgbU8, ToneMapper}; use vanrijn::materials::{LambertianMaterial, PhongMaterial, ReflectiveMaterial}; -use vanrijn::mesh::Triangle; -use vanrijn::raycasting::{Plane, Sphere}; +use vanrijn::mesh::{load_obj, Triangle}; +use vanrijn::raycasting::{Intersect, Plane, Sphere}; use vanrijn::scene::Scene; fn update_texture(image: &ImageRgbU8, texture: &mut Texture) { @@ -59,9 +61,20 @@ pub fn main() -> Result<(), Box> { )?; let mut output_image = ImageRgbF::::new(image_width, image_height); - let scene = Scene { - camera_location: Point3::new(0.0, 0.0, 0.0), - objects: vec![ + let scene = Arc::new(Scene { + camera_location: Point3::new(-2.0, 1.0, -5.0), + objects: load_obj( + Path::new("/home/matthew/Downloads/bunny.obj"), + Arc::new(ReflectiveMaterial { + colour: ColourRgbF::from_named(NamedColour::Yellow), + diffuse_strength: 0.05, + reflection_strength: 0.9, + }), + ) + .unwrap() + .into_iter() + .map(|triangle| Box::new(triangle) as Box>) + .chain(vec![ Box::new(Plane::new( Vector3::new(0.0, 1.0, 0.0), -2.0, @@ -69,17 +82,17 @@ pub fn main() -> Result<(), Box> { colour: ColourRgbF::new(0.55, 0.27, 0.04), diffuse_strength: 0.1, }), - )), + )) as Box>, Box::new(Sphere::new( - Point3::new(1.25, -0.5, 6.0), + Point3::new(-6.25, -0.5, 1.0), 1.0, - Rc::new(LambertianMaterial { + Arc::new(LambertianMaterial { colour: ColourRgbF::from_named(NamedColour::Green), diffuse_strength: 0.1, }), )), Box::new(Sphere::new( - Point3::new(-1.25, -0.5, 6.0), + Point3::new(-4.25, -0.5, 2.0), 1.0, Rc::new(ReflectiveMaterial { colour: ColourRgbF::from_named(NamedColour::Blue), @@ -88,7 +101,7 @@ pub fn main() -> Result<(), Box> { }), )), Box::new(Sphere::new( - Point3::new(0.0, 1.5, 6.0), + Point3::new(-5.0, 1.5, 1.0), 1.0, Rc::new(PhongMaterial { colour: ColourRgbF::from_named(NamedColour::Red), @@ -97,20 +110,9 @@ pub fn main() -> Result<(), Box> { specular_strength: 1.0, }), )), - Box::new(Triangle { - vertices: [ - Point3::new(1.25, -0.5, 6.0), - Point3::new(-1.25, -0.5, 6.0), - Point3::new(0.0, 1.5, 6.0), - ], - normals: [Vector3::new(0.0, 0.0, 1.0); 3], - material: Rc::new(LambertianMaterial { - colour: ColourRgbF::from_named(NamedColour::Green), - diffuse_strength: 0.1, - }), - }), - ], - }; + ]) + .collect(), + }); let mut event_pump = sdl_context.event_pump()?; let mut i = 0; diff --git a/src/mesh.rs b/src/mesh.rs index fc83d7d..21db21a 100644 --- a/src/mesh.rs +++ b/src/mesh.rs @@ -1,10 +1,15 @@ -use nalgebra::{Point3, RealField, Vector2, Vector3}; +use nalgebra::{convert, Point3, RealField, Vector2, Vector3}; +use obj::{IndexTuple, Obj, SimplePolygon}; use super::materials::Material; use super::raycasting::{Intersect, IntersectionInfo, Ray}; use std::rc::Rc; +use alga::general::SupersetOf; +use std::io::Result; +use std::path::Path; +#[derive(Debug)] pub struct Triangle { pub vertices: [Point3; 3], pub normals: [Vector3; 3], @@ -76,6 +81,76 @@ impl Intersect for Triangle { } } +fn get_vertex_and_normal( + index_tuple: &IndexTuple, + vertex_positions: &Vec<[f32; 3]>, + normal_positions: &Vec<[f32; 3]>, +) -> (Point3, Vector3) +where + T: SupersetOf, +{ + let &IndexTuple(vertex_index, _, maybe_normal_index) = index_tuple; + let vertex: Point3 = convert(Point3::from_slice(&vertex_positions[vertex_index])); + let normal = match maybe_normal_index { + Some(normal_index) => convert(Vector3::from_row_slice(&normal_positions[normal_index])), + None => Vector3::zeros(), + }; + (vertex, normal) +} + +fn get_triangles( + polygon: &SimplePolygon, + vertex_positions: &Vec<[f32; 3]>, + normal_positions: &Vec<[f32; 3]>, + material: Arc>, +) -> Vec> +where + T: SupersetOf, +{ + if let Some(v0_index) = polygon.iter().next() { + let (v0_vertex, v0_normal) = + get_vertex_and_normal(v0_index, &vertex_positions, &normal_positions); + polygon + .iter() + .skip(1) + .zip(polygon.iter().skip(2)) + .map(|(v1_index, v2_index)| { + let (v1_vertex, v1_normal) = + get_vertex_and_normal(v1_index, &vertex_positions, &normal_positions); + let (v2_vertex, v2_normal) = + get_vertex_and_normal(v2_index, &vertex_positions, &normal_positions); + let vertices = [v0_vertex, v1_vertex, v2_vertex]; + let normals = [v0_normal, v1_normal, v2_normal]; + Triangle { + vertices, + normals, + material: material.clone(), + } + }) + .collect() + } else { + vec![] + } +} + +pub fn load_obj( + filename: &Path, + material: Arc>, +) -> Result>> +where + T: SupersetOf, +{ + let obj = Obj::::load(filename)?; + + Ok(obj + .objects + .iter() + .flat_map(|object| object.groups.iter()) + .flat_map(|group| group.polys.iter()) + .flat_map(|poly| get_triangles(poly, &obj.position, &obj.normal, material.clone())) + .collect()) +} + fn indices_with_index_of_largest_element_last(v: &Vector3) -> [usize; 3] { if v.x > v.y { if v.z > v.x {