summaryrefslogtreecommitdiff
path: root/racer-tracer/src
diff options
context:
space:
mode:
Diffstat (limited to 'racer-tracer/src')
-rw-r--r--racer-tracer/src/camera.rs40
-rw-r--r--racer-tracer/src/config.rs72
-rw-r--r--racer-tracer/src/error.rs8
-rw-r--r--racer-tracer/src/image_action.rs111
-rw-r--r--racer-tracer/src/main.rs76
-rw-r--r--racer-tracer/src/scene.rs111
-rw-r--r--racer-tracer/src/scene/none.rs15
-rw-r--r--racer-tracer/src/scene/random.rs87
-rw-r--r--racer-tracer/src/scene/yml.rs116
-rw-r--r--racer-tracer/src/util.rs20
-rw-r--r--racer-tracer/src/vec3.rs8
11 files changed, 481 insertions, 183 deletions
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;