diff --git a/data/config.toml b/data/config.toml index 3ad5e0e..968d416 100644 --- a/data/config.toml +++ b/data/config.toml @@ -12,9 +12,9 @@ tile_size = [32, 32] [[render_settings]] threads = 20 filename = "beauty" -min_samples = 32 +min_samples = 256 min_bounces = 1 -max_bounces = 12 +max_bounces = 2 hwss = false camera_id = "main" russian_roulette = true diff --git a/data/lib_textures.toml b/data/lib_textures.toml index 3050940..6051717 100644 --- a/data/lib_textures.toml +++ b/data/lib_textures.toml @@ -25,7 +25,6 @@ curves = ["srgb_r", "srgb_g", "srgb_b", "flat_zero"] [[low_res_hdri]] type = "EXR" filename = "data/hdri/kloofendal_43d_clear_puresky_1k.exr" -alpha_fill = 0.0 curves = ["srgb_r", "srgb_g", "srgb_b", "flat_zero"] [[kiara_dawn_8k]] @@ -65,7 +64,6 @@ curve = "simple_yellow" [[test_texture]] type = "Texture4" filename = "data/textures/test.png" -alpha_fill = 0.0 curves = ["srgb_r", "srgb_g", "srgb_b", "flat_one"] diff --git a/data/scenes/bokeh_test.toml b/data/scenes/test_bokeh.toml similarity index 98% rename from data/scenes/bokeh_test.toml rename to data/scenes/test_bokeh.toml index 3533d24..8b6075c 100644 --- a/data/scenes/bokeh_test.toml +++ b/data/scenes/test_bokeh.toml @@ -670,8 +670,10 @@ origin = [0.5, 20.0, 0.0] [[cameras]] type = "SimpleCamera" name = "main" -look_from = [-5.0, 0.0, 0.5] -look_at = [0.0, 0.0, 0.4] -aperture_size = 0.1 +look_from = [0.0, -5.0, 0.5] +look_at = [0.0, 0.0, 0.2] +lens_diameter = 0.1 +aperture_diameter = 0.1 +aperture = {type = "Bladed", blades = 3, sharpness = 0.5} focal_distance = 5.0 vfov = 45.0 diff --git a/src/bin/main.rs b/src/bin/main.rs index ab6f440..e5d3997 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -16,10 +16,12 @@ extern crate tracing; use std::fs; use std::path::PathBuf; +use std::thread::JoinHandle; use std::{fs::File, sync::Arc}; use tracing::level_filters::LevelFilter; use tracing_subscriber::prelude::*; +// TODO: switch to clap use structopt::StructOpt; #[cfg(all(target_os = "windows", feature = "notification"))] @@ -32,18 +34,29 @@ use win32_notification::NotificationBuilder; struct Opt { #[structopt(long)] pub scene: Option, + #[structopt(long, default_value = "data/config.toml")] pub config: String, + #[structopt(short = "n", long)] pub dry_run: bool, - #[structopt(short = "pll", long, default_value = "warn")] + + #[structopt(long, default_value = "warn")] pub stdout_log_level: String, - #[structopt(short = "wll", long, default_value = "info")] + + #[structopt(long, default_value = "info")] pub write_log_level: String, } -fn construct_scene(config: &mut Config) -> anyhow::Result { - construct_world(config, PathBuf::from(config.scene_file.clone())) +fn construct_scene( + config: &mut Config, + mut handles: &mut Vec>, +) -> anyhow::Result { + construct_world( + config, + PathBuf::from(config.scene_file.clone()), + &mut handles, + ) } fn construct_renderer(config: &Config) -> Box { @@ -128,12 +141,21 @@ fn main() { toml_config.default_scene_file = opts.scene.unwrap_or(toml_config.default_scene_file); let mut config = Config::from(toml_config); - let world = construct_scene(&mut config); + + let mut handles = Vec::new(); + let world = construct_scene(&mut config, &mut handles); if world.is_err() { - error!( - "fatal error parsing world, aborting. error is {:?}", - world.err().unwrap() - ); + let as_error = world.unwrap_err(); + // error!( + // "fatal error parsing world, aborting. error is {:?}", + // as_error + // ); + for frame in as_error.chain() { + error!("{}", frame); + } + for handle in handles { + let _ = handle.join(); + } return; } @@ -156,6 +178,9 @@ fn main() { { if opts.dry_run { // don't send notification if it's a dry run, since no rendering occurred + for handle in handles { + let _ = handle.join(); + } return; } let notification = NotificationBuilder::new() @@ -170,4 +195,7 @@ fn main() { .delete() .expect("Failed to delete notification"); } + for handle in handles { + let _ = handle.join(); + } } diff --git a/src/bin/raymarch.rs b/src/bin/raymarch.rs index 85a6fc7..670cd74 100644 --- a/src/bin/raymarch.rs +++ b/src/bin/raymarch.rs @@ -435,7 +435,8 @@ fn main() { // ) let scene_file_path = config.scene_file.clone(); - let world = construct_world(&mut config, PathBuf::from(scene_file_path)).unwrap(); + let mut handles = Vec::new(); + let world = construct_world(&mut config, PathBuf::from(scene_file_path), &mut handles).unwrap(); let camera = world.cameras[0] .clone() @@ -500,7 +501,7 @@ fn main() { primitives: scene_sdf, environment: env_map, - materials: world.materials, + materials: (*world.materials).clone(), material_map, // max_depth: 20, // world aabb needs to encompass camera diff --git a/src/camera/projective_camera.rs b/src/camera/projective_camera.rs index faa0a73..5404c06 100644 --- a/src/camera/projective_camera.rs +++ b/src/camera/projective_camera.rs @@ -1,3 +1,6 @@ +use optics::aperture::ApertureSample; +use optics::ApertureEnum; + use crate::geometry::*; use crate::prelude::*; @@ -5,19 +8,19 @@ use crate::prelude::*; pub struct ProjectiveCamera { pub origin: Point3, pub direction: Vec3, - // half_height: f32, - // half_width: f32, - focal_distance: f32, - lower_left_corner: Point3, vfov: f32, + focal_distance: f32, // in meters pub lens: Instance, - pub horizontal: Vec3, - pub vertical: Vec3, + aspect_ratio: f32, u: Vec3, v: Vec3, w: Vec3, // TODO: change this to aperture from rust_optics crate - aperture_radius: f32, + pub aperture_diameter: f32, + pub aperture: ApertureEnum, + lower_left_corner: Point3, + vertical: Vec3, + horizontal: Vec3, } impl ProjectiveCamera { @@ -27,10 +30,11 @@ impl ProjectiveCamera { v_up: Vec3, vertical_fov: f32, // vertical_fov should be given in degrees, since it is converted to radians focal_distance: f32, - aperture: f32, + lens_diameter: f32, + aperture_diameter: f32, + aperture: ApertureEnum, ) -> ProjectiveCamera { let direction = (look_at - look_from).normalized(); - let lens_radius = aperture / 2.0; let theta: f32 = vertical_fov.to_radians(); let half_height = (theta / 2.0).tan(); let half_width = 1.0 * half_height; @@ -44,10 +48,10 @@ impl ProjectiveCamera { let w = -direction; let u = -v_up.cross(w).normalized(); let v = w.cross(u).normalized(); - // println!( - // "constructing camera with point, direction, and uvw = {:?} {:?} {:?} {:?} {:?}", - // look_from, direction, u, v, w - // ); + info!( + "constructing camera with point, direction, and uvw = {:?} {:?} {:?} {:?} {:?}", + look_from, direction, u, v, w + ); let transform = Transform3::from_stack( None, @@ -56,6 +60,7 @@ impl ProjectiveCamera { ) .inverse(); + let lens_radius = lens_diameter / 2.0; if lens_radius == 0.0 { warn!("Warn: lens radius is 0.0"); } @@ -63,26 +68,28 @@ impl ProjectiveCamera { ProjectiveCamera { origin: look_from, direction, - // half_height, - // half_width, - focal_distance, - lower_left_corner: look_from - - u * half_width * focal_distance - - v * half_height * focal_distance - - w * focal_distance, vfov: vertical_fov, + aspect_ratio, lens: Instance::new( Aggregate::from(Disk::new(lens_radius, Point3::ORIGIN, true)), Some(transform), + // use some placeholder ids, to be overwritten later when the world is constructed Some(MaterialId::Camera(0)), 0, ), - horizontal: u * 2.0 * half_width * focal_distance, - vertical: v * 2.0 * half_height * focal_distance, u, v, w, - aperture_radius: aperture / 2.0, + lower_left_corner: look_from + - u * half_width * focal_distance + - v * half_height * focal_distance + - w * focal_distance, + horizontal: u * 2.0 * half_width * focal_distance, + vertical: v * 2.0 * half_height * focal_distance, + + aperture_diameter, + aperture, + focal_distance, } } pub fn get_surface(&self) -> Option<&Instance> { @@ -91,9 +98,15 @@ impl ProjectiveCamera { } impl Camera for ProjectiveCamera { - fn get_ray(&self, sampler: &mut Box, _lambda: f32, u: f32, v: f32) -> (Ray, f32) { - // circular aperture/lens - let rd: Vec3 = self.aperture_radius * random_in_unit_disk(sampler.draw_2d()); + fn get_ray(&self, sampler: &mut Box, lambda: f32, u: f32, v: f32) -> (Ray, f32) { + let vec: Vec3 = loop { + if let Ok(p) = self.aperture.sample(sampler.draw_2d()) { + break p; + } + } + .into(); + let rd = self.aperture_diameter * vec; + let offset = self.u * rd.x() + self.v * rd.y(); let ray_origin: Point3 = self.origin + offset; @@ -195,6 +208,7 @@ unsafe impl Sync for ProjectiveCamera {} mod tests { use super::*; use math::prelude::*; + use optics::CircularAperture; #[test] fn test_camera() { @@ -205,6 +219,8 @@ mod tests { 35.2, 5.0, 0.08, + 0.08, + ApertureEnum::CircularAperture(CircularAperture::default()), ) .with_aspect_ratio(0.6); let s = debug_random(); @@ -231,6 +247,8 @@ mod tests { 35.2, 5.0, 0.08, + 0.08, + ApertureEnum::CircularAperture(CircularAperture::default()), ) .with_aspect_ratio(width as f32 / height as f32); let px = (0.99 * width) as usize; @@ -265,6 +283,8 @@ mod tests { 27.0, 5.0, 0.08, + 0.08, + ApertureEnum::CircularAperture(CircularAperture::default()), ) .with_aspect_ratio(0.6); diff --git a/src/camera/realistic_camera.rs b/src/camera/realistic_camera.rs index bed6d07..940d96d 100644 --- a/src/camera/realistic_camera.rs +++ b/src/camera/realistic_camera.rs @@ -164,7 +164,7 @@ impl Camera for RealisticCamera { self.lens_zoom, Input::new(ray, lambda / 1000.0), 1.0, - |e| (self.aperture.intersects(self.aperture_radius, e), false), + |e| (self.aperture.is_rejected(self.aperture_radius, e.origin), false), drop, ); if let Some(Output { diff --git a/src/materials/lambertian.rs b/src/materials/lambertian.rs index 367fb65..5735ff2 100644 --- a/src/materials/lambertian.rs +++ b/src/materials/lambertian.rs @@ -1,6 +1,6 @@ use crate::{prelude::*, texture::EvalAt}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Lambertian { pub texture: TexStack, } diff --git a/src/materials/mod.rs b/src/materials/mod.rs index ab7e9cc..ac9575d 100644 --- a/src/materials/mod.rs +++ b/src/materials/mod.rs @@ -1,6 +1,9 @@ use crate::prelude::*; -use std::marker::{Send, Sync}; +use std::{ + marker::{Send, Sync}, + ops::{Deref, DerefMut}, +}; mod diffuse_light; mod ggx; @@ -236,7 +239,7 @@ macro_rules! generate_enum { } }; ( $name:ident, $( $s:ident),+) => { - #[derive(Clone)] + #[derive(Clone, Debug)] pub enum $name { $( $s($s), @@ -290,7 +293,19 @@ generate_enum!( // PassthroughFilter // ); -pub type MaterialTable = Vec; +#[derive(Debug, Clone)] +pub struct MaterialTable(pub Vec); +impl Deref for MaterialTable { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for MaterialTable { + fn deref_mut(&mut self) -> &mut Vec { + &mut self.0 + } +} // #[cfg(test)] // mod tests { diff --git a/src/materials/passthrough.rs b/src/materials/passthrough.rs index d431d15..da7f827 100644 --- a/src/materials/passthrough.rs +++ b/src/materials/passthrough.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct PassthroughFilter { pub color: Curve, pub outer_medium_id: MediumId, diff --git a/src/mediums/hg.rs b/src/mediums/hg.rs index e4ba1a9..962d7dc 100644 --- a/src/mediums/hg.rs +++ b/src/mediums/hg.rs @@ -13,7 +13,7 @@ pub fn phase_hg(cos_theta: f32, g: f32) -> f32 { ); (1.0 - g * g) / (denom * denom.sqrt() * 2.0 * std::f32::consts::TAU) } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct HenyeyGreensteinHomogeneous { // domain: visible range // range: 0..2 diff --git a/src/mediums/mod.rs b/src/mediums/mod.rs index 2b6844e..543e60f 100644 --- a/src/mediums/mod.rs +++ b/src/mediums/mod.rs @@ -1,6 +1,7 @@ use crate::prelude::*; use std::marker::{Send, Sync}; +use std::ops::{Deref, DerefMut}; mod hg; mod rayleigh; @@ -50,7 +51,7 @@ pub trait Medium { macro_rules! generate_medium_enum { ( $name:ident, $l: ty, $e: ty, $( $s:ident),+) => { - #[derive(Clone)] + #[derive(Clone, Debug)] pub enum $name { $( $s($s), @@ -110,4 +111,16 @@ generate_medium_enum! {MediumEnum, f32, f32, HenyeyGreensteinHomogeneous, Raylei unsafe impl Send for MediumEnum {} unsafe impl Sync for MediumEnum {} -pub type MediumTable = Vec; +#[derive(Debug, Clone)] +pub struct MediumTable(pub Vec); +impl Deref for MediumTable { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl DerefMut for MediumTable { + fn deref_mut(&mut self) -> &mut Vec { + &mut self.0 + } +} diff --git a/src/mediums/rayleigh.rs b/src/mediums/rayleigh.rs index 9912597..f0d1d22 100644 --- a/src/mediums/rayleigh.rs +++ b/src/mediums/rayleigh.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use super::Medium; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Rayleigh { pub corrective_factor: f32, pub ior: Curve, diff --git a/src/parsing/cameras.rs b/src/parsing/cameras.rs index 722cd3b..c78bf5c 100644 --- a/src/parsing/cameras.rs +++ b/src/parsing/cameras.rs @@ -73,11 +73,11 @@ pub struct SimpleCameraData { pub v_up: Option<[f32; 3]>, pub vfov: f32, pub focal_distance: Option, - pub aperture_size: Option, // in meters - #[cfg(feature = "realistic_camera")] + pub aperture_diameter: Option, // in meters + pub lens_diameter: Option, // in meters pub aperture: Option, // defaults to Circular - // pub shutter_open_time: Option, - // pub shutter_close_time: Option, + // pub shutter_open_time: Option, + // pub shutter_close_time: Option, } #[derive(Serialize, Deserialize, Clone)] @@ -136,9 +136,10 @@ pub fn parse_cameras(config: &mut Config, camera_data: Vec) -> Vec, pub resolution: Resolution, @@ -59,6 +62,7 @@ pub struct RenderSettings { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct TOMLRenderSettings { pub filename: Option, pub resolution: Resolution, @@ -104,6 +108,7 @@ impl From for RenderSettings { #[derive(Serialize, Deserialize, Copy, Clone)] #[serde(tag = "type")] +#[serde(deny_unknown_fields)] pub enum RendererType { Naive, #[cfg(feature = "preview")] @@ -116,6 +121,7 @@ pub enum RendererType { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct TOMLConfig { pub env_sampling_probability: Option, //defaults to 0.5 pub default_scene_file: String, diff --git a/src/parsing/curves.rs b/src/parsing/curves.rs index af744cb..fc11e62 100644 --- a/src/parsing/curves.rs +++ b/src/parsing/curves.rs @@ -12,6 +12,7 @@ use std::path::Path; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(deny_unknown_fields)] pub struct DomainMapping { pub x_offset: Option, pub x_scale: Option, @@ -41,6 +42,7 @@ impl Default for DomainMapping { #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(tag = "type")] +#[serde(deny_unknown_fields)] pub enum CurveData { Blackbody { temperature: f32, diff --git a/src/parsing/environment.rs b/src/parsing/environment.rs index be33209..7c7a324 100644 --- a/src/parsing/environment.rs +++ b/src/parsing/environment.rs @@ -3,6 +3,7 @@ use crate::prelude::*; use std::collections::HashMap; use std::hash::{DefaultHasher, Hash, Hasher}; use std::path::PathBuf; +use std::thread::JoinHandle; use math::curves::InterpolationMode; use math::spectral::BOUNDED_VISIBLE_RANGE; @@ -16,12 +17,14 @@ use super::instance::{AxisAngleData, Transform3Data}; use super::{CurveDataOrReference, Vec3Data}; #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct ConstantData { pub color: CurveDataOrReference, pub strength: f32, } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct SunData { pub color: CurveDataOrReference, pub strength: f32, @@ -30,6 +33,7 @@ pub struct SunData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct ImportanceMapData { pub width: usize, pub height: usize, @@ -38,6 +42,7 @@ pub struct ImportanceMapData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct HDRIData { pub texture_name: String, pub strength: f32, @@ -58,6 +63,7 @@ pub fn parse_environment( curves: &HashMap, textures: &HashMap, error_color: &Curve, + mut handles: &mut Vec>, ) -> anyhow::Result { match env_data { EnvironmentData::Constant(data) => Ok(EnvironmentMap::Constant { @@ -92,6 +98,7 @@ pub fn parse_environment( translate: None, } .into(); + let texture_name = &data.texture_name; let texture = textures .get(&data.texture_name) .cloned() @@ -123,6 +130,8 @@ pub fn parse_environment( path.push("importance_maps"); let mut hasher = DefaultHasher::new(); + // hash texture name and luminance curve, so if either change we can detect that and rebake + texture_name.hash(&mut hasher); data.luminance_curve.hash(&mut hasher); let curve_hash = hasher.finish(); path.push(format!( @@ -140,7 +149,7 @@ pub fn parse_environment( let is_baked = unbaked.bake_in_place(&texture, BOUNDED_VISIBLE_RANGE); assert!(is_baked); let baked = unbaked; - let res = baked.save_baked(path.clone()); + let res = baked.save_baked(path.clone(), &mut handles); if res.is_err() { let e = res.unwrap_err(); error!( diff --git a/src/parsing/instance.rs b/src/parsing/instance.rs index 2da302f..167598c 100644 --- a/src/parsing/instance.rs +++ b/src/parsing/instance.rs @@ -21,12 +21,21 @@ pub struct AxisAngleData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct Transform3Data { pub scale: Option, // scale pub rotate: Option>, // axis angle pub translate: Option, // translation } +#[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] +pub struct InstanceData { + pub aggregate: AggregateData, + pub transform: Option, + pub material_name: Option, +} + impl From for Transform3 { fn from(data: Transform3Data) -> Self { info!("parsing transform data"); @@ -60,13 +69,6 @@ impl From for Transform3 { } } -#[derive(Serialize, Deserialize, Clone)] -pub struct InstanceData { - pub aggregate: AggregateData, - pub transform: Option, - pub material_name: Option, -} - pub fn parse_instance( instance_data: InstanceData, materials_mapping: &HashMap, diff --git a/src/parsing/material.rs b/src/parsing/material.rs index 6b844f5..e589b29 100644 --- a/src/parsing/material.rs +++ b/src/parsing/material.rs @@ -11,11 +11,13 @@ use std::collections::HashMap; use super::CurveDataOrReference; #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct LambertianData { pub texture_id: String, } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct GGXData { pub alpha: f32, pub eta: CurveDataOrReference, @@ -27,6 +29,7 @@ pub struct GGXData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct DiffuseLightData { pub bounce_color: CurveDataOrReference, pub emit_color: CurveDataOrReference, @@ -34,6 +37,7 @@ pub struct DiffuseLightData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct SharpLightData { pub bounce_color: CurveDataOrReference, pub emit_color: CurveDataOrReference, @@ -50,6 +54,7 @@ pub struct SharpLightData { #[derive(Serialize, Deserialize, Clone)] #[serde(tag = "type")] +#[serde(deny_unknown_fields)] pub enum MaterialData { GGX(GGXData), Lambertian(LambertianData), diff --git a/src/parsing/medium.rs b/src/parsing/medium.rs index 03801a9..df46a30 100644 --- a/src/parsing/medium.rs +++ b/src/parsing/medium.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use super::CurveDataOrReference; #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct HGMediumData { pub g: CurveDataOrReference, pub sigma_s: CurveDataOrReference, @@ -15,12 +16,14 @@ pub struct HGMediumData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct RayleighData { pub ior: CurveDataOrReference, pub corrective_factor: f32, } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] #[serde(tag = "type")] pub enum MediumData { HG(HGMediumData), diff --git a/src/parsing/meshes.rs b/src/parsing/meshes.rs index 8487fc5..6fa804a 100644 --- a/src/parsing/meshes.rs +++ b/src/parsing/meshes.rs @@ -150,6 +150,7 @@ pub fn parse_obj_mesh( } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct MeshData { pub filename: String, pub mesh_index: Option, diff --git a/src/parsing/mod.rs b/src/parsing/mod.rs index e154a4f..1908ce5 100644 --- a/src/parsing/mod.rs +++ b/src/parsing/mod.rs @@ -1,4 +1,6 @@ +use crate::materials::MaterialTable; use crate::mediums::MediumEnum; +use crate::mediums::MediumTable; use crate::prelude::*; use crate::accelerator::AcceleratorType; @@ -9,13 +11,13 @@ use crate::world::World; use std::collections::HashMap; use std::collections::HashSet; -use std::error::Error; use std::fs; use std::fs::File; use std::io::Read; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; +use std::thread::JoinHandle; pub mod cameras; pub mod config; @@ -52,22 +54,6 @@ use self::primitives::{AggregateData, MeshRef}; pub type Vec3Data = [f32; 3]; pub type Point3Data = [f32; 3]; -macro_rules! unpack_or_LaP { - ($e:expr, $($message: tt)*) => { - match $e { - Ok(inner) => inner, - Err(err) => { - error!($($message)*); - error!("{:?}", err.to_string()); - // if let Some(backtrace) = err.backtrace() { - // error!("{:?}", backtrace); - // } - panic!() - } - } - }; -} - macro_rules! generate_maybe_libs { ($($e:ident),+) => { $( @@ -85,8 +71,7 @@ macro_rules! generate_maybe_libs { match self { Self::Literal(data) => data, Self::Path(path) => - unpack_or_LaP!(load_arbitrary(PathBuf::from(path.clone())), "failed to resolve path {}", path) - + load_arbitrary(PathBuf::from(path.clone())).context(format!("failed to resolve path {}", path)).unwrap() } } } @@ -103,6 +88,7 @@ generate_maybe_libs! {Curve, TextureStack, Material, Mesh, Medium} // verify how much arcs mess with performance compared to unsafe raw ptr access #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct SceneData { pub env_sampling_probability: Option, pub environment: EnvironmentData, @@ -115,7 +101,7 @@ pub struct SceneData { pub cameras: Vec, } -pub fn load_arbitrary, O>(filepath: T) -> Result> +pub fn load_arbitrary, O>(filepath: T) -> anyhow::Result where O: DeserializeOwned, { @@ -148,6 +134,7 @@ pub fn load_scene>(filepath: T) -> anyhow::Result { pub fn construct_world>( mut config: &mut Config, scene_file: P, + mut handles: &mut Vec>, ) -> anyhow::Result { // layout of this function: // parse scene data from file @@ -164,7 +151,7 @@ pub fn construct_world>( // parse scene from disk - let scene = unpack_or_LaP!(load_scene(scene_file), "failed to resolve scene file"); + let scene = load_scene(scene_file).context("failed to resolve scene file")?; // collect information from instances, materials, and env map data to determine what textures and materials actually need to be parsed and what can be discarded. @@ -379,8 +366,14 @@ pub fn construct_world>( } // parse enviroment - let environment = parse_environment(scene.environment, &curves, &textures_map, &mauve) - .context("failed to parse environment")?; + let environment = parse_environment( + scene.environment, + &curves, + &textures_map, + &mauve, + &mut handles, + ) + .context("failed to parse environment")?; // parse mediums from disk or from literal let mut mediums_map: HashMap = HashMap::new(); @@ -548,8 +541,8 @@ pub fn construct_world>( let world = World::new( instances, - materials, - mediums, + MaterialTable(materials), + MediumTable(mediums), environment, cameras, scene.env_sampling_probability.unwrap_or(0.5), @@ -627,13 +620,15 @@ mod test { #[test] fn test_parsing_complex_scene() { let mut default_config = Config::load_default(); + let mut handles = Vec::new(); let world = construct_world( &mut default_config, PathBuf::from("data/scenes/hdri_test_2.toml"), + &mut handles, ) .unwrap(); println!("constructed world"); - for mat in &world.materials { + for mat in &*world.materials { let name = match mat { MaterialEnum::DiffuseLight(_) => "diffuse_light", MaterialEnum::Lambertian(_) => "lambertian", @@ -648,9 +643,11 @@ mod test { #[test] fn test_world() { let mut default_config = Config::load_default(); + let mut handles = Vec::new(); let _world = construct_world( &mut default_config, PathBuf::from("data/scenes/test_prism.toml"), + &mut handles, ) .unwrap(); } diff --git a/src/parsing/primitives.rs b/src/parsing/primitives.rs index af089f6..99bf6cd 100644 --- a/src/parsing/primitives.rs +++ b/src/parsing/primitives.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; #[derive(Serialize, Deserialize, Copy, Clone)] +#[serde(deny_unknown_fields)] pub struct DiskData { pub radius: f32, pub origin: Point3Data, @@ -15,12 +16,14 @@ pub struct DiskData { } #[derive(Serialize, Deserialize, Copy, Clone)] +#[serde(deny_unknown_fields)] pub struct SphereData { pub radius: f32, pub origin: Point3Data, } #[derive(Serialize, Deserialize, Copy, Clone)] +#[serde(deny_unknown_fields)] pub struct RectData { pub size: (f32, f32), pub normal: Axis, @@ -29,6 +32,7 @@ pub struct RectData { } #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] pub struct MeshRef { pub name: String, pub index: Option, diff --git a/src/parsing/texture.rs b/src/parsing/texture.rs index 21c69d1..9b133f4 100644 --- a/src/parsing/texture.rs +++ b/src/parsing/texture.rs @@ -18,6 +18,7 @@ use std::path::Path; use super::CurveDataOrReference; #[derive(Serialize, Deserialize, Clone)] +#[serde(deny_unknown_fields)] #[serde(tag = "type")] pub enum TextureData { Texture1 { diff --git a/src/parsing/tonemap.rs b/src/parsing/tonemap.rs index 1a94d9f..73f1ea5 100644 --- a/src/parsing/tonemap.rs +++ b/src/parsing/tonemap.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::tonemap::{Clamp, Reinhard0, Reinhard0x3, Reinhard1, Reinhard1x3, Tonemapper}; #[derive(Serialize, Deserialize, Clone, Copy)] +#[serde(deny_unknown_fields)] #[serde(tag = "type")] pub enum TonemapSettings { // clamp all colors to 0 to 1, multiplying by 10^exposure beforehand (exposure defaults to 0, not changing anything) diff --git a/src/texture.rs b/src/texture.rs index 059f241..5ae9838 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -28,7 +28,7 @@ where fn eval_at(&self, lambda: T, uv: UV) -> T; } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Texture4 { pub curves: [CurveWithCDF; 4], pub texture: Vec2D, @@ -115,7 +115,7 @@ impl EvalAt for Texture4 { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Texture1 { pub curve: CurveWithCDF, pub texture: Vec2D, @@ -151,7 +151,7 @@ impl EvalAt for Texture1 { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Texture { Texture1(Texture1), Texture4(Texture4), @@ -200,7 +200,7 @@ pub fn replace_channel(tex4: &mut Texture4, tex1: Texture1, channel: u8) { tex4.curves[channel] = tex1.curve; } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct TexStack { pub textures: Vec, } diff --git a/src/vec2d.rs b/src/vec2d.rs index d6a1fc3..010f8c0 100644 --- a/src/vec2d.rs +++ b/src/vec2d.rs @@ -13,7 +13,7 @@ impl From for (f32, f32) { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Vec2D { pub buffer: Vec, pub width: usize, diff --git a/src/world/environment.rs b/src/world/environment.rs index 667b270..b9b18fb 100644 --- a/src/world/environment.rs +++ b/src/world/environment.rs @@ -3,7 +3,7 @@ use crate::texture::EvalAt; use crate::world::importance_map::ImportanceMap; use std::ops::Mul; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum EnvironmentMap { Constant { color: CurveWithCDF, diff --git a/src/world/importance_map.rs b/src/world/importance_map.rs index 5e6cc19..70ce26f 100644 --- a/src/world/importance_map.rs +++ b/src/world/importance_map.rs @@ -3,6 +3,7 @@ use std::{ /* io::{Read, Write}, */ path::{Path, PathBuf /* , PathBuf */}, sync::Arc, + thread::JoinHandle, }; use anyhow::{bail, Context}; @@ -24,7 +25,7 @@ use rayon::iter::ParallelIterator; // would change memory usage complexity from O(n*m) to O(k*n*m) where k is the number of channels in the texstack // which is 4 for every tex4, 1 for every tex1, etc. -#[derive(Clone, Deserialize, Serialize, DeepSizeOf)] +#[derive(Clone, Deserialize, Serialize, DeepSizeOf, Debug)] pub enum ImportanceMap { Baked { @@ -248,7 +249,11 @@ impl ImportanceMap { luminance_curve, } } - pub fn save_baked(&self, filepath: PathBuf) -> anyhow::Result<()> { + pub fn save_baked( + &self, + filepath: PathBuf, + handles: &mut Vec>, + ) -> anyhow::Result<()> { match &self { ImportanceMap::Baked { .. } => { let filepath_as_str = filepath.file_name().unwrap().to_string_lossy(); @@ -261,7 +266,7 @@ impl ImportanceMap { let clone = self.clone(); // intentionally not joining, the thread should finish or panic eventually. - let _join_handle = std::thread::spawn(move || { + let join_handle = std::thread::spawn(move || { let timestamp = std::time::Instant::now(); let Ok(file) = File::create(&cloned_filepath).context(format!( "failed to create file {:?}", @@ -279,6 +284,7 @@ impl ImportanceMap { timestamp.elapsed().as_millis() as f32 / 1000.0 ); }); + handles.push(join_handle); Ok(()) } @@ -479,9 +485,11 @@ mod test { #[test] fn test_env_importance_sampling() { let mut default_config = Config::load_default(); + let mut handles = Vec::new(); let mut world = construct_world( &mut default_config, PathBuf::from("data/scenes/hdri_test_2.toml"), + &mut handles, ) .unwrap(); @@ -636,9 +644,11 @@ mod test { #[test] fn test_env_direct_access() { let mut default_config = Config::load_default(); + let mut handles = Vec::new(); let world = construct_world( &mut default_config, PathBuf::from("data/scenes/hdri_test_2.toml"), + &mut handles, ) .unwrap(); let env = &world.environment; @@ -733,9 +743,12 @@ mod test { #[test] fn test_env_scene_ray_sampling() { let mut default_config = Config::load_default(); + + let mut handles = Vec::new(); let mut world = construct_world( &mut default_config, PathBuf::from("data/scenes/hdri_test_2.toml"), + &mut handles, ) .unwrap(); diff --git a/src/world/mod.rs b/src/world/mod.rs index 9c1acb8..e823940 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -14,7 +14,7 @@ pub use crate::accelerator::{Accelerator, AcceleratorType}; pub use crate::geometry::*; pub use crate::materials::*; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct World { pub accelerator: Accelerator, pub lights: Vec, @@ -268,9 +268,11 @@ mod test { fn test_world_intersection() { crate::log_test_setup(); let mut default_config = Config::load_default(); + let mut handles = Vec::new(); let world = construct_world( &mut default_config, PathBuf::from("data/scenes/test_lighting_north.toml"), + &mut handles, ) .unwrap();