Minor refactor
This commit is contained in:
parent
05462fde28
commit
8ab0a9d139
11 changed files with 130 additions and 81 deletions
|
@ -1,12 +1,12 @@
|
|||
use generator::{done, Gn};
|
||||
use itertools::Itertools;
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{BufRead, BufReader, Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use generator::{done, Gn};
|
||||
use itertools::Itertools;
|
||||
use nalgebra::Vector3;
|
||||
|
||||
/// A pixel color represented by a red, green, and blue value in the range 0-1.
|
||||
|
@ -38,20 +38,28 @@ impl Image {
|
|||
let mut line_reader = BufReader::new(r);
|
||||
|
||||
let mut header = String::new();
|
||||
line_reader.read_line(&mut header)?;
|
||||
let parts = header.split(" ").collect::<Vec<_>>();
|
||||
line_reader
|
||||
.read_line(&mut header)
|
||||
.context("Could not read line")?;
|
||||
let parts = header.trim().split(" ").collect::<Vec<_>>();
|
||||
|
||||
let width = parts[1].parse::<usize>()?;
|
||||
let height = parts[2].parse::<usize>()?;
|
||||
let max_value = parts[3].parse::<usize>()?;
|
||||
let width = parts[1].parse::<usize>().context("Could not read width")?;
|
||||
let height = parts[2].parse::<usize>().context("Could not read height")?;
|
||||
let max_value = parts[3]
|
||||
.parse::<usize>()
|
||||
.context("Could not read max value")?;
|
||||
|
||||
// Generator for reading numbers
|
||||
let numbers = Gn::<()>::new_scoped(move |mut s| {
|
||||
macro_rules! gen_try {
|
||||
($expr:expr) => {
|
||||
($expr:expr, $str:expr $(, $($arg:expr),* $(,)?)?) => {
|
||||
match $expr {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
s.yield_(Err(anyhow::Error::from(e)));
|
||||
s.yield_(
|
||||
Err(anyhow::Error::from(e))
|
||||
.with_context(|| format!($str $(, $($arg,)*)?)),
|
||||
);
|
||||
done!();
|
||||
}
|
||||
}
|
||||
|
@ -59,11 +67,12 @@ impl Image {
|
|||
}
|
||||
|
||||
for line in line_reader.lines() {
|
||||
let line = gen_try!(line);
|
||||
let parts = line.split_whitespace();
|
||||
let line = gen_try!(line, "Could not read line");
|
||||
let parts = line.trim().split_whitespace();
|
||||
|
||||
for part in parts {
|
||||
let int = gen_try!(part.parse::<f64>());
|
||||
let int =
|
||||
gen_try!(part.parse::<u64>(), "Could not read int from: {}", part);
|
||||
s.yield_(Ok(int));
|
||||
}
|
||||
}
|
||||
|
@ -78,9 +87,9 @@ impl Image {
|
|||
None => bail!("Not enough elements"),
|
||||
};
|
||||
|
||||
let r = r? / max_value as f64;
|
||||
let g = g? / max_value as f64;
|
||||
let b = b? / max_value as f64;
|
||||
let r = r? as f64 / max_value as f64;
|
||||
let g = g? as f64 / max_value as f64;
|
||||
let b = b? as f64 / max_value as f64;
|
||||
|
||||
let color = Color::new(r, g, b);
|
||||
data.push(color);
|
||||
|
|
|
@ -112,8 +112,9 @@ fn main() -> Result<()> {
|
|||
|
||||
Ok(match earliest_intersection {
|
||||
// Take the object's material color
|
||||
Some((obj_idx, intersection_context, object)) => scene
|
||||
.compute_pixel_color(obj_idx, object.material, intersection_context),
|
||||
Some((obj_idx, intersection_context, object)) => {
|
||||
scene.compute_pixel_color(obj_idx, object, intersection_context)
|
||||
}
|
||||
|
||||
// There was no intersection, so this should default to the scene's
|
||||
// background color
|
||||
|
|
|
@ -200,7 +200,7 @@ mod tests {
|
|||
let ray = Ray::from_endpoints(eye, end);
|
||||
|
||||
let scene = Scene::default();
|
||||
let res = cylinder.intersects_ray_at(&scene, &ray);
|
||||
let _res = cylinder.intersects_ray_at(&scene, &ray);
|
||||
// panic!("Result: {res:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +1,11 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::image::Color;
|
||||
use crate::ray::Ray;
|
||||
use crate::utils::cross;
|
||||
use crate::Point;
|
||||
|
||||
use super::cylinder::Cylinder;
|
||||
use super::illumination::IntersectionContext;
|
||||
use super::sphere::Sphere;
|
||||
use super::triangle::Triangle;
|
||||
use super::Scene;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ObjectKind {
|
||||
Sphere(Sphere),
|
||||
Cylinder(Cylinder),
|
||||
Triangle(Triangle),
|
||||
}
|
||||
|
||||
impl ObjectKind {
|
||||
/// Determine where the ray intersects this object, returning the earliest
|
||||
/// time this happens. Returns None if no intersection occurs.
|
||||
///
|
||||
/// Also known as Trace_Ray in the slides, except not the part where it calls
|
||||
/// Shade_Ray.
|
||||
pub fn intersects_ray_at(
|
||||
&self,
|
||||
scene: &Scene,
|
||||
ray: &Ray,
|
||||
) -> Result<Option<IntersectionContext>> {
|
||||
match self {
|
||||
ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray),
|
||||
ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray),
|
||||
ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An object in the scene
|
||||
#[derive(Debug)]
|
||||
pub struct Object {
|
||||
pub kind: ObjectKind,
|
||||
|
||||
/// Index into the scene's material color list
|
||||
pub material: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Rect {
|
||||
pub upper_left: Point,
|
||||
|
|
|
@ -25,17 +25,27 @@ impl Scene {
|
|||
pub fn compute_pixel_color(
|
||||
&self,
|
||||
obj_idx: usize,
|
||||
material_idx: usize,
|
||||
object: &Object,
|
||||
intersection_context: IntersectionContext,
|
||||
) -> Color {
|
||||
// TODO: Does it make sense to make this function fallible from an API
|
||||
// design standpoint?
|
||||
let material = match self.materials.get(material_idx) {
|
||||
let material = match self.materials.get(object.material_idx) {
|
||||
Some(v) => v,
|
||||
None => return self.bkg_color,
|
||||
};
|
||||
|
||||
let ambient_component = material.k_a * material.diffuse_color;
|
||||
let diffuse_color = match object.texture_idx {
|
||||
Some(texture_idx) => {
|
||||
let texture = &self.textures[texture_idx];
|
||||
|
||||
// TODO: need to implement
|
||||
material.diffuse_color
|
||||
}
|
||||
None => material.diffuse_color,
|
||||
};
|
||||
|
||||
let ambient_component = material.k_a * diffuse_color;
|
||||
|
||||
// Diffuse and specular lighting for each separate light
|
||||
let diffuse_and_specular: Color = self
|
||||
|
@ -51,7 +61,7 @@ impl Scene {
|
|||
((light_direction + viewer_direction) / 2.0).normalize();
|
||||
|
||||
let diffuse_component = material.k_d
|
||||
* material.diffuse_color
|
||||
* diffuse_color
|
||||
* dot(normal, light_direction).max(0.0);
|
||||
|
||||
let specular_component = material.k_s
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use std::{
|
||||
fs::File,
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use itertools::Itertools;
|
||||
|
@ -15,7 +13,7 @@ use crate::{
|
|||
data::{Attenuation, Light, LightKind, Material, Object},
|
||||
sphere::Sphere,
|
||||
triangle::Triangle,
|
||||
Scene,
|
||||
Scene, texture::Texture,
|
||||
},
|
||||
Point, Vector,
|
||||
};
|
||||
|
@ -37,7 +35,8 @@ impl Scene {
|
|||
};
|
||||
|
||||
let mut scene = Scene::default();
|
||||
let mut material_color = None;
|
||||
let mut material_idx = None;
|
||||
let mut texture_idx = None;
|
||||
|
||||
for line in contents.lines() {
|
||||
// Split lines into words. `parts' is an iterator, which is consumed upon
|
||||
|
@ -167,7 +166,7 @@ impl Scene {
|
|||
};
|
||||
|
||||
let idx = scene.materials.len();
|
||||
material_color = Some(idx);
|
||||
material_idx = Some(idx);
|
||||
scene.materials.push(material);
|
||||
}
|
||||
|
||||
|
@ -177,7 +176,8 @@ impl Scene {
|
|||
|
||||
scene.objects.push(Object {
|
||||
kind: ObjectKind::Sphere(Sphere { center, radius }),
|
||||
material: u!(material_color),
|
||||
material_idx: u!(material_idx),
|
||||
texture_idx,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,8 @@ impl Scene {
|
|||
radius,
|
||||
length,
|
||||
}),
|
||||
material: u!(material_color),
|
||||
material_idx: u!(material_idx),
|
||||
texture_idx,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -237,7 +238,8 @@ impl Scene {
|
|||
};
|
||||
scene.objects.push(Object {
|
||||
kind: ObjectKind::Triangle(triangle),
|
||||
material: u!(material_color),
|
||||
material_idx: u!(material_idx),
|
||||
texture_idx,
|
||||
});
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
|
@ -293,8 +295,12 @@ impl Scene {
|
|||
None => bail!("Did not provide path."),
|
||||
};
|
||||
|
||||
let idx = scene.textures.len();
|
||||
texture_idx = Some(idx);
|
||||
|
||||
let image = Image::from_file(path)?;
|
||||
scene.textures.push(image);
|
||||
let texture = Texture::new(image);
|
||||
scene.textures.push(texture);
|
||||
}
|
||||
|
||||
_ => bail!("Unknown keyword {keyword}"),
|
||||
|
|
|
@ -3,14 +3,15 @@ pub mod data;
|
|||
pub mod illumination;
|
||||
pub mod input_file;
|
||||
pub mod sphere;
|
||||
pub mod texture;
|
||||
pub mod triangle;
|
||||
|
||||
use nalgebra::Vector2;
|
||||
pub mod object;
|
||||
|
||||
use crate::image::{Color, Image};
|
||||
use crate::{Point, Point2, Vector};
|
||||
|
||||
use self::data::{Attenuation, DepthCueing, Light, Material, Object};
|
||||
use self::texture::Texture;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Scene {
|
||||
|
@ -35,7 +36,7 @@ pub struct Scene {
|
|||
pub objects: Vec<Object>,
|
||||
|
||||
/// List of textures
|
||||
pub textures: Vec<Image>,
|
||||
pub textures: Vec<Texture>,
|
||||
|
||||
/// Coordinates into a texture image
|
||||
pub texture_vertices: Vec<Point2>,
|
||||
|
|
47
assignment-1c/src/scene/object.rs
Normal file
47
assignment-1c/src/scene/object.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use crate::ray::Ray;
|
||||
|
||||
use super::cylinder::Cylinder;
|
||||
use super::illumination::IntersectionContext;
|
||||
use super::sphere::Sphere;
|
||||
use super::triangle::Triangle;
|
||||
use super::Scene;
|
||||
|
||||
/// An object in the scene
|
||||
#[derive(Debug)]
|
||||
pub struct Object {
|
||||
pub kind: ObjectKind,
|
||||
|
||||
/// Index into the scene's material color list
|
||||
pub material_idx: usize,
|
||||
|
||||
/// If this object has a texture, this is the index into the texture list
|
||||
pub texture_idx: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ObjectKind {
|
||||
Sphere(Sphere),
|
||||
Cylinder(Cylinder),
|
||||
Triangle(Triangle),
|
||||
}
|
||||
|
||||
impl ObjectKind {
|
||||
/// Determine where the ray intersects this object, returning the earliest
|
||||
/// time this happens. Returns None if no intersection occurs.
|
||||
///
|
||||
/// Also known as Trace_Ray in the slides, except not the part where it calls
|
||||
/// Shade_Ray.
|
||||
pub fn intersects_ray_at(
|
||||
&self,
|
||||
scene: &Scene,
|
||||
ray: &Ray,
|
||||
) -> Result<Option<IntersectionContext>> {
|
||||
match self {
|
||||
ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray),
|
||||
ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray),
|
||||
ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray),
|
||||
}
|
||||
}
|
||||
}
|
16
assignment-1c/src/scene/texture.rs
Normal file
16
assignment-1c/src/scene/texture.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::image::{Color, Image};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Texture(Image);
|
||||
|
||||
impl Texture {
|
||||
pub fn new(image: Image) -> Self {
|
||||
Self(image)
|
||||
}
|
||||
|
||||
/// Returns a pixel at the given coordinate. For non-lattice coordinates,
|
||||
/// interpolation of the image is done.
|
||||
pub fn pixel_at(&self, x: f64, y: f64) -> Option<Color> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ use ordered_float::NotNan;
|
|||
|
||||
use crate::ray::Ray;
|
||||
use crate::utils::{cross, dot};
|
||||
use crate::Point;
|
||||
|
||||
|
||||
use super::illumination::IntersectionContext;
|
||||
use super::Scene;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
packages = (with pkgs; [
|
||||
cargo-deny
|
||||
cargo-edit
|
||||
cargo-expand
|
||||
cargo-flamegraph
|
||||
cargo-watch
|
||||
imagemagick
|
||||
|
|
Loading…
Reference in a new issue