diff --git a/assignment-1c/src/scene/input_file.rs b/assignment-1c/src/scene/input_file.rs index cfd02b0..d28277d 100644 --- a/assignment-1c/src/scene/input_file.rs +++ b/assignment-1c/src/scene/input_file.rs @@ -1,15 +1,18 @@ -use std::{fs::File, io::Read, path::Path}; +use std::{fs::File, io::Read, path::Path, str::FromStr}; use anyhow::Result; +use itertools::Itertools; +use nalgebra::{Vector2, Vector3}; use crate::{ + image::Color, scene::{ cylinder::Cylinder, data::{Attenuation, Light, LightKind, Material, Object}, sphere::Sphere, Scene, }, - Point, + Point, Vector, }; use super::data::{DepthCueing, ObjectKind}; @@ -51,99 +54,114 @@ impl Scene { } // Do float parsing instead else { - let parts = parts - .map(|s| s.parse::().map_err(|e| e.into())) - .collect::>>()?; + /// Shortcut for reading something from the iterator and converting it + /// into the appropriate format + macro_rules! r { + ($ty:ty) => { + <$ty>::construct(&mut parts, ())? + }; - let read_vec3 = |start: usize| { - ensure!(parts.len() >= start + 3, "Vec3 requires 3 components."); + ($ty:ty, $($ex:expr),* $(,)?) => { + <$ty>::construct(&mut parts, $($ex,)*)? + }; + } - Ok(Point::new(parts[start], parts[start + 1], parts[start + 2])) - }; + /// Shortcut for getting material color + macro_rules! mat { + () => { + match material_color { + Some(v) => v, + None => { + bail!("Each sphere must be preceded by a `mtlcolor` line") + } + } + }; + } match keyword { - "eye" => scene.eye_pos = read_vec3(0)?, - "viewdir" => scene.view_dir = read_vec3(0)?, - "updir" => scene.up_dir = read_vec3(0)?, + "eye" => scene.eye_pos = r!(Vector3), + "viewdir" => scene.view_dir = r!(Vector3), + "updir" => scene.up_dir = r!(Vector3), - "hfov" => scene.hfov = parts[0], - "bkgcolor" => scene.bkg_color = read_vec3(0)?, + "hfov" => scene.hfov = r!(f64), + "bkgcolor" => scene.bkg_color = r!(Color), // light x y z w r g b "light" => { - ensure!(parts.len() == 7, "Light requires 7 params"); + let vec3 = r!(Vector3); + let w = r!(usize); + let color = r!(Color); - let kind = match parts[3] as usize { - 0 => LightKind::Directional { - direction: read_vec3(0)?, - }, + let kind = match w as usize { + 0 => LightKind::Directional { direction: vec3 }, 1 => LightKind::Point { - location: read_vec3(0)?, + location: vec3, attenuation: None, }, _ => bail!("Invalid w; must be either 0 or 1"), }; - let light = Light { - kind, - color: read_vec3(4)?, - }; + + let light = Light { kind, color }; scene.lights.push(light); } // attlight x y z w r g b c1 c2 c3 "attlight" => { - ensure!(parts.len() == 10, "Attenuated light requires 10 params"); + let vec3 = r!(Vector3); + let w = r!(usize); + let color = r!(Color); + let c = r!(Vector3); - let kind = match parts[3] as usize { - // TODO: Is this even defined? Pending TA answer - 0 => LightKind::Directional { - direction: read_vec3(0)?, - }, + let kind = match w as usize { + 0 => LightKind::Directional { direction: vec3 }, 1 => LightKind::Point { - location: read_vec3(0)?, + location: vec3, attenuation: Some(Attenuation { - c1: parts[7], - c2: parts[8], - c3: parts[9], + c1: c.x, + c2: c.y, + c3: c.z, }), }, _ => bail!("Invalid w; must be either 0 or 1"), }; - let light = Light { - kind, - color: read_vec3(4)?, - }; + + let light = Light { kind, color }; scene.lights.push(light); } // depthcueing dcr dcg dcb amax amin distmax distmin "depthcueing" => { - ensure!(parts.len() == 7, "Depth cueing requires 7 params"); + let color = r!(Color); + let a_max = r!(f64); + let a_min = r!(f64); + let dist_max = r!(f64); + let dist_min = r!(f64); - let color = read_vec3(0)?; scene.depth_cueing = DepthCueing { color, - a_max: parts[3], - a_min: parts[4], - dist_max: parts[5], - dist_min: parts[6], + a_max, + a_min, + dist_max, + dist_min, }; } // mtlcolor Odr Odg Odb Osr Osg Osb ka kd ks n "mtlcolor" => { - ensure!(parts.len() == 10, "Material color requires 10 params"); - - let diffuse_color = read_vec3(0)?; - let specular_color = read_vec3(3)?; + let diffuse_color = r!(Color); + let specular_color = r!(Color); + let k_a = r!(f64); + let k_d = r!(f64); + let k_s = r!(f64); + let exponent = r!(f64); let material = Material { diffuse_color, specular_color, - k_a: parts[6], - k_d: parts[7], - k_s: parts[8], - exponent: parts[9], + k_a, + k_d, + k_s, + exponent, }; let idx = scene.materials.len(); @@ -151,33 +169,32 @@ impl Scene { scene.materials.push(material); } - "sphere" => scene.objects.push(Object { - kind: ObjectKind::Sphere(Sphere { - center: read_vec3(0)?, - radius: parts[3], - }), - material: match material_color { - Some(v) => v, - None => { - bail!("Each sphere must be preceded by a `mtlcolor` line") - } - }, - }), + "sphere" => { + let center = r!(Point); + let radius = r!(f64); - "cylinder" => scene.objects.push(Object { - kind: ObjectKind::Cylinder(Cylinder { - center: read_vec3(0)?, - direction: read_vec3(3)?, - radius: parts[6], - length: parts[7], - }), - material: match material_color { - Some(v) => v, - None => { - bail!("Each sphere must be preceded by a `mtlcolor` line") - } - }, - }), + scene.objects.push(Object { + kind: ObjectKind::Sphere(Sphere { center, radius }), + material: mat!(), + }); + } + + "cylinder" => { + let center = r!(Point); + let direction = r!(Vector); + let radius = r!(f64); + let length = r!(f64); + + scene.objects.push(Object { + kind: ObjectKind::Cylinder(Cylinder { + center, + direction, + radius, + length, + }), + material: mat!(), + }); + } _ => bail!("Unknown keyword {keyword}"), } @@ -187,3 +204,55 @@ impl Scene { Ok(scene) } } + +pub trait Construct: Sized { + type Args; + + /// Construct an element of this type from an iterator over strings. + fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result + where + I: Iterator; +} + +macro_rules! impl_construct { + ($ty:ty) => { + impl Construct for $ty { + type Args = (); + + fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result + where + I: Iterator, + { + let item = match it.next() { + Some(v) => v, + None => bail!("Ran out of items"), + }; + + Ok(item.parse()?) + } + } + }; +} + +impl_construct!(f64); +impl_construct!(usize); + +impl Construct for Vector3 { + type Args = (); + + fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result + where + I: Iterator, + { + let (x, y, z) = match it.next_tuple() { + Some(v) => v, + None => bail!("Expected 3 values"), + }; + + let x: f64 = x.parse()?; + let y: f64 = y.parse()?; + let z: f64 = z.parse()?; + + Ok(Vector3::new(x, y, z)) + } +}