diff --git a/assignment-1c/src/image.rs b/assignment-1c/src/image.rs index 3bafc31..8ab73e4 100644 --- a/assignment-1c/src/image.rs +++ b/assignment-1c/src/image.rs @@ -1,12 +1,12 @@ -use generator::{done, Gn}; -use itertools::Itertools; use std::{ fs::File, io::{BufRead, BufReader, Read, Write}, path::Path, }; -use anyhow::Result; +use anyhow::{Context, Result}; +use generator::{done, Gn}; +use itertools::Itertools; use nalgebra::Vector3; /// A pixel color represented by a red, green, and blue value in the range 0-1. @@ -38,20 +38,28 @@ impl Image { let mut line_reader = BufReader::new(r); let mut header = String::new(); - line_reader.read_line(&mut header)?; - let parts = header.split(" ").collect::>(); + line_reader + .read_line(&mut header) + .context("Could not read line")?; + let parts = header.trim().split(" ").collect::>(); - let width = parts[1].parse::()?; - let height = parts[2].parse::()?; - let max_value = parts[3].parse::()?; + let width = parts[1].parse::().context("Could not read width")?; + let height = parts[2].parse::().context("Could not read height")?; + let max_value = parts[3] + .parse::() + .context("Could not read max value")?; + // Generator for reading numbers let numbers = Gn::<()>::new_scoped(move |mut s| { macro_rules! gen_try { - ($expr:expr) => { + ($expr:expr, $str:expr $(, $($arg:expr),* $(,)?)?) => { match $expr { Ok(v) => v, Err(e) => { - s.yield_(Err(anyhow::Error::from(e))); + s.yield_( + Err(anyhow::Error::from(e)) + .with_context(|| format!($str $(, $($arg,)*)?)), + ); done!(); } } @@ -59,11 +67,12 @@ impl Image { } for line in line_reader.lines() { - let line = gen_try!(line); - let parts = line.split_whitespace(); + let line = gen_try!(line, "Could not read line"); + let parts = line.trim().split_whitespace(); for part in parts { - let int = gen_try!(part.parse::()); + let int = + gen_try!(part.parse::(), "Could not read int from: {}", part); s.yield_(Ok(int)); } } @@ -78,9 +87,9 @@ impl Image { None => bail!("Not enough elements"), }; - let r = r? / max_value as f64; - let g = g? / max_value as f64; - let b = b? / max_value as f64; + let r = r? as f64 / max_value as f64; + let g = g? as f64 / max_value as f64; + let b = b? as f64 / max_value as f64; let color = Color::new(r, g, b); data.push(color); diff --git a/assignment-1c/src/main.rs b/assignment-1c/src/main.rs index a2bebc2..8743832 100644 --- a/assignment-1c/src/main.rs +++ b/assignment-1c/src/main.rs @@ -112,8 +112,9 @@ fn main() -> Result<()> { Ok(match earliest_intersection { // Take the object's material color - Some((obj_idx, intersection_context, object)) => scene - .compute_pixel_color(obj_idx, object.material, intersection_context), + Some((obj_idx, intersection_context, object)) => { + scene.compute_pixel_color(obj_idx, object, intersection_context) + } // There was no intersection, so this should default to the scene's // background color diff --git a/assignment-1c/src/scene/cylinder.rs b/assignment-1c/src/scene/cylinder.rs index 960c6ba..18dce57 100644 --- a/assignment-1c/src/scene/cylinder.rs +++ b/assignment-1c/src/scene/cylinder.rs @@ -200,7 +200,7 @@ mod tests { let ray = Ray::from_endpoints(eye, end); let scene = Scene::default(); - let res = cylinder.intersects_ray_at(&scene, &ray); + let _res = cylinder.intersects_ray_at(&scene, &ray); // panic!("Result: {res:?}"); } } diff --git a/assignment-1c/src/scene/data.rs b/assignment-1c/src/scene/data.rs index 6729870..577b43c 100644 --- a/assignment-1c/src/scene/data.rs +++ b/assignment-1c/src/scene/data.rs @@ -1,53 +1,11 @@ use std::fmt::Debug; -use anyhow::Result; - use crate::image::Color; -use crate::ray::Ray; use crate::utils::cross; use crate::Point; -use super::cylinder::Cylinder; -use super::illumination::IntersectionContext; -use super::sphere::Sphere; -use super::triangle::Triangle; use super::Scene; -#[derive(Debug)] -pub enum ObjectKind { - Sphere(Sphere), - Cylinder(Cylinder), - Triangle(Triangle), -} - -impl ObjectKind { - /// Determine where the ray intersects this object, returning the earliest - /// time this happens. Returns None if no intersection occurs. - /// - /// Also known as Trace_Ray in the slides, except not the part where it calls - /// Shade_Ray. - pub fn intersects_ray_at( - &self, - scene: &Scene, - ray: &Ray, - ) -> Result> { - match self { - ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray), - ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray), - ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray), - } - } -} - -/// An object in the scene -#[derive(Debug)] -pub struct Object { - pub kind: ObjectKind, - - /// Index into the scene's material color list - pub material: usize, -} - #[derive(Debug)] pub struct Rect { pub upper_left: Point, diff --git a/assignment-1c/src/scene/illumination.rs b/assignment-1c/src/scene/illumination.rs index 07717f0..bd067a9 100644 --- a/assignment-1c/src/scene/illumination.rs +++ b/assignment-1c/src/scene/illumination.rs @@ -25,17 +25,27 @@ impl Scene { pub fn compute_pixel_color( &self, obj_idx: usize, - material_idx: usize, + object: &Object, intersection_context: IntersectionContext, ) -> Color { // TODO: Does it make sense to make this function fallible from an API // design standpoint? - let material = match self.materials.get(material_idx) { + let material = match self.materials.get(object.material_idx) { Some(v) => v, None => return self.bkg_color, }; - let ambient_component = material.k_a * material.diffuse_color; + let diffuse_color = match object.texture_idx { + Some(texture_idx) => { + let texture = &self.textures[texture_idx]; + + // TODO: need to implement + material.diffuse_color + } + None => material.diffuse_color, + }; + + let ambient_component = material.k_a * diffuse_color; // Diffuse and specular lighting for each separate light let diffuse_and_specular: Color = self @@ -51,7 +61,7 @@ impl Scene { ((light_direction + viewer_direction) / 2.0).normalize(); let diffuse_component = material.k_d - * material.diffuse_color + * diffuse_color * dot(normal, light_direction).max(0.0); let specular_component = material.k_s diff --git a/assignment-1c/src/scene/input_file.rs b/assignment-1c/src/scene/input_file.rs index a6fb103..5f37fc3 100644 --- a/assignment-1c/src/scene/input_file.rs +++ b/assignment-1c/src/scene/input_file.rs @@ -1,8 +1,6 @@ -use std::{ - fs::File, - io::Read, - path::{Path, PathBuf}, -}; +use std::fs::File; +use std::io::Read; +use std::path::Path; use anyhow::{Context, Result}; use itertools::Itertools; @@ -15,7 +13,7 @@ use crate::{ data::{Attenuation, Light, LightKind, Material, Object}, sphere::Sphere, triangle::Triangle, - Scene, + Scene, texture::Texture, }, Point, Vector, }; @@ -37,7 +35,8 @@ impl Scene { }; let mut scene = Scene::default(); - let mut material_color = None; + let mut material_idx = None; + let mut texture_idx = None; for line in contents.lines() { // Split lines into words. `parts' is an iterator, which is consumed upon @@ -167,7 +166,7 @@ impl Scene { }; let idx = scene.materials.len(); - material_color = Some(idx); + material_idx = Some(idx); scene.materials.push(material); } @@ -177,7 +176,8 @@ impl Scene { scene.objects.push(Object { kind: ObjectKind::Sphere(Sphere { center, radius }), - material: u!(material_color), + material_idx: u!(material_idx), + texture_idx, }); } @@ -194,7 +194,8 @@ impl Scene { radius, length, }), - material: u!(material_color), + material_idx: u!(material_idx), + texture_idx, }); } @@ -237,7 +238,8 @@ impl Scene { }; scene.objects.push(Object { kind: ObjectKind::Triangle(triangle), - material: u!(material_color), + material_idx: u!(material_idx), + texture_idx, }); #[derive(Debug, Copy, Clone, PartialEq)] @@ -293,8 +295,12 @@ impl Scene { None => bail!("Did not provide path."), }; + let idx = scene.textures.len(); + texture_idx = Some(idx); + let image = Image::from_file(path)?; - scene.textures.push(image); + let texture = Texture::new(image); + scene.textures.push(texture); } _ => bail!("Unknown keyword {keyword}"), diff --git a/assignment-1c/src/scene/mod.rs b/assignment-1c/src/scene/mod.rs index 86153cc..19c7e15 100644 --- a/assignment-1c/src/scene/mod.rs +++ b/assignment-1c/src/scene/mod.rs @@ -3,14 +3,15 @@ pub mod data; pub mod illumination; pub mod input_file; pub mod sphere; +pub mod texture; pub mod triangle; - -use nalgebra::Vector2; +pub mod object; use crate::image::{Color, Image}; use crate::{Point, Point2, Vector}; use self::data::{Attenuation, DepthCueing, Light, Material, Object}; +use self::texture::Texture; #[derive(Debug, Default)] pub struct Scene { @@ -35,7 +36,7 @@ pub struct Scene { pub objects: Vec, /// List of textures - pub textures: Vec, + pub textures: Vec, /// Coordinates into a texture image pub texture_vertices: Vec, diff --git a/assignment-1c/src/scene/object.rs b/assignment-1c/src/scene/object.rs new file mode 100644 index 000000000..6a8ca89 --- /dev/null +++ b/assignment-1c/src/scene/object.rs @@ -0,0 +1,47 @@ +use anyhow::Result; + +use crate::ray::Ray; + +use super::cylinder::Cylinder; +use super::illumination::IntersectionContext; +use super::sphere::Sphere; +use super::triangle::Triangle; +use super::Scene; + +/// An object in the scene +#[derive(Debug)] +pub struct Object { + pub kind: ObjectKind, + + /// Index into the scene's material color list + pub material_idx: usize, + + /// If this object has a texture, this is the index into the texture list + pub texture_idx: Option, +} + +#[derive(Debug)] +pub enum ObjectKind { + Sphere(Sphere), + Cylinder(Cylinder), + Triangle(Triangle), +} + +impl ObjectKind { + /// Determine where the ray intersects this object, returning the earliest + /// time this happens. Returns None if no intersection occurs. + /// + /// Also known as Trace_Ray in the slides, except not the part where it calls + /// Shade_Ray. + pub fn intersects_ray_at( + &self, + scene: &Scene, + ray: &Ray, + ) -> Result> { + match self { + ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray), + ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray), + ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray), + } + } +} diff --git a/assignment-1c/src/scene/texture.rs b/assignment-1c/src/scene/texture.rs new file mode 100644 index 000000000..fc94699 --- /dev/null +++ b/assignment-1c/src/scene/texture.rs @@ -0,0 +1,16 @@ +use crate::image::{Color, Image}; + +#[derive(Debug)] +pub struct Texture(Image); + +impl Texture { + pub fn new(image: Image) -> Self { + Self(image) + } + + /// Returns a pixel at the given coordinate. For non-lattice coordinates, + /// interpolation of the image is done. + pub fn pixel_at(&self, x: f64, y: f64) -> Option { + todo!() + } +} diff --git a/assignment-1c/src/scene/triangle.rs b/assignment-1c/src/scene/triangle.rs index 34b0048..c537586 100644 --- a/assignment-1c/src/scene/triangle.rs +++ b/assignment-1c/src/scene/triangle.rs @@ -4,7 +4,7 @@ use ordered_float::NotNan; use crate::ray::Ray; use crate::utils::{cross, dot}; -use crate::Point; + use super::illumination::IntersectionContext; use super::Scene; diff --git a/flake.nix b/flake.nix index 8825492..167a110 100644 --- a/flake.nix +++ b/flake.nix @@ -15,6 +15,7 @@ packages = (with pkgs; [ cargo-deny cargo-edit + cargo-expand cargo-flamegraph cargo-watch imagemagick