diff --git a/assignment-1b/examples/Hw1bSample1.txt b/assignment-1b/examples/Hw1bSample1.txt index dc42708..75ea085 100755 --- a/assignment-1b/examples/Hw1bSample1.txt +++ b/assignment-1b/examples/Hw1bSample1.txt @@ -4,11 +4,15 @@ viewdir 0 0.1 -1 hfov 90 updir 0 1 0 bkgcolor 0.1 0.1 0.1 + light -1 -1 -1 0 0.9 0.5 0.05 + mtlcolor 0 1 0 1 1 1 0.6 0.2 0.2 10 sphere 0 1.5 -4 1 + mtlcolor 0 1 0 1 1 1 0.1 0.8 0.2 10 sphere -1.275 -0.75 -4 1 + mtlcolor 0 1 0 1 1 1 0.1 0.2 0.8 10 sphere 1.275 -0.75 -4 1 diff --git a/assignment-1b/src/input_file.rs b/assignment-1b/src/input_file.rs deleted file mode 100644 index 9b9dfcf..000000000 --- a/assignment-1b/src/input_file.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::{fs::File, io::Read, path::Path}; - -use anyhow::Result; -use nalgebra::Vector3; - -use crate::{ - image::Color, - scene::{ - cylinder::Cylinder, - data::{Light, LightKind, Material, Object, Scene}, - sphere::Sphere, - }, -}; - -/// Parse the input file into a scene -pub fn parse_input_file(path: impl AsRef) -> Result { - let contents = { - let mut contents = String::new(); - let mut file = File::open(path.as_ref())?; - file.read_to_string(&mut contents)?; - contents - }; - - let mut scene = Scene::default(); - let mut material_color = None; - - for line in contents.lines() { - let mut parts = line.split_whitespace(); - let keyword = match parts.next() { - Some(v) => v, - None => continue, - }; - - if keyword == "imsize" { - let parts = parts - .map(|s| s.parse::().map_err(|e| e.into())) - .collect::>>()?; - if let [width, height] = parts[..] { - scene.image_width = width; - scene.image_height = height; - } - } else if keyword == "projection" { - if let Some("parallel") = parts.next() { - scene.parallel_projection = true; - } - } - // Do float parsing instead - else { - let parts = parts - .map(|s| s.parse::().map_err(|e| e.into())) - .collect::>>()?; - - let read_vec3 = |start: usize| { - if parts.len() < start + 3 { - bail!("Vec3 requires 3 components."); - } - Ok(Vector3::new( - parts[start], - parts[start + 1], - parts[start + 2], - )) - }; - - match keyword { - "eye" => scene.eye_pos = read_vec3(0)?, - "viewdir" => scene.view_dir = read_vec3(0)?, - "updir" => scene.up_dir = read_vec3(0)?, - - "hfov" => scene.hfov = parts[0], - "bkgcolor" => scene.bkg_color = read_vec3(0)?, - - "light" => { - let kind = match parts[3] as usize { - 0 => LightKind::Directional { - direction: read_vec3(0)?, - }, - 1 => LightKind::Point { - location: read_vec3(0)?, - }, - _ => bail!("Invalid w"), - }; - let light = Light { - kind, - color: read_vec3(4)?, - }; - scene.lights.push(light); - } - - // mtlcolor Odr Odg Odb Osr Osg Osb ka kd ks n - "mtlcolor" => { - let diffuse_color = read_vec3(0)?; - let specular_color = read_vec3(3)?; - - let material = Material { - diffuse_color, - specular_color, - k_a: parts[6], - k_d: parts[7], - k_s: parts[8], - exponent: parts[9], - }; - - let idx = scene.materials.len(); - material_color = Some(idx); - scene.materials.push(material); - } - - "sphere" => scene.objects.push(Object { - kind: Box::new(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"), - }, - }), - - "cylinder" => scene.objects.push(Object { - kind: Box::new(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"), - }, - }), - - _ => bail!("Unknown keyword {keyword}"), - } - } - } - - Ok(scene) -} diff --git a/assignment-1b/src/main.rs b/assignment-1b/src/main.rs index 65c0cea..ba32daa 100644 --- a/assignment-1b/src/main.rs +++ b/assignment-1b/src/main.rs @@ -4,7 +4,6 @@ extern crate anyhow; extern crate derivative; mod image; -mod input_file; mod ray; mod scene; mod utils; @@ -16,9 +15,9 @@ use anyhow::Result; use clap::Parser; use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use scene::data::ObjectKind; +use scene::Scene; use crate::image::Image; -use crate::input_file::parse_input_file; use crate::ray::Ray; /// Simple raycaster. @@ -49,7 +48,7 @@ fn main() -> Result<()> { .output_path .unwrap_or_else(|| opt.input_path.with_extension("ppm")); - let mut scene = parse_input_file(&opt.input_path)?; + let mut scene = Scene::from_input_file(&opt.input_path)?; let distance = opt.distance; if opt.force_parallel { diff --git a/assignment-1b/src/scene/data.rs b/assignment-1b/src/scene/data.rs index f053285..154a1f2 100644 --- a/assignment-1b/src/scene/data.rs +++ b/assignment-1b/src/scene/data.rs @@ -7,6 +7,8 @@ use ordered_float::NotNan; use crate::image::Color; use crate::ray::Ray; +use super::Scene; + pub trait ObjectKind: Debug + Send + Sync { /// Determine where the ray intersects this object, returning the earliest /// time this happens. Returns None if no intersection occurs. @@ -64,24 +66,12 @@ pub struct Light { } #[derive(Debug, Default)] -pub struct Scene { - pub eye_pos: Vector3, - pub view_dir: Vector3, - pub up_dir: Vector3, - - /// Horizontal field of view (in degrees) - pub hfov: f64, - pub parallel_projection: bool, - - pub image_width: usize, - pub image_height: usize, - - /// Background color - pub bkg_color: Color, - - pub materials: Vec, - pub lights: Vec, - pub objects: Vec, +pub struct DepthCueing { + color: Color, + a_max: f64, + a_min: f64, + dist_max: f64, + dist_min: f64, } /// Information about an intersection @@ -130,6 +120,7 @@ impl Scene { let ambient_component = material.k_a * material.diffuse_color; + // Diffuse and specular lighting for each separate light let diffuse_and_specular: Vector3 = self .lights .iter() diff --git a/assignment-1b/src/scene/input_file.rs b/assignment-1b/src/scene/input_file.rs new file mode 100644 index 000000000..a1c7226 --- /dev/null +++ b/assignment-1b/src/scene/input_file.rs @@ -0,0 +1,142 @@ +use std::{fs::File, io::Read, path::Path}; + +use anyhow::Result; +use nalgebra::Vector3; + +use crate::scene::{ + cylinder::Cylinder, + data::{Light, LightKind, Material, Object}, + sphere::Sphere, + Scene, +}; + +impl Scene { + /// Parse the input file into a scene + pub fn from_input_file(path: impl AsRef) -> Result { + let contents = { + let mut contents = String::new(); + let mut file = File::open(path.as_ref())?; + file.read_to_string(&mut contents)?; + contents + }; + + let mut scene = Scene::default(); + let mut material_color = None; + + for line in contents.lines() { + let mut parts = line.split_whitespace(); + let keyword = match parts.next() { + Some(v) => v, + None => continue, + }; + + if keyword == "imsize" { + let parts = parts + .map(|s| s.parse::().map_err(|e| e.into())) + .collect::>>()?; + if let [width, height] = parts[..] { + scene.image_width = width; + scene.image_height = height; + } + } else if keyword == "projection" { + if let Some("parallel") = parts.next() { + scene.parallel_projection = true; + } + } + // Do float parsing instead + else { + let parts = parts + .map(|s| s.parse::().map_err(|e| e.into())) + .collect::>>()?; + + let read_vec3 = |start: usize| { + if parts.len() < start + 3 { + bail!("Vec3 requires 3 components."); + } + Ok(Vector3::new( + parts[start], + parts[start + 1], + parts[start + 2], + )) + }; + + match keyword { + "eye" => scene.eye_pos = read_vec3(0)?, + "viewdir" => scene.view_dir = read_vec3(0)?, + "updir" => scene.up_dir = read_vec3(0)?, + + "hfov" => scene.hfov = parts[0], + "bkgcolor" => scene.bkg_color = read_vec3(0)?, + + "light" => { + let kind = match parts[3] as usize { + 0 => LightKind::Directional { + direction: read_vec3(0)?, + }, + 1 => LightKind::Point { + location: read_vec3(0)?, + }, + _ => bail!("Invalid w"), + }; + let light = Light { + kind, + color: read_vec3(4)?, + }; + scene.lights.push(light); + } + + // mtlcolor Odr Odg Odb Osr Osg Osb ka kd ks n + "mtlcolor" => { + let diffuse_color = read_vec3(0)?; + let specular_color = read_vec3(3)?; + + let material = Material { + diffuse_color, + specular_color, + k_a: parts[6], + k_d: parts[7], + k_s: parts[8], + exponent: parts[9], + }; + + let idx = scene.materials.len(); + material_color = Some(idx); + scene.materials.push(material); + } + + "sphere" => scene.objects.push(Object { + kind: Box::new(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") + } + }, + }), + + "cylinder" => scene.objects.push(Object { + kind: Box::new(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") + } + }, + }), + + _ => bail!("Unknown keyword {keyword}"), + } + } + } + + Ok(scene) + } +} diff --git a/assignment-1b/src/scene/mod.rs b/assignment-1b/src/scene/mod.rs index e0465c9..3405b15 100644 --- a/assignment-1b/src/scene/mod.rs +++ b/assignment-1b/src/scene/mod.rs @@ -1,3 +1,32 @@ -pub mod data; -pub mod sphere; pub mod cylinder; +pub mod data; +pub mod input_file; +pub mod sphere; + +use nalgebra::Vector3; + +use crate::image::Color; + +use self::data::{DepthCueing, Light, Material, Object}; + +#[derive(Debug, Default)] +pub struct Scene { + pub eye_pos: Vector3, + pub view_dir: Vector3, + pub up_dir: Vector3, + + /// Horizontal field of view (in degrees) + pub hfov: f64, + pub parallel_projection: bool, + + pub image_width: usize, + pub image_height: usize, + + /// Background color + pub bkg_color: Color, + pub depth_cueing: DepthCueing, + + pub materials: Vec, + pub lights: Vec, + pub objects: Vec, +}