diff options
Diffstat (limited to 'racer-tracer')
| -rw-r--r-- | racer-tracer/config.yml | 11 | ||||
| -rw-r--r-- | racer-tracer/src/camera.rs | 40 | ||||
| -rw-r--r-- | racer-tracer/src/config.rs | 72 | ||||
| -rw-r--r-- | racer-tracer/src/error.rs | 8 | ||||
| -rw-r--r-- | racer-tracer/src/image_action.rs | 111 | ||||
| -rw-r--r-- | racer-tracer/src/main.rs | 76 | ||||
| -rw-r--r-- | racer-tracer/src/scene.rs | 111 | ||||
| -rw-r--r-- | racer-tracer/src/scene/none.rs | 15 | ||||
| -rw-r--r-- | racer-tracer/src/scene/random.rs | 87 | ||||
| -rw-r--r-- | racer-tracer/src/scene/yml.rs | 116 | ||||
| -rw-r--r-- | racer-tracer/src/util.rs | 20 | ||||
| -rw-r--r-- | racer-tracer/src/vec3.rs | 8 |
12 files changed, 489 insertions, 186 deletions
diff --git a/racer-tracer/config.yml b/racer-tracer/config.yml index 0da36dc..e658c1c 100644 --- a/racer-tracer/config.yml +++ b/racer-tracer/config.yml @@ -13,9 +13,14 @@ render: num_threads_height: 10 screen: - width: 1280 - height: 720 + width: 640 + height: 480 -scene: ../resources/scenes/three_balls.yml +loader: + Yml: + path: ../resources/scenes/three_balls.yml image_output_dir: "../" + +image_action: + SavePng # (SavePng, WaitForSignal) diff --git a/racer-tracer/src/camera.rs b/racer-tracer/src/camera.rs index 31c2391..e6396e8 100644 --- a/racer-tracer/src/camera.rs +++ b/racer-tracer/src/camera.rs @@ -1,13 +1,12 @@ use crate::image::Image; use crate::ray::Ray; -use crate::util::degrees_to_radians; +use crate::util::{degrees_to_radians, random_in_unit_disk}; use crate::vec3::Vec3; #[derive(Clone)] pub struct Camera { pub viewport_height: f64, pub viewport_width: f64, - pub focal_length: f64, pub origin: Vec3, pub horizontal: Vec3, pub vertical: Vec3, @@ -15,6 +14,8 @@ pub struct Camera { pub forward: Vec3, pub right: Vec3, pub up: Vec3, + pub lens_radius: f64, + pub focus_distance: f64, } impl Camera { @@ -24,7 +25,8 @@ impl Camera { up: Vec3, vfov: f64, image: &Image, - focal_length: f64, + aperture: f64, + focus_distance: f64, ) -> Camera { let h = (degrees_to_radians(vfov) / 2.0).tan(); let viewport_height = 2.0 * h; @@ -34,45 +36,47 @@ impl Camera { let right = up.cross(&forward).unit_vector(); let up = forward.cross(&right); - let horizontal = viewport_width * right; - let vertical = viewport_height * up; - + let horizontal = focus_distance * viewport_width * right; + let vertical = focus_distance * viewport_height * up; Camera { viewport_height, viewport_width, - focal_length, origin: look_from, horizontal, vertical, - upper_left_corner: look_from + vertical / 2.0 - horizontal / 2.0 - forward, + upper_left_corner: look_from + vertical / 2.0 + - horizontal / 2.0 + - focus_distance * forward, forward, right, up, + lens_radius: aperture * 0.5, + focus_distance, } } pub fn get_ray(&self, u: f64, v: f64) -> Ray { + let ray_direction = self.lens_radius * random_in_unit_disk(); + let offset = self.right * ray_direction.x() + self.up * ray_direction.y(); Ray::new( - self.origin, - self.upper_left_corner + u * self.horizontal - v * self.vertical - self.origin, + self.origin + offset, + self.upper_left_corner + u * self.horizontal - v * self.vertical - self.origin - offset, ) } - // TODO: Add support for rotation - - // TODO: Use forward facing vector pub fn go_forward(&mut self, go: f64) { self.origin += self.forward * go; - self.upper_left_corner = - self.origin + self.vertical / 2.0 - self.horizontal / 2.0 - self.forward; + self.upper_left_corner = self.origin + self.vertical / 2.0 + - self.horizontal / 2.0 + - self.focus_distance * self.forward; } - // TODO: Use right facing vector pub fn go_right(&mut self, go: f64) { self.origin += self.right * go; - self.upper_left_corner = - self.origin + self.vertical / 2.0 - self.horizontal / 2.0 - self.forward; + self.upper_left_corner = self.origin + self.vertical / 2.0 + - self.horizontal / 2.0 + - self.focus_distance * self.forward; } } diff --git a/racer-tracer/src/config.rs b/racer-tracer/src/config.rs index 7bd7887..4cb0880 100644 --- a/racer-tracer/src/config.rs +++ b/racer-tracer/src/config.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use config::File; use serde::Deserialize; @@ -34,20 +34,77 @@ pub struct Args { #[structopt(short = "s", long = "scene")] pub scene: Option<String>, + + #[structopt(long = "image-action")] + pub image_action: Option<ImageAction>, } impl TryFrom<Args> for Config { type Error = TracerError; fn try_from(args: Args) -> Result<Self, TracerError> { - Config::from_file(args.config).map(|mut cfg| { - if args.scene.is_some() { - cfg.scene = args.scene; + Config::from_file(args.config).and_then(|mut cfg| { + if let Some(image_action) = args.image_action { + cfg.image_action = image_action; + } + + if let Some(scene) = args.scene { + if scene == "random" { + cfg.loader = SceneLoader::Random; + } else { + let path = PathBuf::from(scene); + cfg.loader = path + .extension() + .map(|s| s.to_string_lossy()) + .ok_or_else(|| { + TracerError::ArgumentParsingError(format!( + "Could not get extension from scene file: {}", + path.display() + )) + }) + .and_then(|p| match p.as_ref() { + "yml" => Ok(SceneLoader::Yml { path: path.clone() }), + _ => Err(TracerError::ArgumentParsingError(format!( + "Could not find a suitable scene loader for file: {}", + path.display() + ))), + })?; + }; } - cfg + + Ok(cfg) }) } } +#[derive(StructOpt, Debug, Clone, Deserialize, Default)] +pub enum SceneLoader { + #[default] + None, + Yml { + path: PathBuf, + }, + Random, +} + +#[derive(StructOpt, Debug, Clone, Deserialize, Default)] +pub enum ImageAction { + #[default] + WaitForSignal, + SavePng, +} + +impl FromStr for ImageAction { + type Err = TracerError; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "png" => Ok(ImageAction::SavePng), + "show" => Ok(ImageAction::WaitForSignal), + _ => Ok(ImageAction::WaitForSignal), + } + } +} + #[derive(Default, Debug, Deserialize)] pub struct Config { #[serde(default)] @@ -60,7 +117,10 @@ pub struct Config { pub screen: Screen, #[serde(default)] - pub scene: Option<String>, + pub loader: SceneLoader, + + #[serde(default)] + pub image_action: ImageAction, #[serde(default)] pub image_output_dir: Option<PathBuf>, diff --git a/racer-tracer/src/error.rs b/racer-tracer/src/error.rs index 1725d1a..e0e0934 100644 --- a/racer-tracer/src/error.rs +++ b/racer-tracer/src/error.rs @@ -20,6 +20,9 @@ pub enum TracerError { #[error("Config Error ({0}): {1}")] Configuration(String, String), + #[error("Argument parsing Error: {0}")] + ArgumentParsingError(String), + #[error("Unknown Material {0}.")] UnknownMaterial(String), @@ -37,6 +40,9 @@ pub enum TracerError { #[error("Image save error: {0}")] ImageSave(String), + + #[error("Scene failed to load: {0}")] + SceneLoad(String), } impl From<TracerError> for i32 { @@ -57,6 +63,8 @@ impl From<TracerError> for i32 { TracerError::CancelEvent => 10, TracerError::Generic(_) => 11, TracerError::ImageSave(_) => 12, + TracerError::SceneLoad(_) => 13, + TracerError::ArgumentParsingError(_) => 14, } } } diff --git a/racer-tracer/src/image_action.rs b/racer-tracer/src/image_action.rs new file mode 100644 index 0000000..825c506 --- /dev/null +++ b/racer-tracer/src/image_action.rs @@ -0,0 +1,111 @@ +use std::{path::PathBuf, sync::RwLock}; + +use sha2::{Digest, Sha256}; +use synchronoise::SignalEvent; + +use crate::{ + config::{Config, ImageAction as CImageAction}, + error::TracerError, +}; + +pub trait ImageAction: Send + Sync { + fn action( + &self, + screen_buffer: &RwLock<Vec<u32>>, + event: &SignalEvent, + config: &Config, + ) -> Result<(), TracerError>; +} + +impl From<&CImageAction> for Box<dyn ImageAction> { + fn from(image_action: &CImageAction) -> Self { + match image_action { + CImageAction::WaitForSignal => Box::new(WaitForSignal::new()), + CImageAction::SavePng => Box::new(SavePng::new()), + } + } +} + +pub struct SavePng {} + +impl SavePng { + pub fn new() -> Self { + Self {} + } +} + +impl ImageAction for SavePng { + fn action( + &self, + screen_buffer: &RwLock<Vec<u32>>, + _event: &SignalEvent, + config: &Config, + ) -> Result<(), TracerError> { + screen_buffer + .read() + .map_err(|e| TracerError::FailedToAcquireLock(e.to_string())) + .map(|buf| { + // Convert ARGB8 to RGBA8 + buf.iter() + .map(|v| { + let a: u32 = (v >> 24) & 0xff; + let r: u32 = (v >> 16) & 0xff; + let g: u32 = (v >> 8) & 0xff; + let b: u32 = v & 0xff; + + (r << 24) | (g << 16) | (b << 8) | a + }) + .flat_map(|val| val.to_be_bytes()) + .collect::<Vec<u8>>() + }) + .and_then(|buf| match &config.image_output_dir { + Some(image_dir) => { + println!("Saving image..."); + let mut sha = Sha256::new(); + + sha.update(buf.as_slice()); + + let mut file_path = PathBuf::from(image_dir); + file_path.push(format!("{:X}.png", sha.finalize())); + + img::save_buffer( + file_path.as_path(), + buf.as_slice(), + config.screen.width as u32, + config.screen.height as u32, + img::ColorType::Rgba8, + ) + .map_err(|e| { + let error = e.to_string(); + TracerError::ImageSave(error) + }) + .map(|_| { + println!("Saved image to: {}", file_path.to_string_lossy()); + }) + } + None => Ok(()), + }) + } +} + +pub struct WaitForSignal {} + +impl WaitForSignal { + pub fn new() -> Self { + Self {} + } +} + +impl ImageAction for WaitForSignal { + fn action( + &self, + _screen_buffer: &RwLock<Vec<u32>>, + event: &SignalEvent, + _config: &Config, + ) -> Result<(), TracerError> { + println!("Press R to resume."); + event.wait(); + event.reset(); + Ok(()) + } +} diff --git a/racer-tracer/src/main.rs b/racer-tracer/src/main.rs index 5612649..6648c81 100644 --- a/racer-tracer/src/main.rs +++ b/racer-tracer/src/main.rs @@ -4,6 +4,7 @@ mod camera; mod config; mod geometry; mod image; +mod image_action; mod material; mod ray; mod render; @@ -15,14 +16,13 @@ extern crate image as img; use std::{ convert::TryFrom, - path::PathBuf, sync::RwLock, time::{Duration, Instant}, vec::Vec, }; +use image_action::ImageAction; use minifb::{Key, Window, WindowOptions}; -use sha2::{Digest, Sha256}; use synchronoise::SignalEvent; use crate::vec3::Vec3; @@ -38,18 +38,19 @@ use crate::{ fn run(config: Config) -> Result<(), TracerError> { let image = image::Image::new(config.screen.width, config.screen.height); let screen_buffer: RwLock<Vec<u32>> = RwLock::new(vec![0; image.width * image.height]); + let look_from = Vec3::new(13.0, 2.0, 3.0); + let look_at = Vec3::new(0.0, 0.0, 0.0); let camera = RwLock::new(Camera::new( - Vec3::new(-2.0, 2.0, 1.0), - Vec3::new(0.0, 0.0, -1.0), + look_from, + look_at, Vec3::new(0.0, 1.0, 0.0), - 90.0, + 20.0, &image, - 1.0, + 0.1, + 10.0, )); - let scene: Scene = config - .scene - .ok_or(TracerError::NoScene()) - .and_then(Scene::from_file)?; + + let scene = Scene::try_new((&config.loader).into())?; let mut window_res: Result<(), TracerError> = Ok(()); let mut render_res: Result<(), TracerError> = Ok(()); @@ -58,6 +59,8 @@ fn run(config: Config) -> Result<(), TracerError> { let cancel_render = SignalEvent::manual(false); let exit = SignalEvent::manual(false); + let image_action: Box<dyn ImageAction> = (&config.image_action).into(); + rayon::scope(|s| { s.spawn(|_| { while render_res.is_ok() { @@ -94,62 +97,11 @@ fn run(config: Config) -> Result<(), TracerError> { ) .and_then(|_| { render_image.reset(); - println!( "It took {} seconds to render the image.", Instant::now().duration_since(render_time).as_secs() ); - screen_buffer - .read() - .map_err(|e| { - TracerError::FailedToAcquireLock(e.to_string()) - }) - .map(|buf| { - // Convert ARGB8 to RGBA8 - buf.iter() - .map(|v| { - let a: u32 = (v >> 24) & 0xff; - let r: u32 = (v >> 16) & 0xff; - let g: u32 = (v >> 8) & 0xff; - let b: u32 = v & 0xff; - - (r << 24) | (g << 16) | (b << 8) | a - }) - .flat_map(|val| val.to_be_bytes()) - .collect::<Vec<u8>>() - }) - }) - .and_then(|buf| { - match &config.image_output_dir { - Some(image_dir) => { - println!("Saving image..."); - let mut sha = Sha256::new(); - - sha.update(buf.as_slice()); - - let mut file_path = PathBuf::from(image_dir); - file_path.push(format!("{:X}.png", sha.finalize())); - - img::save_buffer( - file_path.as_path(), - buf.as_slice(), - config.screen.width as u32, - config.screen.height as u32, - img::ColorType::Rgba8, - ) - .map_err(|e| { - let error = e.to_string(); - TracerError::ImageSave(error) - }) - .map(|_| { - println!( - "Saved image to: {}", - file_path.to_string_lossy() - ); - }) - } - None => Ok(()), - } + image_action.action(&screen_buffer, &render_image, &config) }) }, ) diff --git a/racer-tracer/src/scene.rs b/racer-tracer/src/scene.rs index c0c6bb7..12e2296 100644 --- a/racer-tracer/src/scene.rs +++ b/racer-tracer/src/scene.rs @@ -1,13 +1,10 @@ -use std::{collections::HashMap, path::Path, sync::Arc}; - -use config::File; -use serde::Deserialize; +pub mod none; +pub mod random; +pub mod yml; use crate::{ - error::TracerError, - geometry::{sphere::Sphere, Hittable}, - material::{dialectric::Dialectric, lambertian::Lambertian, metal::Metal, SharedMaterial}, - vec3::{Color, Vec3}, + config::SceneLoader as CSLoader, error::TracerError, geometry::Hittable, + scene::none::NoneLoader, scene::random::Random, scene::yml::YmlLoader, }; pub struct Scene { @@ -16,20 +13,14 @@ pub struct Scene { impl Scene { #[allow(dead_code)] - pub fn new() -> Self { - Self { - objects: Vec::new(), - } + pub fn try_new(loader: Box<dyn SceneLoader>) -> Result<Self, TracerError> { + loader.load().map(|objects| Self { objects }) } #[allow(dead_code)] pub fn add(&mut self, hittable: Box<dyn Hittable>) { self.objects.push(hittable); } - - pub fn from_file<P: AsRef<Path>>(file: P) -> Result<Self, TracerError> { - SceneData::from_file(file)?.try_into() - } } impl Hittable for Scene { @@ -53,86 +44,16 @@ impl Hittable for Scene { } } -#[derive(Debug, Deserialize)] -enum MaterialData { - Lambertian { color: Color }, - Metal { color: Color, fuzz: f64 }, - Dialectric { refraction_index: f64 }, -} - -#[derive(Debug, Deserialize)] -enum GeometryData { - Sphere { - pos: Vec3, - radius: f64, - material: String, - }, -} - -#[derive(Deserialize)] -struct SceneData { - materials: HashMap<String, MaterialData>, - geometry: Vec<GeometryData>, -} - -impl SceneData { - pub fn from_file<P: AsRef<Path>>(file: P) -> Result<Self, TracerError> { - config::Config::builder() - .add_source(File::from(file.as_ref())) - .build() - .map_err(|e| { - TracerError::Configuration( - file.as_ref().to_string_lossy().into_owned(), - e.to_string(), - ) - })? - .try_deserialize() - .map_err(|e| { - TracerError::Configuration( - file.as_ref().to_string_lossy().into_owned(), - e.to_string(), - ) - }) - } +pub trait SceneLoader: Send + Sync { + fn load(&self) -> Result<Vec<Box<dyn Hittable>>, TracerError>; } -impl TryInto<Scene> for SceneData { - type Error = TracerError; - fn try_into(self) -> Result<Scene, TracerError> { - let mut materials: HashMap<String, SharedMaterial> = HashMap::new(); - self.materials - .into_iter() - .for_each(|(id, material)| match material { - MaterialData::Lambertian { color } => { - materials.insert(id, Arc::new(Box::new(Lambertian::new(color)))); - } - MaterialData::Metal { color, fuzz } => { - materials.insert(id, Arc::new(Box::new(Metal::new(color, fuzz)))); - } - MaterialData::Dialectric { refraction_index } => { - materials.insert(id, Arc::new(Box::new(Dialectric::new(refraction_index)))); - } - }); - - let geometry: Vec<Box<dyn Hittable>> = self - .geometry - .into_iter() - .map(|geo| match geo { - GeometryData::Sphere { - pos, - radius, - material, - } => materials - .get(&material) - .ok_or(TracerError::UnknownMaterial(material)) - .map(|mat| { - let apa: Box<dyn Hittable> = - Box::new(Sphere::new(pos, radius, Arc::clone(mat))); - apa - }), - }) - .collect::<Result<Vec<Box<dyn Hittable>>, TracerError>>()?; - - Ok(Scene { objects: geometry }) +impl From<&CSLoader> for Box<dyn SceneLoader> { + fn from(loader: &CSLoader) -> Self { + match loader { + CSLoader::Yml { path } => Box::new(YmlLoader::new(path.clone())), + CSLoader::Random => Box::new(Random::new()), + CSLoader::None => Box::new(NoneLoader::new()), + } } } diff --git a/racer-tracer/src/scene/none.rs b/racer-tracer/src/scene/none.rs new file mode 100644 index 0000000..2aed105 --- /dev/null +++ b/racer-tracer/src/scene/none.rs @@ -0,0 +1,15 @@ +use crate::{error::TracerError, scene::SceneLoader}; + +pub struct NoneLoader {} + +impl NoneLoader { + pub fn new() -> Self { + Self {} + } +} + +impl SceneLoader for NoneLoader { + fn load(&self) -> Result<Vec<Box<dyn crate::geometry::Hittable>>, TracerError> { + Ok(Vec::new()) + } +} diff --git a/racer-tracer/src/scene/random.rs b/racer-tracer/src/scene/random.rs new file mode 100644 index 0000000..ddbcfc1 --- /dev/null +++ b/racer-tracer/src/scene/random.rs @@ -0,0 +1,87 @@ +use std::sync::Arc; + +use crate::{ + error::TracerError, + geometry::{sphere::Sphere, Hittable}, + material::{dialectric::Dialectric, lambertian::Lambertian, metal::Metal, SharedMaterial}, + scene::SceneLoader, + util::{random_double, random_double_range}, + vec3::{Color, Vec3}, +}; + +pub struct Random {} + +impl Random { + pub fn new() -> Self { + Self {} + } +} + +impl SceneLoader for Random { + fn load(&self) -> Result<Vec<Box<dyn crate::geometry::Hittable>>, TracerError> { + let mut geometry: Vec<Box<dyn Hittable>> = Vec::new(); + let ground_material: SharedMaterial = + Arc::new(Box::new(Lambertian::new(Color::new(0.5, 0.5, 0.5)))); + geometry.push(Box::new(Sphere::new( + Vec3::new(0.0, -1000.0, 0.0), + 1000.0, + ground_material, + ))); + + for a in -11..11 { + for b in -11..11 { + let choose_mat = random_double(); + let center = Vec3::new( + a as f64 + 0.9 * random_double(), + 0.2, + b as f64 + 0.9 * random_double(), + ); + + if (center - Vec3::new(4.0, 0.2, 0.0)).length() > 0.9 { + if choose_mat < 0.8 { + // diffuse + let albedo = Color::random() * Color::random(); + let mat: SharedMaterial = Arc::new(Box::new(Lambertian::new(albedo))); + geometry.push(Box::new(Sphere::new(center, 0.2, mat))); + } else if choose_mat > 0.95 { + // metal + let albedo = Color::random_range(0.5, 1.0); + let fuzz = random_double_range(0.0, 0.5); + let mat: SharedMaterial = Arc::new(Box::new(Metal::new(albedo, fuzz))); + geometry.push(Box::new(Sphere::new(center, 0.2, mat))); + } else { + // glass + let mat: SharedMaterial = Arc::new(Box::new(Dialectric::new(1.5))); + geometry.push(Box::new(Sphere::new(center, 0.2, mat))); + } + } + } + } + + let dial_mat: SharedMaterial = Arc::new(Box::new(Dialectric::new(1.5))); + geometry.push(Box::new(Sphere::new( + Vec3::new(0.0, 1.0, 0.0), + 1.0, + dial_mat, + ))); + + let lamb_mat: SharedMaterial = + Arc::new(Box::new(Lambertian::new(Color::new(0.4, 0.2, 0.1)))); + geometry.push(Box::new(Sphere::new( + Vec3::new(-4.0, 1.0, 0.0), + 1.0, + lamb_mat, + ))); + + let metal_mat: SharedMaterial = + Arc::new(Box::new(Metal::new(Color::new(0.7, 0.6, 0.5), 0.0))); + + geometry.push(Box::new(Sphere::new( + Vec3::new(4.0, 1.0, 0.0), + 1.0, + metal_mat, + ))); + + Ok(geometry) + } +} diff --git a/racer-tracer/src/scene/yml.rs b/racer-tracer/src/scene/yml.rs new file mode 100644 index 0000000..3e69786 --- /dev/null +++ b/racer-tracer/src/scene/yml.rs @@ -0,0 +1,116 @@ +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::Arc, +}; + +use serde::Deserialize; + +use crate::{ + error::TracerError, + geometry::{sphere::Sphere, Hittable}, + material::{dialectric::Dialectric, lambertian::Lambertian, metal::Metal, SharedMaterial}, + scene::SceneLoader, + vec3::{Color, Vec3}, +}; + +use config::File; + +pub struct YmlLoader { + path: PathBuf, +} + +impl YmlLoader { + pub fn new(path: PathBuf) -> Self { + Self { path } + } +} + +impl SceneLoader for YmlLoader { + fn load(&self) -> Result<Vec<Box<dyn crate::geometry::Hittable>>, TracerError> { + let datta = SceneData::from_file(PathBuf::from(&self.path))?; + datta.try_into() + } +} + +#[derive(Debug, Deserialize)] +enum MaterialData { + Lambertian { color: Color }, + Metal { color: Color, fuzz: f64 }, + Dialectric { refraction_index: f64 }, +} + +#[derive(Debug, Deserialize)] +enum GeometryData { + Sphere { + pos: Vec3, + radius: f64, + material: String, + }, +} + +#[derive(Deserialize)] +struct SceneData { + materials: HashMap<String, MaterialData>, + geometry: Vec<GeometryData>, +} + +impl SceneData { + pub fn from_file<P: AsRef<Path>>(file: P) -> Result<Self, TracerError> { + config::Config::builder() + .add_source(File::from(file.as_ref())) + .build() + .map_err(|e| { + TracerError::Configuration( + file.as_ref().to_string_lossy().into_owned(), + dbg!(e).to_string(), + ) + })? + .try_deserialize() + .map_err(|e| { + TracerError::Configuration( + file.as_ref().to_string_lossy().into_owned(), + dbg!(e).to_string(), + ) + }) + } +} + +impl TryInto<Vec<Box<dyn Hittable>>> for SceneData { + type Error = TracerError; + fn try_into(self) -> Result<Vec<Box<dyn Hittable>>, TracerError> { + let mut materials: HashMap<String, SharedMaterial> = HashMap::new(); + self.materials + .into_iter() + .for_each(|(id, material)| match material { + MaterialData::Lambertian { color } => { + materials.insert(id, Arc::new(Box::new(Lambertian::new(color)))); + } + MaterialData::Metal { color, fuzz } => { + materials.insert(id, Arc::new(Box::new(Metal::new(color, fuzz)))); + } + MaterialData::Dialectric { refraction_index } => { + materials.insert(id, Arc::new(Box::new(Dialectric::new(refraction_index)))); + } + }); + + let geometry: Vec<Box<dyn Hittable>> = self + .geometry + .into_iter() + .map(|geo| match geo { + GeometryData::Sphere { + pos, + radius, + material, + } => materials + .get(&material) + .ok_or(TracerError::UnknownMaterial(material)) + .map(|mat| { + Box::new(Sphere::new(pos, radius, Arc::clone(mat))) as Box<dyn Hittable> + }), + }) + .collect::<Result<Vec<Box<dyn Hittable>>, TracerError>>()?; + + Ok(geometry) + } +} diff --git a/racer-tracer/src/util.rs b/racer-tracer/src/util.rs index 2f04567..5fb2d36 100644 --- a/racer-tracer/src/util.rs +++ b/racer-tracer/src/util.rs @@ -1,6 +1,7 @@ use rand::Rng; -// For later use +use crate::vec3::Vec3; + pub fn degrees_to_radians(degrees: f64) -> f64 { degrees * std::f64::consts::PI / 180.0 } @@ -10,8 +11,23 @@ pub fn random_double() -> f64 { rng.gen::<f64>() } -// For later use pub fn random_double_range(min: f64, max: f64) -> f64 { let mut rng = rand::thread_rng(); rng.gen_range(min..max) } + +pub fn random_in_unit_disk() -> Vec3 { + // TODO: This feels not nice + loop { + let p = Vec3::new( + random_double_range(-1.0, 1.0), + random_double_range(-1.0, 1.0), + 0.0, + ); + if p.length_squared() >= 1.0 { + continue; + } + + return p; + } +} diff --git a/racer-tracer/src/vec3.rs b/racer-tracer/src/vec3.rs index 8530bfe..e315203 100644 --- a/racer-tracer/src/vec3.rs +++ b/racer-tracer/src/vec3.rs @@ -275,6 +275,14 @@ impl ops::Mul<Vec3> for f64 { } } +impl ops::Mul<&f64> for Vec3 { + type Output = Vec3; + + fn mul(self, rhs: &f64) -> Self::Output { + Vec3::new(self.data[0] * rhs, self.data[1] * rhs, self.data[2] * rhs) + } +} + impl ops::MulAssign<f64> for Vec3 { fn mul_assign(&mut self, rhs: f64) { self.data[0] *= rhs; |
