slight refactor

This commit is contained in:
Michael Zhang 2023-02-15 01:20:03 -06:00
parent 34d9459c49
commit 194ea035e2
6 changed files with 188 additions and 161 deletions

View file

@ -4,11 +4,15 @@ viewdir 0 0.1 -1
hfov 90 hfov 90
updir 0 1 0 updir 0 1 0
bkgcolor 0.1 0.1 0.1 bkgcolor 0.1 0.1 0.1
light -1 -1 -1 0 0.9 0.5 0.05 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 mtlcolor 0 1 0 1 1 1 0.6 0.2 0.2 10
sphere 0 1.5 -4 1 sphere 0 1.5 -4 1
mtlcolor 0 1 0 1 1 1 0.1 0.8 0.2 10 mtlcolor 0 1 0 1 1 1 0.1 0.8 0.2 10
sphere -1.275 -0.75 -4 1 sphere -1.275 -0.75 -4 1
mtlcolor 0 1 0 1 1 1 0.1 0.2 0.8 10 mtlcolor 0 1 0 1 1 1 0.1 0.2 0.8 10
sphere 1.275 -0.75 -4 1 sphere 1.275 -0.75 -4 1

View file

@ -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<Path>) -> Result<Scene> {
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::<usize>().map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?;
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::<f64>().map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?;
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)
}

View file

@ -4,7 +4,6 @@ extern crate anyhow;
extern crate derivative; extern crate derivative;
mod image; mod image;
mod input_file;
mod ray; mod ray;
mod scene; mod scene;
mod utils; mod utils;
@ -16,9 +15,9 @@ use anyhow::Result;
use clap::Parser; use clap::Parser;
use rayon::prelude::{IntoParallelIterator, ParallelIterator}; use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use scene::data::ObjectKind; use scene::data::ObjectKind;
use scene::Scene;
use crate::image::Image; use crate::image::Image;
use crate::input_file::parse_input_file;
use crate::ray::Ray; use crate::ray::Ray;
/// Simple raycaster. /// Simple raycaster.
@ -49,7 +48,7 @@ fn main() -> Result<()> {
.output_path .output_path
.unwrap_or_else(|| opt.input_path.with_extension("ppm")); .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; let distance = opt.distance;
if opt.force_parallel { if opt.force_parallel {

View file

@ -7,6 +7,8 @@ use ordered_float::NotNan;
use crate::image::Color; use crate::image::Color;
use crate::ray::Ray; use crate::ray::Ray;
use super::Scene;
pub trait ObjectKind: Debug + Send + Sync { pub trait ObjectKind: Debug + Send + Sync {
/// Determine where the ray intersects this object, returning the earliest /// Determine where the ray intersects this object, returning the earliest
/// time this happens. Returns None if no intersection occurs. /// time this happens. Returns None if no intersection occurs.
@ -64,24 +66,12 @@ pub struct Light {
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Scene { pub struct DepthCueing {
pub eye_pos: Vector3<f64>, color: Color,
pub view_dir: Vector3<f64>, a_max: f64,
pub up_dir: Vector3<f64>, a_min: f64,
dist_max: f64,
/// Horizontal field of view (in degrees) dist_min: f64,
pub hfov: f64,
pub parallel_projection: bool,
pub image_width: usize,
pub image_height: usize,
/// Background color
pub bkg_color: Color,
pub materials: Vec<Material>,
pub lights: Vec<Light>,
pub objects: Vec<Object>,
} }
/// Information about an intersection /// Information about an intersection
@ -130,6 +120,7 @@ impl Scene {
let ambient_component = material.k_a * material.diffuse_color; let ambient_component = material.k_a * material.diffuse_color;
// Diffuse and specular lighting for each separate light
let diffuse_and_specular: Vector3<f64> = self let diffuse_and_specular: Vector3<f64> = self
.lights .lights
.iter() .iter()

View file

@ -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<Path>) -> Result<Self> {
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::<usize>().map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?;
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::<f64>().map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?;
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)
}
}

View file

@ -1,3 +1,32 @@
pub mod data;
pub mod sphere;
pub mod cylinder; 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<f64>,
pub view_dir: Vector3<f64>,
pub up_dir: Vector3<f64>,
/// 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<Material>,
pub lights: Vec<Light>,
pub objects: Vec<Object>,
}