Refactor with macros
This commit is contained in:
parent
00000540b4
commit
973e03df4d
1 changed files with 147 additions and 78 deletions
|
@ -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::<f64>().map_err(|e| e.into()))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
/// 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<f64>),
|
||||
"viewdir" => scene.view_dir = r!(Vector3<f64>),
|
||||
"updir" => scene.up_dir = r!(Vector3<f64>),
|
||||
|
||||
"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<f64>);
|
||||
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<f64>);
|
||||
let w = r!(usize);
|
||||
let color = r!(Color);
|
||||
let c = r!(Vector3<f64>);
|
||||
|
||||
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<Self>
|
||||
where
|
||||
I: Iterator<Item = &'a str>;
|
||||
}
|
||||
|
||||
macro_rules! impl_construct {
|
||||
($ty:ty) => {
|
||||
impl Construct for $ty {
|
||||
type Args = ();
|
||||
|
||||
fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result<Self>
|
||||
where
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
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<f64> {
|
||||
type Args = ();
|
||||
|
||||
fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result<Self>
|
||||
where
|
||||
I: Iterator<Item = &'a str>,
|
||||
{
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue