slight refactor
This commit is contained in:
parent
34d9459c49
commit
194ea035e2
6 changed files with 188 additions and 161 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
142
assignment-1b/src/scene/input_file.rs
Normal file
142
assignment-1b/src/scene/input_file.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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>,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue