Cleanup of shared codebase to no_std and spirv compatibility
This commit is contained in:
parent
b36105edc1
commit
7ebed27d4a
100 changed files with 1648 additions and 1104 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,6 +5,7 @@ target/
|
||||||
flip.rs
|
flip.rs
|
||||||
.vscode
|
.vscode
|
||||||
rust-analyzer.json
|
rust-analyzer.json
|
||||||
|
rust-analyzer.toml
|
||||||
data/
|
data/
|
||||||
src/gpu/
|
src/gpu/
|
||||||
src/tests/
|
src/tests/
|
||||||
|
|
|
||||||
33
Cargo.toml
33
Cargo.toml
|
|
@ -6,11 +6,12 @@ edition = "2024"
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
use_f64 = []
|
use_f64 = []
|
||||||
use_gpu = []
|
use_gpu = ["dep:wgpu"]
|
||||||
use_nvtx = []
|
use_nvtx = ["dep:nvtx"]
|
||||||
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw"]
|
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw", "dep:cuda-runtime-sys"]
|
||||||
vulkan = ["ash", "gpu-allocator"]
|
vulkan = ["ash", "gpu-allocator"]
|
||||||
ash = ["dep:ash"]
|
ash = ["dep:ash"]
|
||||||
|
gpu-allocator = ["dep:gpu-allocator"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.100"
|
anyhow = "1.0.100"
|
||||||
|
|
@ -22,7 +23,6 @@ indicatif = "0.18.3"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
memmap2 = "0.9.9"
|
memmap2 = "0.9.9"
|
||||||
nvtx = "1.3.0"
|
|
||||||
parking_lot = "0.12.5"
|
parking_lot = "0.12.5"
|
||||||
paste = "1.0.15"
|
paste = "1.0.15"
|
||||||
qoi = "0.4.1"
|
qoi = "0.4.1"
|
||||||
|
|
@ -30,16 +30,6 @@ rand = "0.9.2"
|
||||||
rayon = "1.11.0"
|
rayon = "1.11.0"
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
unicode-normalization = "0.1.25"
|
unicode-normalization = "0.1.25"
|
||||||
wgpu = "27.0.1"
|
|
||||||
|
|
||||||
shared = { path = "shared" }
|
|
||||||
ptex-filter = { path = "crates/ptex-filter" }
|
|
||||||
# kernels = { path = "kernels" }
|
|
||||||
ash = { version = "0.38", optional = true }
|
|
||||||
gpu-allocator = { version = "0.28", features = ["vulkan"], optional = true }
|
|
||||||
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
|
||||||
cust = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, features = ["glam"], optional = true }
|
|
||||||
cust_raw = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
|
||||||
ptex = "0.3.0"
|
ptex = "0.3.0"
|
||||||
slice = "0.0.4"
|
slice = "0.0.4"
|
||||||
crossbeam-channel = "0.5.15"
|
crossbeam-channel = "0.5.15"
|
||||||
|
|
@ -49,7 +39,18 @@ enum_dispatch = "0.3.13"
|
||||||
bytemuck = "1.24.0"
|
bytemuck = "1.24.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
smallvec = "1.15.1"
|
smallvec = "1.15.1"
|
||||||
cuda-runtime-sys = "0.3.0-alpha.1"
|
|
||||||
|
shared = { path = "shared" }
|
||||||
|
ptex-filter = { path = "crates/ptex-filter" }
|
||||||
|
# kernels = { path = "kernels" }
|
||||||
|
nvtx = { version = "1.3.0", optional = true }
|
||||||
|
wgpu = { version = "27.0.1", optional = true }
|
||||||
|
ash = { version = "0.38", optional = true }
|
||||||
|
gpu-allocator = { version = "0.28", features = ["vulkan"], optional = true }
|
||||||
|
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
||||||
|
cust = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, features = ["glam"], optional = true }
|
||||||
|
cust_raw = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
||||||
|
cuda-runtime-sys = { version = "0.3.0-alpha.1", optional = true}
|
||||||
cudarc = { version = "0.18.2", features = ["cuda-13000"], optional = true }
|
cudarc = { version = "0.18.2", features = ["cuda-13000"], optional = true }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
@ -59,7 +60,7 @@ cc = "1.2.53"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["shared"]
|
members = ["shared"]
|
||||||
exclude = ["crates/ptex-filter"]
|
exclude = ["crates/ptex-filter", "kernels"]
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
excessive_precision = "allow"
|
excessive_precision = "allow"
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
[package]
|
[package]
|
||||||
name = "kernels"
|
name = "kernels"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
cuda_std = { git = "https://github.com/rust-gpu/rust-cuda", rev = "7fa76f3d717038a92c90bf4a482b0b8dd3259344" }
|
|
||||||
shared = { path = "../shared", features = ["cuda"] }
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["dylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
spirv-std = { git = "https://github.com/Rust-GPU/rust-gpu", branch = "main" }
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
|
||||||
|
[package.metadata.rust-gpu.install]
|
||||||
|
spirv-builder-source = "https://github.com/Rust-GPU/rust-gpu"
|
||||||
|
|
||||||
|
[package.metadata.rust-gpu.build]
|
||||||
|
target = "spirv-unknown-vulkan1.2"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,20 @@
|
||||||
#![cfg_attr(target_arch = "nvptx64", no_std)]
|
#![cfg_attr(target_arch = "spirv", no_std)]
|
||||||
#![cfg_attr(target_arch = "nvptx64", feature(abi_ptx))]
|
|
||||||
|
|
||||||
use cuda_std::prelude::*;
|
use spirv_std::spirv;
|
||||||
|
|
||||||
/// Scales each element: data[i] *= scale
|
/// Core logic — testable on CPU
|
||||||
#[kernel]
|
pub fn scale_kernel_logic(idx: usize, input: &[f32], output: &mut [f32], scale: f32) {
|
||||||
#[allow(improper_ctypes_definitions)]
|
if idx < input.len() {
|
||||||
pub unsafe fn scale_array(data: *mut f32, len: u32, scale: f32) {
|
output[idx] = input[idx] * scale;
|
||||||
let idx = thread::index_1d() as u32;
|
|
||||||
if idx >= len {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let ptr = unsafe { data.add(idx as usize) };
|
|
||||||
*ptr = *ptr * scale;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds two arrays: c[i] = a[i] + b[i]
|
#[spirv(compute(threads(64)))]
|
||||||
#[kernel]
|
pub fn scale_kernel(
|
||||||
#[allow(improper_ctypes_definitions)]
|
#[spirv(global_invocation_id)] id: spirv_std::glam::UVec3,
|
||||||
pub unsafe fn add_arrays(a: *const f32, b: *const f32, c: *mut f32, len: u32) {
|
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] input: &[f32],
|
||||||
let idx = thread::index_1d() as u32;
|
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] output: &mut [f32],
|
||||||
if idx >= len {
|
#[spirv(push_constant)] scale: &f32,
|
||||||
return;
|
) {
|
||||||
}
|
scale_kernel_logic(id.x as usize, input, output, *scale);
|
||||||
|
|
||||||
let i = idx as usize;
|
|
||||||
*c.add(i) = *a.add(i) + *b.add(i);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,12 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.10.0"
|
bitflags = "2.10.0"
|
||||||
bumpalo = "3.19.1"
|
|
||||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||||
enum_dispatch = "0.3.13"
|
enum_dispatch = "0.3.13"
|
||||||
log = "0.4.29"
|
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
|
||||||
num = "0.4.3"
|
|
||||||
num-integer = "0.1.46"
|
|
||||||
num-traits = "0.2.19"
|
|
||||||
once_cell = "1.21.3"
|
|
||||||
smallvec = "1.15.1"
|
|
||||||
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
|
||||||
half = "2.7.1"
|
|
||||||
rand = "0.9.2"
|
|
||||||
anyhow = "1.0.100"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
use_f64 = []
|
use_f64 = []
|
||||||
cuda = ["cuda_std"]
|
cuda = ["cuda_std"]
|
||||||
|
cpu_debug = []
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use crate::utils::sampling::{
|
||||||
};
|
};
|
||||||
use crate::{Float, INV_2_PI, INV_PI, PI};
|
use crate::{Float, INV_2_PI, INV_PI, PI};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
static P_MAX: usize = 3;
|
static P_MAX: usize = 3;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -81,7 +82,7 @@ impl HairBxDF {
|
||||||
let ap0 = SampledSpectrum::new(f);
|
let ap0 = SampledSpectrum::new(f);
|
||||||
let ap1 = t * (1.0 - f).powi(2);
|
let ap1 = t * (1.0 - f).powi(2);
|
||||||
let tf = t * f;
|
let tf = t * f;
|
||||||
std::array::from_fn(|p| match p {
|
core::array::from_fn(|p| match p {
|
||||||
0 => ap0,
|
0 => ap0,
|
||||||
1 => ap1,
|
1 => ap1,
|
||||||
_ if p < P_MAX => ap1 * tf.pow_int(p - 1),
|
_ if p < P_MAX => ap1 * tf.pow_int(p - 1),
|
||||||
|
|
@ -133,7 +134,7 @@ impl HairBxDF {
|
||||||
let t = t_value.exp();
|
let t = t_value.exp();
|
||||||
let ap = Self::ap(cos_theta_o, self.eta, self.h, t);
|
let ap = Self::ap(cos_theta_o, self.eta, self.h, t);
|
||||||
let sum_y: Float = ap.iter().map(|s| s.average()).sum();
|
let sum_y: Float = ap.iter().map(|s| s.average()).sum();
|
||||||
std::array::from_fn(|i| ap[i].average() / sum_y)
|
core::array::from_fn(|i| ap[i].average() / sum_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigma_a_from_concentration(
|
pub fn sigma_a_from_concentration(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::spectra::SampledSpectrum;
|
||||||
use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
||||||
use crate::{Float, INV_PI};
|
use crate::{Float, INV_PI};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use crate::utils::math::square;
|
||||||
use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
||||||
use crate::{Float, INV_PI};
|
use crate::{Float, INV_PI};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use crate::core::geometry::{
|
||||||
spherical_direction, spherical_theta,
|
spherical_direction, spherical_theta,
|
||||||
};
|
};
|
||||||
use crate::core::medium::{HGPhaseFunction, PhaseFunctionTrait};
|
use crate::core::medium::{HGPhaseFunction, PhaseFunctionTrait};
|
||||||
use crate::core::options::get_options;
|
|
||||||
use crate::core::scattering::{
|
use crate::core::scattering::{
|
||||||
TrowbridgeReitzDistribution, fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect,
|
TrowbridgeReitzDistribution, fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect,
|
||||||
refract,
|
refract,
|
||||||
|
|
@ -30,6 +29,7 @@ use crate::utils::sampling::{
|
||||||
};
|
};
|
||||||
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2};
|
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum TopOrBottom<'a, T, B> {
|
pub enum TopOrBottom<'a, T, B> {
|
||||||
|
|
@ -91,6 +91,7 @@ where
|
||||||
albedo: SampledSpectrum,
|
albedo: SampledSpectrum,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
|
seed: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, B, const TWO_SIDED: bool> LayeredBxDF<T, B, TWO_SIDED>
|
impl<T, B, const TWO_SIDED: bool> LayeredBxDF<T, B, TWO_SIDED>
|
||||||
|
|
@ -106,6 +107,7 @@ where
|
||||||
g: Float,
|
g: Float,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
|
seed: i32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
top,
|
top,
|
||||||
|
|
@ -115,6 +117,7 @@ where
|
||||||
albedo,
|
albedo,
|
||||||
max_depth,
|
max_depth,
|
||||||
n_samples,
|
n_samples,
|
||||||
|
seed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -369,7 +372,7 @@ where
|
||||||
f = self.n_samples as Float * enter_interface.f(wo, wi, mode);
|
f = self.n_samples as Float * enter_interface.f(wo, wi, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash0 = hash_buffer(&[get_options().seed as Float, wo.x(), wo.y(), wo.z()], 0);
|
let hash0 = hash_buffer(&[self.seed as Float, wo.x(), wo.y(), wo.z()], 0);
|
||||||
let hash1 = hash_buffer(&[wi.x(), wi.y(), wi.z()], 0);
|
let hash1 = hash_buffer(&[wi.x(), wi.y(), wi.z()], 0);
|
||||||
let mut rng = Rng::new_with_offset(hash0, hash1);
|
let mut rng = Rng::new_with_offset(hash0, hash1);
|
||||||
|
|
||||||
|
|
@ -415,7 +418,7 @@ where
|
||||||
let mut specular_path = bs.is_specular();
|
let mut specular_path = bs.is_specular();
|
||||||
|
|
||||||
// Declare RNG for layered BSDF sampling
|
// Declare RNG for layered BSDF sampling
|
||||||
let hash0 = hash_buffer(&[get_options().seed as Float, wo.x(), wo.y(), wo.z()], 0);
|
let hash0 = hash_buffer(&[self.seed as Float, wo.x(), wo.y(), wo.z()], 0);
|
||||||
let hash1 = hash_buffer(&[uc, u.x(), u.y()], 0);
|
let hash1 = hash_buffer(&[uc, u.x(), u.y()], 0);
|
||||||
let mut rng = Rng::new_with_offset(hash0, hash1);
|
let mut rng = Rng::new_with_offset(hash0, hash1);
|
||||||
|
|
||||||
|
|
@ -517,7 +520,7 @@ where
|
||||||
wi = -wi;
|
wi = -wi;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hash0 = hash_buffer(&[get_options().seed as Float, wi.x(), wi.y(), wi.z()], 0);
|
let hash0 = hash_buffer(&[self.seed as Float, wi.x(), wi.y(), wi.z()], 0);
|
||||||
let hash1 = hash_buffer(&[wo.x(), wo.y(), wo.z()], 0);
|
let hash1 = hash_buffer(&[wo.x(), wo.y(), wo.z()], 0);
|
||||||
let mut rng = Rng::new_with_offset(hash0, hash1);
|
let mut rng = Rng::new_with_offset(hash0, hash1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::sampling::{PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
use crate::utils::sampling::{PiecewiseLinear2D, cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
||||||
use crate::{Float, INV_PI, PI, PI_OVER_2};
|
use crate::{Float, INV_PI, PI, PI_OVER_2};
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use crate::core::scattering::refract;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::{lerp, quadratic, square};
|
use crate::utils::math::{lerp, quadratic, square};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
@ -35,87 +36,17 @@ const EXIT_PUPIL_SAMPLES: usize = 64;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct RealisticCamera {
|
pub struct RealisticCamera {
|
||||||
base: CameraBase,
|
pub base: CameraBase,
|
||||||
focus_distance: Float,
|
pub focus_distance: Float,
|
||||||
set_aperture_diameter: Float,
|
pub set_aperture_diameter: Float,
|
||||||
aperture_image: Ptr<DeviceImage>,
|
pub aperture_image: Ptr<DeviceImage>,
|
||||||
element_interfaces: Ptr<LensElementInterface>,
|
pub element_interfaces: Ptr<LensElementInterface>,
|
||||||
n_elements: usize,
|
pub n_elements: usize,
|
||||||
physical_extent: Bounds2f,
|
pub physical_extent: Bounds2f,
|
||||||
exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
|
pub exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
impl RealisticCamera {
|
impl RealisticCamera {
|
||||||
pub fn new(
|
|
||||||
base: CameraBase,
|
|
||||||
lens_params: &[Float],
|
|
||||||
focus_distance: Float,
|
|
||||||
set_aperture_diameter: Float,
|
|
||||||
aperture_image: Ptr<DeviceImage>,
|
|
||||||
) -> Self {
|
|
||||||
let film_ptr = base.film;
|
|
||||||
if film_ptr.is_null() {
|
|
||||||
panic!("Camera must have a film");
|
|
||||||
}
|
|
||||||
let film = &*film_ptr;
|
|
||||||
|
|
||||||
let aspect = film.full_resolution().x() as Float / film.full_resolution().y() as Float;
|
|
||||||
let diagonal = film.diagonal();
|
|
||||||
let x = (square(diagonal) / (1.0 + square(diagonal))).sqrt();
|
|
||||||
let y = x * aspect;
|
|
||||||
let physical_extent =
|
|
||||||
Bounds2f::from_points(Point2f::new(-x / 2., -y / 2.), Point2f::new(x / 2., y / 2.));
|
|
||||||
let mut element_interface: Vec<LensElementInterface> = Vec::new();
|
|
||||||
|
|
||||||
for i in (0..lens_params.len()).step_by(4) {
|
|
||||||
let curvature_radius = lens_params[i] / 1000.0;
|
|
||||||
let thickness = lens_params[i + 1] / 1000.0;
|
|
||||||
let eta = lens_params[i + 2];
|
|
||||||
let mut aperture_diameter = lens_params[i + 3] / 1000.0;
|
|
||||||
|
|
||||||
if curvature_radius == 0.0 {
|
|
||||||
aperture_diameter /= 1000.0;
|
|
||||||
if set_aperture_diameter > aperture_diameter {
|
|
||||||
println!("Aperture is larger than possible")
|
|
||||||
} else {
|
|
||||||
aperture_diameter = set_aperture_diameter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let el_int = LensElementInterface {
|
|
||||||
curvature_radius,
|
|
||||||
thickness,
|
|
||||||
eta,
|
|
||||||
aperture_radius: aperture_diameter / 2.0,
|
|
||||||
};
|
|
||||||
element_interface.push(el_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
let half_diag = film.diagonal() / 2.0;
|
|
||||||
let mut exit_pupil_bounds = [Bounds2f::default(); EXIT_PUPIL_SAMPLES];
|
|
||||||
|
|
||||||
for i in 0..EXIT_PUPIL_SAMPLES {
|
|
||||||
let r0 = (i as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
|
||||||
let r1 = ((i + 1) as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
|
||||||
exit_pupil_bounds[i] = Self::compute_exit_pupil_bounds(&element_interface, r0, r1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let n_elements = element_interface.len();
|
|
||||||
let element_interfaces = element_interface.as_ptr();
|
|
||||||
std::mem::forget(element_interface);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
base,
|
|
||||||
focus_distance,
|
|
||||||
element_interfaces: Ptr::from(element_interfaces),
|
|
||||||
n_elements,
|
|
||||||
physical_extent,
|
|
||||||
set_aperture_diameter,
|
|
||||||
aperture_image,
|
|
||||||
exit_pupil_bounds,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compute_cardinal_points(r_in: Ray, r_out: Ray) -> (Float, Float) {
|
pub fn compute_cardinal_points(r_in: Ray, r_out: Ray) -> (Float, Float) {
|
||||||
let tf = -r_out.o.x() / r_out.d.x();
|
let tf = -r_out.o.x() / r_out.d.x();
|
||||||
let tp = (r_in.o.x() - r_out.o.x()) / r_out.d.x();
|
let tp = (r_in.o.x() - r_out.o.x()) / r_out.d.x();
|
||||||
|
|
@ -218,11 +149,8 @@ impl RealisticCamera {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unable to find exit pupil in x = {},{} on film.
|
||||||
if pupil_bounds.is_degenerate() {
|
if pupil_bounds.is_degenerate() {
|
||||||
print!(
|
|
||||||
"Unable to find exit pupil in x = {},{} on film.",
|
|
||||||
film_x_0, film_x_1
|
|
||||||
);
|
|
||||||
return pupil_bounds;
|
return pupil_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use crate::core::pbrt::{Float, PI};
|
||||||
use crate::core::sampler::CameraSample;
|
use crate::core::sampler::CameraSample;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::{equal_area_square_to_sphere, wrap_equal_area_square};
|
use crate::utils::math::{equal_area_square_to_sphere, wrap_equal_area_square};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::utils::math::{catmull_rom_weights, square};
|
||||||
use crate::utils::sampling::sample_catmull_rom_2d;
|
use crate::utils::sampling::sample_catmull_rom_2d;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use std::sync::Arc;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BSSRDFSample {
|
pub struct BSSRDFSample {
|
||||||
|
|
|
||||||
|
|
@ -5,15 +5,15 @@ use crate::core::geometry::{
|
||||||
};
|
};
|
||||||
use crate::core::interaction::Interaction;
|
use crate::core::interaction::Interaction;
|
||||||
use crate::core::medium::Medium;
|
use crate::core::medium::Medium;
|
||||||
use crate::core::options::RenderingCoordinateSystem;
|
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::sampler::CameraSample;
|
use crate::core::sampler::CameraSample;
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
|
use crate::utils::options::RenderingCoordinateSystem;
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::transform::{AnimatedTransform, Transform};
|
use crate::utils::transform::{AnimatedTransform, Transform};
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::any::TypeId;
|
use core::any::TypeId;
|
||||||
use std::fmt;
|
use core::fmt;
|
||||||
use std::ops::{
|
use core::ops::{
|
||||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -10,6 +10,7 @@ use crate::core::spectrum::Spectrum;
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::find_interval;
|
use crate::utils::find_interval;
|
||||||
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
|
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ impl From<[Float; 3]> for XYZ {
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a XYZ {
|
impl<'a> IntoIterator for &'a XYZ {
|
||||||
type Item = &'a Float;
|
type Item = &'a Float;
|
||||||
type IntoIter = std::array::IntoIter<&'a Float, 3>;
|
type IntoIter = core::array::IntoIter<&'a Float, 3>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
[&self.x, &self.y, &self.z].into_iter()
|
[&self.x, &self.y, &self.z].into_iter()
|
||||||
|
|
@ -284,7 +285,7 @@ impl From<(Float, Float, Float)> for RGB {
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a RGB {
|
impl<'a> IntoIterator for &'a RGB {
|
||||||
type Item = &'a Float;
|
type Item = &'a Float;
|
||||||
type IntoIter = std::array::IntoIter<&'a Float, 3>;
|
type IntoIter = core::array::IntoIter<&'a Float, 3>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
[&self.r, &self.g, &self.b].into_iter()
|
[&self.r, &self.g, &self.b].into_iter()
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use crate::utils::math::{SquareMatrix, wrap_equal_area_square};
|
||||||
use crate::utils::sampling::VarianceEstimator;
|
use crate::utils::sampling::VarianceEstimator;
|
||||||
use crate::utils::transform::AnimatedTransform;
|
use crate::utils::transform::AnimatedTransform;
|
||||||
use crate::utils::{AtomicFloat, Ptr};
|
use crate::utils::{AtomicFloat, Ptr};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -298,9 +299,9 @@ pub struct SpectralPixel {
|
||||||
impl Clone for SpectralPixel {
|
impl Clone for SpectralPixel {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rgb_sum: std::array::from_fn(|i| AtomicFloat::new(self.rgb_sum[i].get())),
|
rgb_sum: core::array::from_fn(|i| AtomicFloat::new(self.rgb_sum[i].get())),
|
||||||
rgb_weight_sum: AtomicFloat::new(self.rgb_weight_sum.get()),
|
rgb_weight_sum: AtomicFloat::new(self.rgb_weight_sum.get()),
|
||||||
rgb_splat: std::array::from_fn(|i| AtomicFloat::new(self.rgb_splat[i].get())),
|
rgb_splat: core::array::from_fn(|i| AtomicFloat::new(self.rgb_splat[i].get())),
|
||||||
bucket_offset: self.bucket_offset,
|
bucket_offset: self.bucket_offset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -309,9 +310,9 @@ impl Clone for SpectralPixel {
|
||||||
impl Default for SpectralPixel {
|
impl Default for SpectralPixel {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rgb_sum: std::array::from_fn(|_| AtomicFloat::new(0.0)),
|
rgb_sum: core::array::from_fn(|_| AtomicFloat::new(0.0)),
|
||||||
rgb_weight_sum: AtomicFloat::new(0.0),
|
rgb_weight_sum: AtomicFloat::new(0.0),
|
||||||
rgb_splat: std::array::from_fn(|_| AtomicFloat::new(0.0)),
|
rgb_splat: core::array::from_fn(|_| AtomicFloat::new(0.0)),
|
||||||
bucket_offset: 0,
|
bucket_offset: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use super::{Float, NumFloat};
|
use super::{Float, NumFloat};
|
||||||
use super::{Point, Point2f, Point3, Point3f, Vector, Vector2, Vector2f, Vector3, Vector3f};
|
use super::{Point, Point2f, Point3, Point3f, Vector, Vector2, Vector2f, Vector3, Vector3f};
|
||||||
use crate::core::geometry::traits::{Sqrt, VectorLike};
|
use crate::core::geometry::traits::{SqrtExt, VectorLike};
|
||||||
use crate::core::geometry::{max, min};
|
use crate::core::geometry::{max, min};
|
||||||
use crate::utils::interval::Interval;
|
use crate::utils::interval::Interval;
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
|
use core::mem;
|
||||||
|
use core::ops::{Add, Div, DivAssign, Mul, Sub};
|
||||||
use num_traits::{Bounded, Num};
|
use num_traits::{Bounded, Num};
|
||||||
use std::mem;
|
|
||||||
use std::ops::{Add, Div, DivAssign, Mul, Sub};
|
|
||||||
|
|
||||||
// AABB BOUNDING BOXES
|
// AABB BOUNDING BOXES
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub struct Bounds<T, const N: usize> {
|
||||||
|
|
||||||
impl<'a, T, const N: usize> IntoIterator for &'a Bounds<T, N> {
|
impl<'a, T, const N: usize> IntoIterator for &'a Bounds<T, N> {
|
||||||
type Item = &'a Point<T, N>;
|
type Item = &'a Point<T, N>;
|
||||||
type IntoIter = std::array::IntoIter<&'a Point<T, N>, 2>;
|
type IntoIter = core::array::IntoIter<&'a Point<T, N>, 2>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
[&self.p_min, &self.p_max].into_iter()
|
[&self.p_min, &self.p_max].into_iter()
|
||||||
|
|
@ -137,7 +137,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn corner(&self, corner_index: usize) -> Point<T, N> {
|
pub fn corner(&self, corner_index: usize) -> Point<T, N> {
|
||||||
Point(std::array::from_fn(|i| {
|
Point(core::array::from_fn(|i| {
|
||||||
if (corner_index >> i) & 1 == 1 {
|
if (corner_index >> i) & 1 == 1 {
|
||||||
self.p_max[i]
|
self.p_max[i]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -206,7 +206,7 @@ where
|
||||||
|
|
||||||
impl<T> Bounds3<T>
|
impl<T> Bounds3<T>
|
||||||
where
|
where
|
||||||
T: NumFloat + PartialOrd + Copy + Default + Sqrt,
|
T: NumFloat + PartialOrd + Copy + Default + SqrtExt,
|
||||||
{
|
{
|
||||||
pub fn bounding_sphere(&self) -> (Point3<T>, T) {
|
pub fn bounding_sphere(&self) -> (Point3<T>, T) {
|
||||||
let two = T::one() + T::one();
|
let two = T::one() + T::one();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use super::{Bounds3f, Float, PI, Point3f, Vector3f, VectorLike};
|
use super::{Bounds3f, Float, PI, Point3f, Vector3f, VectorLike};
|
||||||
use crate::utils::math::{degrees, safe_acos, safe_asin, safe_sqrt, square};
|
use crate::utils::math::{degrees, safe_acos, safe_asin, safe_sqrt, square};
|
||||||
use crate::utils::transform::TransformGeneric;
|
use crate::utils::transform::TransformGeneric;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DirectionCone {
|
pub struct DirectionCone {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub use self::primitives::{
|
||||||
Vector3i,
|
Vector3i,
|
||||||
};
|
};
|
||||||
pub use self::ray::{Ray, RayDifferential};
|
pub use self::ray::{Ray, RayDifferential};
|
||||||
pub use self::traits::{Lerp, Sqrt, Tuple, VectorLike};
|
pub use self::traits::{Lerp, SqrtExt, Tuple, VectorLike};
|
||||||
|
|
||||||
use crate::core::pbrt::{Float, PI};
|
use crate::core::pbrt::{Float, PI};
|
||||||
use crate::utils::math::{clamp, square};
|
use crate::utils::math::{clamp, square};
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use super::traits::{Sqrt, Tuple, VectorLike};
|
use super::traits::{SqrtExt, Tuple, VectorLike};
|
||||||
use super::{Float, NumFloat, PI};
|
use super::{Float, NumFloat, PI};
|
||||||
use crate::utils::interval::Interval;
|
use crate::utils::interval::Interval;
|
||||||
use crate::utils::math::{clamp, difference_of_products, quadratic, safe_asin};
|
use crate::utils::math::{clamp, difference_of_products, quadratic, safe_asin};
|
||||||
use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero};
|
use core::hash::{Hash, Hasher};
|
||||||
use std::hash::{Hash, Hasher};
|
use core::iter::Sum;
|
||||||
use std::iter::Sum;
|
use core::ops::{
|
||||||
use std::ops::{
|
|
||||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
|
use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero};
|
||||||
|
|
||||||
pub trait MulAdd<M = Self, A = Self> {
|
pub trait MulAdd<M = Self, A = Self> {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
@ -18,7 +18,7 @@ impl MulAdd<Float, Float> for Float {
|
||||||
type Output = Float;
|
type Output = Float;
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn mul_add(self, multiplier: Float, addend: Float) -> Self::Output {
|
fn mul_add(self, multiplier: Float, addend: Float) -> Self::Output {
|
||||||
self.mul_add(multiplier, addend)
|
num_traits::Float::mul_add(self, multiplier, addend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -305,7 +305,7 @@ macro_rules! impl_float_vector_ops {
|
||||||
+ Mul<Output = T>
|
+ Mul<Output = T>
|
||||||
+ Sub<Output = T>
|
+ Sub<Output = T>
|
||||||
+ Div<Output = T>
|
+ Div<Output = T>
|
||||||
+ Sqrt,
|
+ SqrtExt,
|
||||||
{
|
{
|
||||||
type Scalar = T;
|
type Scalar = T;
|
||||||
fn dot(self, rhs: Self) -> T {
|
fn dot(self, rhs: Self) -> T {
|
||||||
|
|
@ -449,7 +449,7 @@ impl<T: Copy, const N: usize> From<Point<T, N>> for Vector<T, N> {
|
||||||
|
|
||||||
impl<T, const N: usize> Point<T, N>
|
impl<T, const N: usize> Point<T, N>
|
||||||
where
|
where
|
||||||
T: NumFloat + Sqrt,
|
T: NumFloat + SqrtExt,
|
||||||
{
|
{
|
||||||
pub fn distance(self, other: Self) -> T {
|
pub fn distance(self, other: Self) -> T {
|
||||||
(self - other).norm()
|
(self - other).norm()
|
||||||
|
|
@ -814,7 +814,7 @@ impl<const N: usize> From<Point<i32, N>> for Point<Float, N> {
|
||||||
|
|
||||||
impl<T> Normal3<T>
|
impl<T> Normal3<T>
|
||||||
where
|
where
|
||||||
T: Num + PartialOrd + Copy + Neg<Output = T> + Sqrt,
|
T: Num + PartialOrd + Copy + Neg<Output = T> + SqrtExt,
|
||||||
{
|
{
|
||||||
pub fn face_forward(self, v: impl Into<Vector3<T>>) -> Self {
|
pub fn face_forward(self, v: impl Into<Vector3<T>>) -> Self {
|
||||||
let v: Vector3<T> = v.into();
|
let v: Vector3<T> = v.into();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::interval::Interval;
|
use crate::utils::interval::Interval;
|
||||||
use crate::utils::math::{next_float_down, next_float_up};
|
use crate::utils::math::{next_float_down, next_float_up};
|
||||||
|
use core::ops::{Add, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
||||||
use num_traits::{Float as NumFloat, FloatConst, Num, One, Signed, Zero};
|
use num_traits::{Float as NumFloat, FloatConst, Num, One, Signed, Zero};
|
||||||
use std::ops::{Add, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
|
||||||
|
|
||||||
pub trait Tuple<T, const N: usize>:
|
pub trait Tuple<T, const N: usize>:
|
||||||
Sized + Copy + Index<usize, Output = T> + IndexMut<usize>
|
Sized + Copy + Index<usize, Output = T> + IndexMut<usize>
|
||||||
|
|
@ -77,7 +77,7 @@ pub trait VectorLike:
|
||||||
+ Div<Self::Scalar, Output = Self>
|
+ Div<Self::Scalar, Output = Self>
|
||||||
+ Mul<Self::Scalar, Output = Self>
|
+ Mul<Self::Scalar, Output = Self>
|
||||||
{
|
{
|
||||||
type Scalar: Copy + Zero + Add<Output = Self::Scalar> + Mul<Output = Self::Scalar> + Sqrt;
|
type Scalar: Copy + Zero + Add<Output = Self::Scalar> + Mul<Output = Self::Scalar> + SqrtExt;
|
||||||
|
|
||||||
fn dot(self, rhs: Self) -> Self::Scalar;
|
fn dot(self, rhs: Self) -> Self::Scalar;
|
||||||
fn norm_squared(self) -> Self::Scalar {
|
fn norm_squared(self) -> Self::Scalar {
|
||||||
|
|
@ -96,7 +96,7 @@ pub trait VectorLike:
|
||||||
}
|
}
|
||||||
|
|
||||||
fn norm(&self) -> Self::Scalar {
|
fn norm(&self) -> Self::Scalar {
|
||||||
self.norm_squared().sqrt()
|
self.norm_squared().sqrt_ext()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize(self) -> Self
|
fn normalize(self) -> Self
|
||||||
|
|
@ -119,36 +119,36 @@ pub trait VectorLike:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Sqrt {
|
pub trait SqrtExt {
|
||||||
fn sqrt(self) -> Self;
|
fn sqrt_ext(self) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sqrt for Float {
|
impl SqrtExt for Float {
|
||||||
fn sqrt(self) -> Self {
|
fn sqrt_ext(self) -> Self {
|
||||||
self.sqrt()
|
<Self as num_traits::Float>::sqrt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sqrt for f64 {
|
impl SqrtExt for f64 {
|
||||||
fn sqrt(self) -> Self {
|
fn sqrt_ext(self) -> Self {
|
||||||
self.sqrt()
|
<Self as num_traits::Float>::sqrt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sqrt for i32 {
|
impl SqrtExt for i32 {
|
||||||
fn sqrt(self) -> Self {
|
fn sqrt_ext(self) -> Self {
|
||||||
self.isqrt()
|
self.isqrt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sqrt for u32 {
|
impl SqrtExt for u32 {
|
||||||
fn sqrt(self) -> Self {
|
fn sqrt_ext(self) -> Self {
|
||||||
self.isqrt()
|
self.isqrt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sqrt for Interval {
|
impl SqrtExt for Interval {
|
||||||
fn sqrt(self) -> Self {
|
fn sqrt_ext(self) -> Self {
|
||||||
let low = if self.low < 0.0 {
|
let low = if self.low < 0.0 {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@ use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
||||||
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::containers::DeviceArray2D;
|
use crate::utils::containers::DeviceArray2D;
|
||||||
use crate::utils::math::{f16_to_f32, lerp, square};
|
use crate::utils::math::{f16_to_f32_software, lerp, square};
|
||||||
use core::hash;
|
use core::hash;
|
||||||
use half::f16;
|
use core::ops::{Deref, DerefMut};
|
||||||
use smallvec::{SmallVec, smallvec};
|
use num_traits::Float as NumFloat;
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum WrapMode {
|
pub enum WrapMode {
|
||||||
|
|
@ -36,8 +35,8 @@ pub enum PixelFormat {
|
||||||
F32,
|
F32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for PixelFormat {
|
impl core::fmt::Display for PixelFormat {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
PixelFormat::U8 => write!(f, "U256"),
|
PixelFormat::U8 => write!(f, "U256"),
|
||||||
PixelFormat::F16 => write!(f, "Half"),
|
PixelFormat::F16 => write!(f, "Half"),
|
||||||
|
|
@ -72,7 +71,7 @@ impl PixelFormat {
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Pixels {
|
pub enum Pixels {
|
||||||
U8(Ptr<u8>),
|
U8(Ptr<u8>),
|
||||||
F16(Ptr<f16>),
|
F16(Ptr<u16>),
|
||||||
F32(Ptr<f32>),
|
F32(Ptr<f32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +179,7 @@ impl ImageAccess for DeviceImage {
|
||||||
}
|
}
|
||||||
Pixels::F16(ptr) => {
|
Pixels::F16(ptr) => {
|
||||||
let raw_val = *ptr.add(offset as usize);
|
let raw_val = *ptr.add(offset as usize);
|
||||||
raw_val.to_f32()
|
f16_to_f32_software(raw_val)
|
||||||
}
|
}
|
||||||
Pixels::F32(ptr) => *ptr.add(offset as usize),
|
Pixels::F32(ptr) => *ptr.add(offset as usize),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ use crate::core::material::{
|
||||||
Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext, bump_map, normal_map,
|
Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext, bump_map, normal_map,
|
||||||
};
|
};
|
||||||
use crate::core::medium::{Medium, MediumInterface, PhaseFunction};
|
use crate::core::medium::{Medium, MediumInterface, PhaseFunction};
|
||||||
use crate::core::options::get_options;
|
|
||||||
use crate::core::sampler::{Sampler, SamplerTrait};
|
use crate::core::sampler::{Sampler, SamplerTrait};
|
||||||
use crate::core::shape::Shape;
|
use crate::core::shape::Shape;
|
||||||
use crate::core::texture::{GPUFloatTexture, UniversalTextureEvaluator};
|
use crate::core::texture::{GPUFloatTexture, UniversalTextureEvaluator};
|
||||||
|
|
@ -21,8 +20,6 @@ use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::{clamp, difference_of_products, square};
|
use crate::utils::math::{clamp, difference_of_products, square};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use std::any::Any;
|
|
||||||
use std::default;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default, Copy, Clone, Debug)]
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
|
|
@ -347,69 +344,6 @@ impl SurfaceInteraction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
pub fn get_bsdf(
|
|
||||||
&mut self,
|
|
||||||
r: &Ray,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
camera: &Camera,
|
|
||||||
sampler: &mut Sampler,
|
|
||||||
) -> Option<BSDF> {
|
|
||||||
self.compute_differentials(r, camera, sampler.samples_per_pixel() as i32);
|
|
||||||
|
|
||||||
let material = {
|
|
||||||
let root_mat = self.material;
|
|
||||||
let mut active_mat: &Material = &*root_mat;
|
|
||||||
let tex_eval = UniversalTextureEvaluator;
|
|
||||||
while let Material::Mix(mix) = active_mat {
|
|
||||||
// We need a context to evaluate the 'amount' texture
|
|
||||||
let ctx = MaterialEvalContext::from(&*self);
|
|
||||||
active_mat = mix.choose_material(&tex_eval, &ctx)?;
|
|
||||||
}
|
|
||||||
active_mat.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ctx = MaterialEvalContext::from(&*self);
|
|
||||||
let tex_eval = UniversalTextureEvaluator;
|
|
||||||
let displacement = material.get_displacement();
|
|
||||||
let normal_map = Ptr::from(material.get_normal_map().unwrap());
|
|
||||||
if !displacement.is_null() || !normal_map.is_null() {
|
|
||||||
// This calls the function defined above
|
|
||||||
self.compute_bump_geom(&tex_eval, displacement, normal_map);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bsdf = material.get_bsdf(&tex_eval, &ctx, lambda);
|
|
||||||
if get_options().force_diffuse {
|
|
||||||
let r = bsdf.rho_wo(self.common.wo, &[sampler.get1d()], &[sampler.get2d()]);
|
|
||||||
let diff_bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
|
|
||||||
bsdf = BSDF::new(self.shading.n, self.shading.dpdu, Ptr::from(&diff_bxdf));
|
|
||||||
}
|
|
||||||
Some(bsdf)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "cuda"))]
|
|
||||||
pub fn get_bssrdf(
|
|
||||||
&self,
|
|
||||||
_ray: &Ray,
|
|
||||||
lambda: &SampledWavelengths,
|
|
||||||
_camera: &Camera,
|
|
||||||
) -> Option<BSSRDF> {
|
|
||||||
let material = {
|
|
||||||
let mut active_mat = unsafe { self.material.as_ref() };
|
|
||||||
let tex_eval = UniversalTextureEvaluator;
|
|
||||||
while let Material::Mix(mix) = active_mat {
|
|
||||||
// We need a context to evaluate the 'amount' texture
|
|
||||||
let ctx = MaterialEvalContext::from(self);
|
|
||||||
active_mat = mix.choose_material(&tex_eval, &ctx)?;
|
|
||||||
}
|
|
||||||
active_mat.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let ctx = MaterialEvalContext::from(self);
|
|
||||||
let tex_eval = UniversalTextureEvaluator;
|
|
||||||
material.get_bssrdf(&tex_eval, &ctx, lambda)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_bump_geom(
|
fn compute_bump_geom(
|
||||||
&mut self,
|
&mut self,
|
||||||
tex_eval: &UniversalTextureEvaluator,
|
tex_eval: &UniversalTextureEvaluator,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::materials::*;
|
use crate::materials::*;
|
||||||
|
use core::ops::Deref;
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::bxdfs::{
|
use crate::bxdfs::{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds3f, Frame, Point2f, Point3f, Point3i, Ray, Vector3f, VectorLike, spherical_direction,
|
Bounds3f, Frame, Point2f, Point3f, Point3i, Ray, Vector3f, VectorLike, spherical_direction,
|
||||||
|
|
@ -15,6 +14,7 @@ use crate::utils::math::{clamp, square};
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
use crate::utils::transform::Transform;
|
use crate::utils::transform::Transform;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -371,7 +371,7 @@ impl MediumProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait MediumTrait: Send + Sync + std::fmt::Debug {
|
pub trait MediumTrait: Send + Sync + core::fmt::Debug {
|
||||||
fn is_emissive(&self) -> bool;
|
fn is_emissive(&self) -> bool;
|
||||||
fn sample_point(&self, p: Point3f, lambda: &SampledWavelengths) -> MediumProperties;
|
fn sample_point(&self, p: Point3f, lambda: &SampledWavelengths) -> MediumProperties;
|
||||||
fn sample_ray(
|
fn sample_ray(
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ pub mod interaction;
|
||||||
pub mod light;
|
pub mod light;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod medium;
|
pub mod medium;
|
||||||
pub mod options;
|
|
||||||
pub mod pbrt;
|
pub mod pbrt;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub mod sampler;
|
pub mod sampler;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
use crate::core::geometry::Lerp;
|
use crate::core::geometry::Lerp;
|
||||||
use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
use core::ops::{Add, Mul};
|
||||||
use num_traits::{Num, PrimInt};
|
use num_traits::{Num, PrimInt};
|
||||||
use std::hash::Hash;
|
|
||||||
use std::ops::{Add, Mul};
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use crate::core::image::DeviceImage;
|
use crate::core::image::DeviceImage;
|
||||||
use crate::core::light::LightTrait;
|
use crate::core::light::LightTrait;
|
||||||
|
|
@ -15,40 +12,6 @@ use crate::utils::Ptr;
|
||||||
|
|
||||||
pub type Float = f32;
|
pub type Float = f32;
|
||||||
|
|
||||||
// #[derive(Copy, Clone, Debug)]
|
|
||||||
// pub struct Host;
|
|
||||||
//
|
|
||||||
// #[derive(Copy, Clone, Debug)]
|
|
||||||
// pub struct Device;
|
|
||||||
//
|
|
||||||
// pub trait Backend: Copy + Clone + 'static {
|
|
||||||
// type ShapeRef: Copy + Clone;
|
|
||||||
// type TextureRef: Copy + Clone;
|
|
||||||
// type ImageRef: Copy + Clone;
|
|
||||||
// type DenseSpectrumRef = Ptr<DenselySampledSpectrum>;
|
|
||||||
// type ColorSpaceRef = Ptr<RGBColorSpace>;
|
|
||||||
// type DiffuseLight: LightTrait;
|
|
||||||
// type PointLight: LightTrait;
|
|
||||||
// type UniformInfiniteLight: LightTrait;
|
|
||||||
// type PortalInfiniteLight: LightTrait;
|
|
||||||
// type ImageInfiniteLight: LightTrait;
|
|
||||||
// type SpotLight: LightTrait;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl Backend for Device {
|
|
||||||
// type ShapeRef = Ptr<Shape>;
|
|
||||||
// type TextureRef = Ptr<GPUFloatTexture>;
|
|
||||||
// type ColorSpaceRef = Ptr<RGBColorSpace>;
|
|
||||||
// type DenseSpectrumRef = Ptr<DenselySampledSpectrum>;
|
|
||||||
// type ImageRef = Ptr<DeviceImage>;
|
|
||||||
// type DiffuseLight = Ptr<DiffuseAreaLight<Device>>;
|
|
||||||
// type PointLight = Ptr<PointLight<Device>>;
|
|
||||||
// type UniformInfiniteLight = Ptr<UniformInfiniteLight<Device>>;
|
|
||||||
// type PortalInfiniteLight = Ptr<PortalInfiniteLight<Device>>;
|
|
||||||
// type ImageInfiniteLight = Ptr<ImageInfiniteLight<Device>>;
|
|
||||||
// type SpotLight = Ptr<SpotLight<Device>>;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
#[cfg(not(feature = "use_f64"))]
|
#[cfg(not(feature = "use_f64"))]
|
||||||
pub type FloatBits = u32;
|
pub type FloatBits = u32;
|
||||||
|
|
||||||
|
|
@ -134,24 +97,28 @@ impl FloatBitOps for f64 {
|
||||||
pub const MACHINE_EPSILON: Float = Float::EPSILON * 0.5;
|
pub const MACHINE_EPSILON: Float = Float::EPSILON * 0.5;
|
||||||
pub const SHADOW_EPSILON: Float = 0.0001;
|
pub const SHADOW_EPSILON: Float = 0.0001;
|
||||||
pub const ONE_MINUS_EPSILON: Float = 0.99999994;
|
pub const ONE_MINUS_EPSILON: Float = 0.99999994;
|
||||||
pub const PI: Float = std::f32::consts::PI;
|
pub const PI: Float = core::f32::consts::PI;
|
||||||
pub const INV_PI: Float = 0.318_309_886_183_790_671_54;
|
pub const INV_PI: Float = core::f32::consts::FRAC_1_PI;
|
||||||
pub const INV_2_PI: Float = 0.159_154_943_091_895_335_77;
|
pub const INV_2_PI: Float = 0.159_154_943_091_895_335_77;
|
||||||
pub const INV_4_PI: Float = 0.079_577_471_545_947_667_88;
|
pub const INV_4_PI: Float = 0.079_577_471_545_947_667_88;
|
||||||
pub const PI_OVER_2: Float = 1.570_796_326_794_896_619_23;
|
pub const PI_OVER_2: Float = core::f32::consts::FRAC_PI_2;
|
||||||
pub const PI_OVER_4: Float = 0.785_398_163_397_448_309_61;
|
pub const PI_OVER_4: Float = core::f32::consts::FRAC_PI_4;
|
||||||
pub const SQRT_2: Float = 1.414_213_562_373_095_048_80;
|
pub const SQRT_2: Float = core::f32::consts::SQRT_2;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn gamma(n: i32) -> Float {
|
pub fn gamma(n: i32) -> Float {
|
||||||
n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON)
|
n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
#[cfg(feature = "cpu_debug")]
|
||||||
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
pub mod debug {
|
||||||
|
use core::sync::atomic::AtomicU64;
|
||||||
|
use core::sync::atomic::Ordering as SyncOrdering;
|
||||||
|
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
||||||
|
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! check_rare {
|
macro_rules! check_rare {
|
||||||
($frequency_threshold:expr, $condition:expr) => {
|
($frequency_threshold:expr, $condition:expr) => {
|
||||||
use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
||||||
const CHECK_INTERVAL: u64 = 4096;
|
const CHECK_INTERVAL: u64 = 4096;
|
||||||
|
|
@ -175,4 +142,5 @@ macro_rules! check_rare {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use crate::utils::hash::hash_float;
|
||||||
use crate::utils::transform::{AnimatedTransform, Transform};
|
use crate::utils::transform::{AnimatedTransform, Transform};
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait PrimitiveTrait: Send + Sync {
|
pub trait PrimitiveTrait: Send + Sync {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::core::filter::FilterTrait;
|
use crate::core::filter::FilterTrait;
|
||||||
use crate::core::geometry::{Bounds2f, Point2f, Point2i, Vector2f};
|
use crate::core::geometry::{Bounds2f, Point2f, Point2i, Vector2f};
|
||||||
use crate::core::options::{PBRTOptions, get_options};
|
|
||||||
use crate::core::pbrt::{Float, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
|
use crate::core::pbrt::{Float, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::containers::DeviceArray2D;
|
use crate::utils::containers::DeviceArray2D;
|
||||||
|
|
@ -14,7 +13,6 @@ use crate::utils::rng::Rng;
|
||||||
use crate::utils::sobol::N_SOBOL_DIMENSIONS;
|
use crate::utils::sobol::N_SOBOL_DIMENSIONS;
|
||||||
use crate::utils::{hash::*, sobol};
|
use crate::utils::{hash::*, sobol};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
use rand::seq::index::sample;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ use crate::core::pbrt::{Float, PI};
|
||||||
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
||||||
use crate::utils::math::{clamp, lerp, safe_sqrt, square};
|
use crate::utils::math::{clamp, lerp, safe_sqrt, square};
|
||||||
use crate::utils::sampling::sample_uniform_disk_polar;
|
use crate::utils::sampling::sample_uniform_disk_polar;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
use num::complex::Complex;
|
use crate::utils::complex::Complex;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy)]
|
||||||
|
|
@ -148,11 +149,11 @@ pub fn fr_dielectric(cos_theta_i: Float, eta: Float) -> Float {
|
||||||
(square(r_parl) + square(r_perp)) / 2.
|
(square(r_parl) + square(r_perp)) / 2.
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fr_complex(cos_theta_i: Float, eta: Complex<Float>) -> Float {
|
pub fn fr_complex(cos_theta_i: Float, eta: Complex) -> Float {
|
||||||
let cos_corr = clamp(cos_theta_i, 0., 1.);
|
let cos_corr = clamp(cos_theta_i, 0., 1.);
|
||||||
let sin2_theta_i = 1. - square(cos_corr);
|
let sin2_theta_i = 1. - square(cos_corr);
|
||||||
let sin2_theta_t: Complex<Float> = sin2_theta_i / square(eta);
|
let sin2_theta_t: Complex = sin2_theta_i / square(eta);
|
||||||
let cos2_theta_t: Complex<Float> = (1. - sin2_theta_t).sqrt();
|
let cos2_theta_t: Complex = (1. - sin2_theta_t).sqrt();
|
||||||
|
|
||||||
let r_parl = (eta * cos_corr - cos2_theta_t) / (eta * cos_corr + cos2_theta_t);
|
let r_parl = (eta * cos_corr - cos2_theta_t) / (eta * cos_corr + cos2_theta_t);
|
||||||
let r_perp = (cos_corr - eta * cos2_theta_t) / (cos_corr + eta * cos2_theta_t);
|
let r_perp = (cos_corr - eta * cos2_theta_t) / (cos_corr + eta * cos2_theta_t);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use crate::utils::Transform;
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::{Float, INV_2_PI, INV_PI, PI};
|
use crate::{Float, INV_2_PI, INV_PI, PI};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
pub use crate::textures::*;
|
pub use crate::textures::*;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use bytemuck::cast_slice;
|
use bytemuck::cast_slice;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
#[repr(C, align(16))]
|
#[repr(C, align(16))]
|
||||||
struct AlignedData<const N: usize>(pub [u8; N]);
|
struct AlignedData<const N: usize>(pub [u8; N]);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ use crate::Float;
|
||||||
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
use crate::utils::math::{lerp, windowed_sinc};
|
use crate::utils::math::{lerp, windowed_sinc};
|
||||||
use rand::Rng;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
|
@ -10,6 +9,7 @@ pub struct LanczosSincFilter {
|
||||||
pub radius: Vector2f,
|
pub radius: Vector2f,
|
||||||
pub tau: Float,
|
pub tau: Float,
|
||||||
pub sampler: FilterSampler,
|
pub sampler: FilterSampler,
|
||||||
|
pub integral: Float,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterTrait for LanczosSincFilter {
|
impl FilterTrait for LanczosSincFilter {
|
||||||
|
|
@ -23,26 +23,7 @@ impl FilterTrait for LanczosSincFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn integral(&self) -> Float {
|
fn integral(&self) -> Float {
|
||||||
let sqrt_samples = 64;
|
self.integral
|
||||||
let n_samples = sqrt_samples * sqrt_samples;
|
|
||||||
let area = (2.0 * self.radius.x()) * (2.0 * self.radius.y());
|
|
||||||
let mut sum = 0.0;
|
|
||||||
let mut rng = rand::rng();
|
|
||||||
|
|
||||||
for y in 0..sqrt_samples {
|
|
||||||
for x in 0..sqrt_samples {
|
|
||||||
let u = Point2f::new(
|
|
||||||
(x as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
|
||||||
(y as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
|
||||||
);
|
|
||||||
let p = Point2f::new(
|
|
||||||
lerp(u.x(), -self.radius.x(), self.radius.x()),
|
|
||||||
lerp(u.y(), -self.radius.y(), self.radius.y()),
|
|
||||||
);
|
|
||||||
sum += self.evaluate(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sum / n_samples as Float * area
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(&self, u: Point2f) -> FilterSample {
|
fn sample(&self, u: Point2f) -> FilterSample {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use crate::Float;
|
||||||
use crate::core::filter::{FilterSample, FilterTrait};
|
use crate::core::filter::{FilterSample, FilterTrait};
|
||||||
use crate::core::geometry::{Point2f, Vector2f};
|
use crate::core::geometry::{Point2f, Vector2f};
|
||||||
use crate::utils::math::sample_tent;
|
use crate::utils::math::sample_tent;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#![allow(unused_imports, dead_code)]
|
#![allow(unused_imports, dead_code)]
|
||||||
#![feature(float_erf)]
|
|
||||||
#![feature(f16)]
|
|
||||||
#![feature(associated_type_defaults)]
|
#![feature(associated_type_defaults)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
pub mod bxdfs;
|
pub mod bxdfs;
|
||||||
pub mod cameras;
|
pub mod cameras;
|
||||||
|
|
@ -16,4 +15,5 @@ pub mod textures;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub use core::pbrt::*;
|
pub use core::pbrt::*;
|
||||||
|
pub use utils::PBRTOptions;
|
||||||
pub use utils::ptr::Ptr;
|
pub use utils::ptr::Ptr;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use crate::spectra::*;
|
||||||
use crate::utils::hash::hash_float;
|
use crate::utils::hash::hash_float;
|
||||||
use crate::utils::{Ptr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ use crate::utils::sampling::{
|
||||||
};
|
};
|
||||||
use crate::utils::{Ptr, Transform};
|
use crate::utils::{Ptr, Transform};
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use std::sync::Arc;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use crate::{
|
||||||
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
||||||
utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D},
|
utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D},
|
||||||
};
|
};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::sampling::AliasTable;
|
use crate::utils::sampling::AliasTable;
|
||||||
use crate::{Float, ONE_MINUS_EPSILON, PI};
|
use crate::{Float, ONE_MINUS_EPSILON, PI};
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -26,7 +27,7 @@ pub struct CompactLightBounds {
|
||||||
pub qb: [[u16; 3]; 2],
|
pub qb: [[u16; 3]; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
const _: () = assert!(std::mem::size_of::<CompactLightBounds>() == 24);
|
const _: () = assert!(core::mem::size_of::<CompactLightBounds>() == 24);
|
||||||
|
|
||||||
impl CompactLightBounds {
|
impl CompactLightBounds {
|
||||||
pub fn new(lb: &LightBounds, all_b: &Bounds3f) -> Self {
|
pub fn new(lb: &LightBounds, all_b: &Bounds3f) -> Self {
|
||||||
|
|
@ -291,7 +292,7 @@ pub struct LightBVHNode {
|
||||||
packed_data: u32,
|
packed_data: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
const _: () = assert!(std::mem::size_of::<LightBVHNode>() == 32);
|
const _: () = assert!(core::mem::size_of::<LightBVHNode>() == 32);
|
||||||
|
|
||||||
impl LightBVHNode {
|
impl LightBVHNode {
|
||||||
/// Mask to isolate the Leaf Flag (Bit 31)
|
/// Mask to isolate the Leaf Flag (Bit 31)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::core::spectrum::SpectrumTrait;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ pub struct CoatedDiffuseMaterial {
|
||||||
pub max_depth: u32,
|
pub max_depth: u32,
|
||||||
pub n_samples: u32,
|
pub n_samples: u32,
|
||||||
pub remap_roughness: bool,
|
pub remap_roughness: bool,
|
||||||
|
pub seed: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoatedDiffuseMaterial {
|
impl CoatedDiffuseMaterial {
|
||||||
|
|
@ -45,6 +46,7 @@ impl CoatedDiffuseMaterial {
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
|
seed: i32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
displacement,
|
displacement,
|
||||||
|
|
@ -59,6 +61,7 @@ impl CoatedDiffuseMaterial {
|
||||||
remap_roughness,
|
remap_roughness,
|
||||||
max_depth,
|
max_depth,
|
||||||
n_samples,
|
n_samples,
|
||||||
|
seed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +115,7 @@ impl MaterialTrait for CoatedDiffuseMaterial {
|
||||||
gg,
|
gg,
|
||||||
self.max_depth,
|
self.max_depth,
|
||||||
self.n_samples,
|
self.n_samples,
|
||||||
|
self.seed,
|
||||||
));
|
));
|
||||||
|
|
||||||
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
||||||
|
|
@ -165,6 +169,7 @@ pub struct CoatedConductorMaterial {
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
|
seed: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoatedConductorMaterial {
|
impl CoatedConductorMaterial {
|
||||||
|
|
@ -186,6 +191,7 @@ impl CoatedConductorMaterial {
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
|
seed: i32,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
displacement,
|
displacement,
|
||||||
|
|
@ -204,6 +210,7 @@ impl CoatedConductorMaterial {
|
||||||
remap_roughness,
|
remap_roughness,
|
||||||
max_depth,
|
max_depth,
|
||||||
n_samples,
|
n_samples,
|
||||||
|
seed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -279,6 +286,7 @@ impl MaterialTrait for CoatedConductorMaterial {
|
||||||
gg,
|
gg,
|
||||||
self.max_depth,
|
self.max_depth,
|
||||||
self.n_samples,
|
self.n_samples,
|
||||||
|
self.seed,
|
||||||
));
|
));
|
||||||
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
||||||
}
|
}
|
||||||
|
|
@ -302,20 +310,24 @@ impl MaterialTrait for CoatedConductorMaterial {
|
||||||
self.conductor_vroughness,
|
self.conductor_vroughness,
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut spectrum_textures = Vec::with_capacity(4);
|
let mut spectrum_textures = [Ptr::null(); 4];
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
spectrum_textures.push(self.albedo);
|
spectrum_textures[n] = self.albedo;
|
||||||
|
n += 1;
|
||||||
|
|
||||||
if !self.conductor_eta.is_null() {
|
if !self.conductor_eta.is_null() {
|
||||||
spectrum_textures.push(self.conductor_eta);
|
spectrum_textures[n] = self.conductor_eta;
|
||||||
|
n += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.k.is_null() {
|
if !self.k.is_null() {
|
||||||
spectrum_textures.push(self.k);
|
spectrum_textures[n] = self.k;
|
||||||
|
n += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.conductor_eta.is_null() {
|
if !self.conductor_eta.is_null() {
|
||||||
spectrum_textures.push(self.reflectance);
|
spectrum_textures[n] = self.reflectance;
|
||||||
}
|
}
|
||||||
|
|
||||||
tex_eval.can_evaluate(&float_textures, &spectrum_textures)
|
tex_eval.can_evaluate(&float_textures, &spectrum_textures)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use crate::utils::splines::{
|
||||||
bound_cubic_bezier, cubic_bezier_control_points, evaluate_cubic_bezier, subdivide_cubic_bezier,
|
bound_cubic_bezier, cubic_bezier_control_points, evaluate_cubic_bezier, subdivide_cubic_bezier,
|
||||||
};
|
};
|
||||||
use crate::utils::transform::{Transform, look_at};
|
use crate::utils::transform::{Transform, look_at};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,11 @@ use crate::utils::splines::{
|
||||||
use crate::utils::transform::{Transform, look_at};
|
use crate::utils::transform::{Transform, look_at};
|
||||||
use crate::{Float, PI, gamma};
|
use crate::{Float, PI, gamma};
|
||||||
|
|
||||||
use crate::core::geometry::{Sqrt, Tuple};
|
use crate::core::geometry::{SqrtExt, Tuple};
|
||||||
use crate::utils::interval::Interval;
|
use crate::utils::interval::Interval;
|
||||||
use crate::utils::math::{clamp, difference_of_products, lerp, square};
|
use crate::utils::math::{clamp, difference_of_products, lerp, square};
|
||||||
use std::mem;
|
use core::mem;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -68,13 +69,13 @@ impl CylinderShape {
|
||||||
let f = b / (2. * a);
|
let f = b / (2. * a);
|
||||||
let vx: Interval = oi.x() - f * di.x();
|
let vx: Interval = oi.x() - f * di.x();
|
||||||
let vy: Interval = oi.y() - f * di.y();
|
let vy: Interval = oi.y() - f * di.y();
|
||||||
let length: Interval = (square(vx) + square(vy)).sqrt();
|
let length: Interval = (square(vx) + square(vy)).sqrt_ext();
|
||||||
let discrim: Interval =
|
let discrim: Interval =
|
||||||
4. * a * (Interval::new(self.radius) * length) * (Interval::new(self.radius) - length);
|
4. * a * (Interval::new(self.radius) * length) * (Interval::new(self.radius) - length);
|
||||||
if discrim.low < 0. {
|
if discrim.low < 0. {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let root_discrim = discrim.sqrt();
|
let root_discrim = discrim.sqrt_ext();
|
||||||
let q = if Float::from(b) < 0. {
|
let q = if Float::from(b) < 0. {
|
||||||
-0.5 * (b - root_discrim)
|
-0.5 * (b - root_discrim)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::utils::Transform;
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::utils::sampling::sample_uniform_disk_concentric;
|
use crate::utils::sampling::sample_uniform_disk_concentric;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
use std::sync::Arc;
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::core::geometry::{
|
||||||
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
|
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
|
||||||
Vector3fi, VectorLike,
|
Vector3fi, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::geometry::{Frame, Sqrt, spherical_direction};
|
use crate::core::geometry::{Frame, SqrtExt, spherical_direction};
|
||||||
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
||||||
use crate::core::pbrt::gamma;
|
use crate::core::pbrt::gamma;
|
||||||
use crate::core::shape::{
|
use crate::core::shape::{
|
||||||
|
|
@ -13,9 +13,9 @@ use crate::utils::interval::Interval;
|
||||||
use crate::utils::math::{clamp, difference_of_products, radians, safe_acos, safe_sqrt, square};
|
use crate::utils::math::{clamp, difference_of_products, radians, safe_acos, safe_sqrt, square};
|
||||||
use crate::utils::sampling::sample_uniform_sphere;
|
use crate::utils::sampling::sample_uniform_sphere;
|
||||||
use crate::{Float, PI};
|
use crate::{Float, PI};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
use std::mem;
|
use core::mem;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -94,7 +94,7 @@ impl SphereShape {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let root_discrim = discrim.sqrt();
|
let root_discrim = discrim.sqrt_ext();
|
||||||
|
|
||||||
let q = if Float::from(b) < 0. {
|
let q = if Float::from(b) < 0. {
|
||||||
-0.5 * (b - root_discrim)
|
-0.5 * (b - root_discrim)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::geometry::{
|
||||||
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3,
|
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3,
|
||||||
Vector3f,
|
Vector3f,
|
||||||
};
|
};
|
||||||
use crate::core::geometry::{Sqrt, Tuple, VectorLike, spherical_triangle_area};
|
use crate::core::geometry::{SqrtExt, Tuple, VectorLike, spherical_triangle_area};
|
||||||
use crate::core::interaction::{
|
use crate::core::interaction::{
|
||||||
Interaction, InteractionBase, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
Interaction, InteractionBase, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,7 @@ use crate::core::pbrt::Float;
|
||||||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
||||||
use crate::utils::math::SquareMatrix3f;
|
use crate::utils::math::SquareMatrix3f;
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use anyhow::{Result, anyhow};
|
use core::cmp::{Eq, PartialEq};
|
||||||
|
|
||||||
use std::cmp::{Eq, PartialEq};
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Debug, Clone)]
|
#[derive(Copy, Debug, Clone)]
|
||||||
|
|
@ -19,13 +17,14 @@ pub struct DeviceStandardColorSpaces {
|
||||||
|
|
||||||
impl DeviceStandardColorSpaces {
|
impl DeviceStandardColorSpaces {
|
||||||
#[cfg(not(target_arch = "nvptx64"))]
|
#[cfg(not(target_arch = "nvptx64"))]
|
||||||
pub fn get_named(&self, name: &str) -> Result<Ptr<RGBColorSpace>> {
|
pub fn get_named(&self, name: &str) -> Option<Ptr<RGBColorSpace>> {
|
||||||
match name.to_lowercase().as_str() {
|
let lower = name.as_bytes();
|
||||||
"srgb" => Ok(self.srgb),
|
match lower {
|
||||||
"dci-p3" => Ok(self.dci_p3),
|
b if b.eq_ignore_ascii_case(b"srgb") => Some(self.srgb),
|
||||||
"rec2020" => Ok(self.rec2020),
|
b if b.eq_ignore_ascii_case(b"dci-p3") => Some(self.dci_p3),
|
||||||
"aces2065-1" => Ok(self.aces2065_1),
|
b if b.eq_ignore_ascii_case(b"rec2020") => Some(self.rec2020),
|
||||||
_ => Err(anyhow!("No such spectrum")),
|
b if b.eq_ignore_ascii_case(b"aces2065-1") => Some(self.aces2065_1),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,11 +50,12 @@ pub enum ColorSpaceId {
|
||||||
impl ColorSpaceId {
|
impl ColorSpaceId {
|
||||||
#[cfg(not(target_arch = "nvptx64"))]
|
#[cfg(not(target_arch = "nvptx64"))]
|
||||||
pub fn from_name(name: &str) -> Option<Self> {
|
pub fn from_name(name: &str) -> Option<Self> {
|
||||||
match name.to_lowercase().as_str() {
|
let lower = name.as_bytes();
|
||||||
"srgb" => Some(Self::SRGB),
|
match lower {
|
||||||
"dci-p3" => Some(Self::DciP3),
|
b if b.eq_ignore_ascii_case(b"srgb") => Some(Self::SRGB),
|
||||||
"rec2020" => Some(Self::Rec2020),
|
b if b.eq_ignore_ascii_case(b"dci-p3") => Some(Self::DciP3),
|
||||||
"aces2065-1" => Some(Self::Aces2065_1),
|
b if b.eq_ignore_ascii_case(b"rec2020") => Some(Self::Rec2020),
|
||||||
|
b if b.eq_ignore_ascii_case(b"aces2065-1") => Some(Self::Aces2065_1),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::core::spectrum::{SpectrumTrait, StandardSpectra};
|
use crate::core::spectrum::{SpectrumTrait, StandardSpectra};
|
||||||
use crate::utils::math::{clamp, lerp};
|
use crate::utils::math::{clamp, lerp};
|
||||||
use std::ops::{
|
use core::ops::{
|
||||||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||||
};
|
};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
pub const CIE_Y_INTEGRAL: Float = 106.856895;
|
pub const CIE_Y_INTEGRAL: Float = 106.856895;
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ impl SampledSpectrum {
|
||||||
F: FnMut(usize) -> Float,
|
F: FnMut(usize) -> Float,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
values: std::array::from_fn(cb),
|
values: core::array::from_fn(cb),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +131,7 @@ impl SampledSpectrum {
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a SampledSpectrum {
|
impl<'a> IntoIterator for &'a SampledSpectrum {
|
||||||
type Item = &'a Float;
|
type Item = &'a Float;
|
||||||
type IntoIter = std::slice::Iter<'a, Float>;
|
type IntoIter = core::slice::Iter<'a, Float>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.values.iter()
|
self.values.iter()
|
||||||
|
|
@ -139,7 +140,7 @@ impl<'a> IntoIterator for &'a SampledSpectrum {
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a mut SampledSpectrum {
|
impl<'a> IntoIterator for &'a mut SampledSpectrum {
|
||||||
type Item = &'a mut Float;
|
type Item = &'a mut Float;
|
||||||
type IntoIter = std::slice::IterMut<'a, Float>;
|
type IntoIter = core::slice::IterMut<'a, Float>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
self.values.iter_mut()
|
self.values.iter_mut()
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,7 @@ use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::find_interval;
|
use crate::utils::find_interval;
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use core::slice;
|
use core::slice;
|
||||||
use std::hash::{Hash, Hasher};
|
use num_traits::Float as NumFloat;
|
||||||
use std::sync::LazyLock;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
@ -68,17 +67,6 @@ impl PartialEq for DenselySampledSpectrum {
|
||||||
|
|
||||||
impl Eq for DenselySampledSpectrum {}
|
impl Eq for DenselySampledSpectrum {}
|
||||||
|
|
||||||
// impl Hash for DenselySampledSpectrum {
|
|
||||||
// fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
// self.lambda_min.hash(state);
|
|
||||||
// self.lambda_max.hash(state);
|
|
||||||
//
|
|
||||||
// for v in &self.values {
|
|
||||||
// v.to_bits().hash(state);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl SpectrumTrait for DenselySampledSpectrum {
|
impl SpectrumTrait for DenselySampledSpectrum {
|
||||||
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||||
let mut s = SampledSpectrum::default();
|
let mut s = SampledSpectrum::default();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::core::texture::{
|
||||||
};
|
};
|
||||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::{Ptr, math::square};
|
use crate::utils::{Ptr, math::square};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
fn checkerboard(
|
fn checkerboard(
|
||||||
ctx: &TextureEvalContext,
|
ctx: &TextureEvalContext,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths};
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::square;
|
use crate::utils::math::square;
|
||||||
use crate::utils::noise::noise_2d;
|
use crate::utils::noise::noise_2d;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
fn inside_polka_dot(st: Point2f) -> bool {
|
fn inside_polka_dot(st: Point2f) -> bool {
|
||||||
let s_cell = (st[0] + 0.5).floor();
|
let s_cell = (st[0] + 0.5).floor();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use crate::utils::math::clamp;
|
||||||
use crate::utils::noise::fbm;
|
use crate::utils::noise::fbm;
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::splines::evaluate_cubic_bezier;
|
use crate::utils::splines::evaluate_cubic_bezier;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
|
|
||||||
115
shared/src/utils/complex.rs
Normal file
115
shared/src/utils/complex.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
use crate::Float;
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Complex {
|
||||||
|
pub re: Float,
|
||||||
|
pub im: Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Complex {
|
||||||
|
pub fn new(re: Float, im: Float) -> Self {
|
||||||
|
Self { re, im }
|
||||||
|
}
|
||||||
|
pub fn norm(&self) -> Float {
|
||||||
|
(self.re * self.re + self.im * self.im).sqrt()
|
||||||
|
}
|
||||||
|
pub fn sqrt(self) -> Self {
|
||||||
|
let r = self.norm();
|
||||||
|
let re = ((r + self.re) / 2.0).sqrt();
|
||||||
|
let im = self.im.signum() * ((r - self.re) / 2.0).sqrt();
|
||||||
|
Self { re, im }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Add for Complex {
|
||||||
|
type Output = Self;
|
||||||
|
fn add(self, rhs: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
re: self.re + rhs.re,
|
||||||
|
im: self.im + rhs.im,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Mul for Complex {
|
||||||
|
type Output = Self;
|
||||||
|
fn mul(self, rhs: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
re: self.re * rhs.re - self.im * rhs.im,
|
||||||
|
im: self.re * rhs.im + self.im * rhs.re,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Div for Complex {
|
||||||
|
type Output = Self;
|
||||||
|
fn div(self, rhs: Self) -> Self {
|
||||||
|
let denom = rhs.re * rhs.re + rhs.im * rhs.im;
|
||||||
|
Self {
|
||||||
|
re: (self.re * rhs.re + self.im * rhs.im) / denom,
|
||||||
|
im: (self.im * rhs.re - self.re * rhs.im) / denom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Sub for Complex {
|
||||||
|
type Output = Self;
|
||||||
|
fn sub(self, rhs: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
re: self.re - rhs.re,
|
||||||
|
im: self.im - rhs.im,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Mul<Complex> for Float {
|
||||||
|
type Output = Complex;
|
||||||
|
fn mul(self, rhs: Complex) -> Complex {
|
||||||
|
Complex {
|
||||||
|
re: self * rhs.re,
|
||||||
|
im: self * rhs.im,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Mul<Float> for Complex {
|
||||||
|
type Output = Complex;
|
||||||
|
fn mul(self, rhs: Float) -> Complex {
|
||||||
|
Complex {
|
||||||
|
re: self.re * rhs,
|
||||||
|
im: self.im * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Sub<Complex> for Float {
|
||||||
|
type Output = Complex;
|
||||||
|
fn sub(self, rhs: Complex) -> Complex {
|
||||||
|
Complex {
|
||||||
|
re: self - rhs.re,
|
||||||
|
im: -rhs.im,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Add<Complex> for Float {
|
||||||
|
type Output = Complex;
|
||||||
|
fn add(self, rhs: Complex) -> Complex {
|
||||||
|
Complex {
|
||||||
|
re: self + rhs.re,
|
||||||
|
im: rhs.im,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::ops::Div<Complex> for Float {
|
||||||
|
type Output = Complex;
|
||||||
|
fn div(self, rhs: Complex) -> Complex {
|
||||||
|
let denom = rhs.re * rhs.re + rhs.im * rhs.im;
|
||||||
|
Complex {
|
||||||
|
re: self * rhs.re / denom,
|
||||||
|
im: -self * rhs.im / denom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,11 +4,7 @@ use crate::core::geometry::{
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use crate::utils::math::lerp;
|
use crate::utils::math::lerp;
|
||||||
use std::collections::HashSet;
|
use core::ops::{Add, Index, IndexMut, Mul, Sub};
|
||||||
use std::collections::hash_map::RandomState;
|
|
||||||
use std::hash::{BuildHasher, Hash, Hasher};
|
|
||||||
use std::ops::{Add, Index, IndexMut, Mul, Sub};
|
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
|
||||||
|
|
||||||
pub trait Interpolatable:
|
pub trait Interpolatable:
|
||||||
Copy + Default + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self>
|
Copy + Default + Add<Output = Self> + Sub<Output = Self> + Mul<Float, Output = Self>
|
||||||
|
|
@ -158,7 +154,7 @@ impl<T> SampledGrid<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bytes_allocated(&self) -> u32 {
|
pub fn bytes_allocated(&self) -> u32 {
|
||||||
self.values_len * std::mem::size_of::<T>() as u32
|
self.values_len * core::mem::size_of::<T>() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn x_size(&self) -> i32 {
|
pub fn x_size(&self) -> i32 {
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
use std::fmt;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum LlsError {
|
|
||||||
SingularMatrix,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for LlsError {}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum InversionError {
|
|
||||||
SingularMatrix,
|
|
||||||
EmptyMatrix,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for LlsError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
LlsError::SingularMatrix => write!(f, "Matrix is singular and cannot be inverted."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for InversionError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
InversionError::SingularMatrix => {
|
|
||||||
write!(f, "Matrix is singular and cannot be inverted.")
|
|
||||||
}
|
|
||||||
InversionError::EmptyMatrix => write!(f, "Matrix is empty and cannot be inverted."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for InversionError {}
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
use crate::core::pbrt::Float;
|
use crate::Float;
|
||||||
use std::collections::hash_map::DefaultHasher;
|
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
|
|
||||||
const U32_TO_F32_SCALE: f32 = 1.0 / 4294967296.0;
|
const U32_TO_F32_SCALE: f32 = 1.0 / 4294967296.0;
|
||||||
|
|
||||||
|
|
@ -58,9 +56,9 @@ pub fn murmur_hash_64a(key: &[u8], seed: u64) -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_buffer<T: ?Sized>(data: &T, seed: u64) -> u64 {
|
pub fn hash_buffer<T: ?Sized>(data: &T, seed: u64) -> u64 {
|
||||||
let len = std::mem::size_of_val(data);
|
let len = core::mem::size_of_val(data);
|
||||||
let ptr = data as *const T as *const u8;
|
let ptr = data as *const T as *const u8;
|
||||||
let bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
|
let bytes = unsafe { core::slice::from_raw_parts(ptr, len) };
|
||||||
murmur_hash_64a(bytes, seed)
|
murmur_hash_64a(bytes, seed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +70,7 @@ macro_rules! hash_values {
|
||||||
// We use a temporary tuple or array to pack bits
|
// We use a temporary tuple or array to pack bits
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
struct PackedData {
|
struct PackedData {
|
||||||
$(_field: Box<[u8]>),* // Phantom, logic below is simpler
|
$(_field: Box<[u8]>),*
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate total size and create a byte buffer
|
// Calculate total size and create a byte buffer
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::core::pbrt::Float;
|
use crate::core::pbrt::Float;
|
||||||
use crate::utils::math::{next_float_down, next_float_up};
|
use crate::utils::math::{next_float_down, next_float_up};
|
||||||
use num_traits::Zero;
|
use num_traits::Zero;
|
||||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use super::error::{InversionError, LlsError};
|
|
||||||
use crate::core::color::{RGB, XYZ};
|
use crate::core::color::{RGB, XYZ};
|
||||||
use crate::core::geometry::{Lerp, MulAdd, Point, Point2f, Point2i, Vector, Vector3f, VectorLike};
|
use crate::core::geometry::{Lerp, MulAdd, Point, Point2f, Point2i, Vector, Vector3f, VectorLike};
|
||||||
use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI_OVER_4};
|
use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI_OVER_4};
|
||||||
|
|
@ -6,13 +5,11 @@ use crate::utils::hash::{hash_buffer, mix_bits};
|
||||||
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
|
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
|
||||||
|
|
||||||
use crate::utils::Ptr;
|
use crate::utils::Ptr;
|
||||||
use half::f16;
|
use core::fmt::{self, Display, Write};
|
||||||
|
use core::iter::{Product, Sum};
|
||||||
|
use core::mem;
|
||||||
|
use core::ops::{Add, Div, Index, IndexMut, Mul, Neg, Rem};
|
||||||
use num_traits::{Float as NumFloat, Num, One, Signed, Zero};
|
use num_traits::{Float as NumFloat, Num, One, Signed, Zero};
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use std::iter::{Product, Sum};
|
|
||||||
use std::mem;
|
|
||||||
use std::ops::{Add, Div, Index, IndexMut, Mul, Neg, Rem};
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn degrees(a: Float) -> Float {
|
pub fn degrees(a: Float) -> Float {
|
||||||
|
|
@ -69,7 +66,7 @@ pub fn evaluate_polynomial(t: Float, coeffs: &[Float]) -> Float {
|
||||||
assert!(!coeffs.is_empty());
|
assert!(!coeffs.is_empty());
|
||||||
let mut result = coeffs[0];
|
let mut result = coeffs[0];
|
||||||
for &c in &coeffs[1..] {
|
for &c in &coeffs[1..] {
|
||||||
result = t.mul_add(result, c);
|
result = num_traits::Float::mul_add(t, result, c);
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
@ -274,13 +271,13 @@ pub fn smooth_step(x: Float, a: Float, b: Float) -> Float {
|
||||||
pub fn linear_least_squares<const R: usize, const N: usize>(
|
pub fn linear_least_squares<const R: usize, const N: usize>(
|
||||||
a: [[Float; R]; N],
|
a: [[Float; R]; N],
|
||||||
b: [[Float; R]; N],
|
b: [[Float; R]; N],
|
||||||
) -> Result<SquareMatrix<Float, R>, Box<dyn Error>> {
|
) -> Option<SquareMatrix<Float, R>> {
|
||||||
let am = Matrix::from(a);
|
let am = Matrix::from(a);
|
||||||
let bm = Matrix::from(b);
|
let bm = Matrix::from(b);
|
||||||
let ata = am.transpose() * am;
|
let ata = am.transpose() * am;
|
||||||
let atb = am.transpose() * bm;
|
let atb = am.transpose() * bm;
|
||||||
let at_ai = ata.inverse()?;
|
let at_ai = ata.inverse()?;
|
||||||
Ok((at_ai * atb).transpose())
|
Some((at_ai * atb).transpose())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newton_bisection<P>(mut x0: Float, mut x1: Float, mut f: P) -> Float
|
pub fn newton_bisection<P>(mut x0: Float, mut x1: Float, mut f: P) -> Float
|
||||||
|
|
@ -473,10 +470,24 @@ pub fn gaussian(x: Float, y: Float, sigma: Float) -> Float {
|
||||||
(-(x * x + y * y) / (2. * sigma * sigma)).exp()
|
(-(x * x + y * y) / (2. * sigma * sigma)).exp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn erf(x: Float) -> Float {
|
||||||
|
let a1 = 0.254829592;
|
||||||
|
let a2 = -0.284496736;
|
||||||
|
let a3 = 1.421413741;
|
||||||
|
let a4 = -1.453152027;
|
||||||
|
let a5 = 1.061405429;
|
||||||
|
let p = 0.3275911;
|
||||||
|
let sign = if x < 0.0 { -1.0 } else { 1.0 };
|
||||||
|
let x = x.abs();
|
||||||
|
let t = 1.0 / (1.0 + p * x);
|
||||||
|
let y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * (-x * x).exp();
|
||||||
|
sign * y
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gaussian_integral(x0: Float, x1: Float, mu: Float, sigma: Float) -> Float {
|
pub fn gaussian_integral(x0: Float, x1: Float, mu: Float, sigma: Float) -> Float {
|
||||||
assert!(sigma > 0.);
|
assert!(sigma > 0.);
|
||||||
let sigma_root2 = sigma * 1.414213562373095;
|
let sigma_root2 = sigma * 1.414213562373095;
|
||||||
0.5 * (((mu - x0) / sigma_root2).erf() - ((mu - x1) / sigma_root2).erf())
|
0.5 * (erf((mu - x0) / sigma_root2) - erf((mu - x1) / sigma_root2))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_linear(u: Float, a: Float, b: Float) -> Float {
|
pub fn sample_linear(u: Float, a: Float, b: Float) -> Float {
|
||||||
|
|
@ -1079,7 +1090,7 @@ impl<T, const R: usize, const C: usize> Matrix<T, R, C> {
|
||||||
where
|
where
|
||||||
T: Clone + Zero,
|
T: Clone + Zero,
|
||||||
{
|
{
|
||||||
let m: [[T; C]; R] = std::array::from_fn(|_| std::array::from_fn(|_| T::zero()));
|
let m: [[T; C]; R] = core::array::from_fn(|_| core::array::from_fn(|_| T::zero()));
|
||||||
Self { m }
|
Self { m }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1189,21 +1200,30 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CountingFmt(usize);
|
||||||
|
|
||||||
|
impl Write for CountingFmt {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
self.0 += s.len();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Display, const R: usize, const C: usize> Display for Matrix<T, R, C> {
|
impl<T: Display, const R: usize, const C: usize> Display for Matrix<T, R, C> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut col_widths = [0; C];
|
let mut col_widths = [0; C];
|
||||||
|
|
||||||
|
// Measure without allocation
|
||||||
for row in self.m.iter() {
|
for row in self.m.iter() {
|
||||||
for (j, element) in row.iter().enumerate() {
|
for (j, element) in row.iter().enumerate() {
|
||||||
let width = format!("{}", element).len();
|
let mut counter = CountingFmt(0);
|
||||||
if width > col_widths[j] {
|
let _ = write!(counter, "{}", element); // Ignores errors, just counts
|
||||||
col_widths[j] = width;
|
col_widths[j] = col_widths[j].max(counter.0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..R {
|
for i in 0..R {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
#[allow(clippy::needless_range_loop)]
|
|
||||||
for j in 0..C {
|
for j in 0..C {
|
||||||
write!(f, "{: >width$} ", self.m[i][j], width = col_widths[j])?;
|
write!(f, "{: >width$} ", self.m[i][j], width = col_widths[j])?;
|
||||||
}
|
}
|
||||||
|
|
@ -1238,8 +1258,8 @@ impl<T, const N: usize> SquareMatrix<T, N> {
|
||||||
T: Copy + Zero + One,
|
T: Copy + Zero + One,
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
m: std::array::from_fn(|i| {
|
m: core::array::from_fn(|i| {
|
||||||
std::array::from_fn(|j| if i == j { T::one() } else { T::zero() })
|
core::array::from_fn(|j| if i == j { T::one() } else { T::zero() })
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1302,9 +1322,9 @@ impl<T, const N: usize> SquareMatrix<T, N>
|
||||||
where
|
where
|
||||||
T: NumFloat + Sum + Product + Copy,
|
T: NumFloat + Sum + Product + Copy,
|
||||||
{
|
{
|
||||||
pub fn inverse(&self) -> Result<Self, InversionError> {
|
pub fn inverse(&self) -> Option<Self> {
|
||||||
if N == 0 {
|
if N == 0 {
|
||||||
return Err(InversionError::EmptyMatrix);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mat = self.m;
|
let mut mat = self.m;
|
||||||
|
|
@ -1322,7 +1342,7 @@ where
|
||||||
|
|
||||||
let pivot = mat[i][i];
|
let pivot = mat[i][i];
|
||||||
if pivot.is_zero() {
|
if pivot.is_zero() {
|
||||||
return Err(InversionError::SingularMatrix);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inv_pivot = T::one() / pivot;
|
let inv_pivot = T::one() / pivot;
|
||||||
|
|
@ -1344,7 +1364,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(inv)
|
|
||||||
|
Some(inv)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn determinant(&self) -> T {
|
pub fn determinant(&self) -> T {
|
||||||
|
|
@ -1448,7 +1469,8 @@ where
|
||||||
{
|
{
|
||||||
type Output = Vector<T, N>;
|
type Output = Vector<T, N>;
|
||||||
fn mul(self, rhs: Vector<T, N>) -> Self::Output {
|
fn mul(self, rhs: Vector<T, N>) -> Self::Output {
|
||||||
let arr = std::array::from_fn(|i| self.m[i].iter().zip(&rhs.0).map(|(m, v)| *m * *v).sum());
|
let arr =
|
||||||
|
core::array::from_fn(|i| self.m[i].iter().zip(&rhs.0).map(|(m, v)| *m * *v).sum());
|
||||||
Vector(arr)
|
Vector(arr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1459,7 +1481,8 @@ where
|
||||||
{
|
{
|
||||||
type Output = Point<T, N>;
|
type Output = Point<T, N>;
|
||||||
fn mul(self, rhs: Point<T, N>) -> Self::Output {
|
fn mul(self, rhs: Point<T, N>) -> Self::Output {
|
||||||
let arr = std::array::from_fn(|i| self.m[i].iter().zip(&rhs.0).map(|(m, v)| *m * *v).sum());
|
let arr =
|
||||||
|
core::array::from_fn(|i| self.m[i].iter().zip(&rhs.0).map(|(m, v)| *m * *v).sum());
|
||||||
Point(arr)
|
Point(arr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1540,31 +1563,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
pub fn f16_to_f32_software(h: u16) -> f32 {
|
||||||
pub fn f16_to_f32(bits: u16) -> f32 {
|
|
||||||
#[cfg(target_os = "cuda")]
|
|
||||||
{
|
|
||||||
// Use hardware intrinsic on CUDA
|
|
||||||
// Cast bits to cuda_std::f16, then cast to f32
|
|
||||||
let half_val = unsafe { core::mem::transmute::<u16, cuda_std::f16>(bits) };
|
|
||||||
half_val.to_f32()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "spirv")]
|
|
||||||
{
|
|
||||||
// Use shared logic or spirv-std intrinsics if available.
|
|
||||||
// Sadly, f16 support in rust-gpu is still maturing.
|
|
||||||
// A manual bit-conversion function is often safest here.
|
|
||||||
f16_to_f32_software(bits)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "cuda", target_arch = "spirv")))]
|
|
||||||
{
|
|
||||||
f16::from_bits(bits).to_f32()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn f16_to_f32_software(h: u16) -> f32 {
|
|
||||||
let sign = ((h >> 15) & 1) as u32;
|
let sign = ((h >> 15) & 1) as u32;
|
||||||
let exp = ((h >> 10) & 0x1F) as u32;
|
let exp = ((h >> 10) & 0x1F) as u32;
|
||||||
let mant = (h & 0x3FF) as u32;
|
let mant = (h & 0x3FF) as u32;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
pub mod complex;
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
pub mod error;
|
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod interval;
|
pub mod interval;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod noise;
|
pub mod noise;
|
||||||
|
pub mod options;
|
||||||
pub mod ptr;
|
pub mod ptr;
|
||||||
pub mod quaternion;
|
pub mod quaternion;
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
|
|
@ -13,11 +14,12 @@ pub mod sobol;
|
||||||
pub mod splines;
|
pub mod splines;
|
||||||
pub mod transform;
|
pub mod transform;
|
||||||
|
|
||||||
|
pub use options::PBRTOptions;
|
||||||
pub use ptr::Ptr;
|
pub use ptr::Ptr;
|
||||||
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
||||||
|
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
|
use core::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn find_interval<F>(sz: u32, pred: F) -> u32
|
pub fn find_interval<F>(sz: u32, pred: F) -> u32
|
||||||
|
|
@ -113,37 +115,37 @@ impl AtomicFloat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AtomicDouble {
|
// pub struct AtomicDouble {
|
||||||
bits: AtomicU64,
|
// bits: AtomicU64,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl AtomicDouble {
|
// impl AtomicDouble {
|
||||||
pub fn new(val: f64) -> Self {
|
// pub fn new(val: f64) -> Self {
|
||||||
Self {
|
// Self {
|
||||||
bits: AtomicU64::new(val.to_bits()),
|
// bits: AtomicU64::new(val.to_bits()),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
pub fn get(&self) -> f64 {
|
// pub fn get(&self) -> f64 {
|
||||||
f64::from_bits(self.bits.load(Ordering::Relaxed))
|
// f64::from_bits(self.bits.load(Ordering::Relaxed))
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
pub fn add(&self, val: f64) {
|
// pub fn add(&self, val: f64) {
|
||||||
let mut current_bits = self.bits.load(Ordering::Relaxed);
|
// let mut current_bits = self.bits.load(Ordering::Relaxed);
|
||||||
loop {
|
// loop {
|
||||||
let current_val = f64::from_bits(current_bits);
|
// let current_val = f64::from_bits(current_bits);
|
||||||
let new_val = current_val + val;
|
// let new_val = current_val + val;
|
||||||
let new_bits = new_val.to_bits();
|
// let new_bits = new_val.to_bits();
|
||||||
|
//
|
||||||
match self.bits.compare_exchange_weak(
|
// match self.bits.compare_exchange_weak(
|
||||||
current_bits,
|
// current_bits,
|
||||||
new_bits,
|
// new_bits,
|
||||||
Ordering::Relaxed,
|
// Ordering::Relaxed,
|
||||||
Ordering::Relaxed,
|
// Ordering::Relaxed,
|
||||||
) {
|
// ) {
|
||||||
Ok(_) => break,
|
// Ok(_) => break,
|
||||||
Err(x) => current_bits = x,
|
// Err(x) => current_bits = x,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use crate::core::geometry::{Point3f, Vector3f, VectorLike};
|
use crate::core::geometry::{Point3f, Vector3f, VectorLike};
|
||||||
use crate::utils::math::{clamp, lerp, smooth_step};
|
use crate::utils::math::{clamp, lerp, smooth_step};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
|
|
||||||
static NOISE_PERM_SIZE: usize = 256;
|
static NOISE_PERM_SIZE: usize = 256;
|
||||||
static NOISE_PERM: [i32; 2 * NOISE_PERM_SIZE] = [
|
static NOISE_PERM: [i32; 2 * NOISE_PERM_SIZE] = [
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
|
||||||
use crate::Float;
|
use crate::Float;
|
||||||
use std::ops::Deref;
|
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
||||||
use std::sync::OnceLock;
|
use core::ops::Deref;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum RenderingCoordinateSystem {
|
pub enum RenderingCoordinateSystem {
|
||||||
|
|
@ -48,17 +47,17 @@ pub struct PBRTOptions {
|
||||||
pub basic: BasicPBRTOptions,
|
pub basic: BasicPBRTOptions,
|
||||||
|
|
||||||
pub n_threads: usize,
|
pub n_threads: usize,
|
||||||
pub log_level: String,
|
pub log_level: &'static str,
|
||||||
pub write_partial_images: bool,
|
pub write_partial_images: bool,
|
||||||
pub image_file: String,
|
pub image_file: &'static str,
|
||||||
pub pixel_samples: Option<i32>,
|
pub pixel_samples: Option<i32>,
|
||||||
pub gpu_device: Option<u32>,
|
pub gpu_device: Option<u32>,
|
||||||
pub mse_reference_image: Option<String>,
|
pub mse_reference_image: Option<&'static str>,
|
||||||
pub mse_reference_output: Option<String>,
|
pub mse_reference_output: Option<&'static str>,
|
||||||
pub debug_start: Option<(Point2i, i32)>,
|
pub debug_start: Option<(Point2i, i32)>,
|
||||||
pub quick_render: bool,
|
pub quick_render: bool,
|
||||||
pub upgrade: bool,
|
pub upgrade: bool,
|
||||||
pub display_server: String,
|
pub display_server: &'static str,
|
||||||
pub crop_window: Option<Bounds2f>,
|
pub crop_window: Option<Bounds2f>,
|
||||||
pub pixel_bounds: Option<Bounds2i>,
|
pub pixel_bounds: Option<Bounds2i>,
|
||||||
pub pixel_material: Option<Point2i>,
|
pub pixel_material: Option<Point2i>,
|
||||||
|
|
@ -70,17 +69,17 @@ impl Default for PBRTOptions {
|
||||||
Self {
|
Self {
|
||||||
basic: BasicPBRTOptions::default(),
|
basic: BasicPBRTOptions::default(),
|
||||||
n_threads: 0,
|
n_threads: 0,
|
||||||
log_level: "info".to_string(),
|
log_level: "info",
|
||||||
write_partial_images: false,
|
write_partial_images: false,
|
||||||
pixel_samples: None,
|
pixel_samples: None,
|
||||||
gpu_device: None,
|
gpu_device: None,
|
||||||
quick_render: false,
|
quick_render: false,
|
||||||
upgrade: false,
|
upgrade: false,
|
||||||
image_file: "output.exr".to_string(),
|
image_file: "output.exr",
|
||||||
mse_reference_image: None,
|
mse_reference_image: None,
|
||||||
mse_reference_output: None,
|
mse_reference_output: None,
|
||||||
debug_start: Some((Point2i::default(), 0)),
|
debug_start: Some((Point2i::default(), 0)),
|
||||||
display_server: "".to_string(),
|
display_server: "",
|
||||||
crop_window: None,
|
crop_window: None,
|
||||||
pixel_bounds: None,
|
pixel_bounds: None,
|
||||||
pixel_material: None,
|
pixel_material: None,
|
||||||
|
|
@ -96,22 +95,3 @@ impl Deref for PBRTOptions {
|
||||||
&self.basic
|
&self.basic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static OPTIONS: OnceLock<PBRTOptions> = OnceLock::new();
|
|
||||||
|
|
||||||
pub fn init_pbrt(options: PBRTOptions) {
|
|
||||||
OPTIONS
|
|
||||||
.set(options)
|
|
||||||
.expect("PBRT has already been initialized!");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cleanup_pbrt() {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_options() -> &'static PBRTOptions {
|
|
||||||
OPTIONS.get().unwrap_or_else(|| {
|
|
||||||
static DEFAULT: OnceLock<PBRTOptions> = OnceLock::new();
|
|
||||||
DEFAULT.get_or_init(PBRTOptions::default)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -16,7 +16,7 @@ impl<T: ?Sized> Copy for Ptr<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized> PartialEq for Ptr<T> {
|
impl<T: ?Sized> PartialEq for Ptr<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
std::ptr::addr_eq(self.ptr, other.ptr)
|
core::ptr::addr_eq(self.ptr, other.ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ unsafe impl<T: ?Sized + Sync> Sync for Ptr<T> {}
|
||||||
impl<T> Ptr<T> {
|
impl<T> Ptr<T> {
|
||||||
pub const fn null() -> Self {
|
pub const fn null() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ptr: std::ptr::null(),
|
ptr: core::ptr::null(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ impl<T> Ptr<T> {
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
&[]
|
&[]
|
||||||
} else {
|
} else {
|
||||||
unsafe { std::slice::from_raw_parts(self.ptr, len) }
|
unsafe { core::slice::from_raw_parts(self.ptr, len) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,12 +101,12 @@ impl<T> Ptr<T> {
|
||||||
if self.is_null() {
|
if self.is_null() {
|
||||||
if len == 0 { Some(&[]) } else { None }
|
if len == 0 { Some(&[]) } else { None }
|
||||||
} else {
|
} else {
|
||||||
Some(unsafe { std::slice::from_raw_parts(self.ptr, len) })
|
Some(unsafe { core::slice::from_raw_parts(self.ptr, len) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> std::ops::Deref for Ptr<T> {
|
impl<T> core::ops::Deref for Ptr<T> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||||
use std::ops::{Index, IndexMut};
|
use core::ops::{Index, IndexMut};
|
||||||
|
|
||||||
use crate::core::geometry::{Vector3f, VectorLike};
|
use crate::core::geometry::{Vector3f, VectorLike};
|
||||||
use crate::utils::math::{safe_asin, sinx_over_x};
|
use crate::utils::math::{safe_asin, sinx_over_x};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::check_rare;
|
|
||||||
use crate::core::geometry::{
|
use crate::core::geometry::{
|
||||||
Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike,
|
Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike,
|
||||||
};
|
};
|
||||||
use crate::core::pbrt::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS};
|
|
||||||
use crate::utils::containers::DeviceArray2D;
|
use crate::utils::containers::DeviceArray2D;
|
||||||
use crate::utils::find_interval;
|
use crate::utils::find_interval;
|
||||||
use crate::utils::math::{
|
use crate::utils::math::{
|
||||||
|
|
@ -12,8 +10,12 @@ use crate::utils::math::{
|
||||||
use crate::utils::ptr::Ptr;
|
use crate::utils::ptr::Ptr;
|
||||||
use crate::utils::rng::Rng;
|
use crate::utils::rng::Rng;
|
||||||
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
|
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
|
||||||
|
use num_traits::Float as NumFloat;
|
||||||
use num_traits::Num;
|
use num_traits::Num;
|
||||||
|
|
||||||
|
#[cfg(feature = "cpu_debug")]
|
||||||
|
use crate::{RARE_EVENT_CONDITION_MET, RARE_EVENT_TOTAL_CALLS, check_rare};
|
||||||
|
|
||||||
pub fn linear_pdf<T>(x: T, a: T, b: T) -> T
|
pub fn linear_pdf<T>(x: T, a: T, b: T) -> T
|
||||||
where
|
where
|
||||||
T: Num + Copy + PartialOrd,
|
T: Num + Copy + PartialOrd,
|
||||||
|
|
@ -267,7 +269,7 @@ pub fn invert_spherical_rectangle_sample(
|
||||||
let b0sq = square(b0) + square(b1);
|
let b0sq = square(b0) + square(b1);
|
||||||
|
|
||||||
let solid_angle_f64: f64 =
|
let solid_angle_f64: f64 =
|
||||||
g0 as f64 + g1 as f64 + g2 as f64 + g3 as f64 - 2. * std::f64::consts::PI;
|
g0 as f64 + g1 as f64 + g2 as f64 + g3 as f64 - 2. * core::f64::consts::PI;
|
||||||
let solid_angle = solid_angle_f64 as Float;
|
let solid_angle = solid_angle_f64 as Float;
|
||||||
|
|
||||||
if solid_angle < 1e-3 {
|
if solid_angle < 1e-3 {
|
||||||
|
|
@ -289,6 +291,7 @@ pub fn invert_spherical_rectangle_sample(
|
||||||
let invcusq = 1. + z0sq / square(xu);
|
let invcusq = 1. + z0sq / square(xu);
|
||||||
let fusq = invcusq - b0sq;
|
let fusq = invcusq - b0sq;
|
||||||
let fu = fusq.sqrt().copysign(xu);
|
let fu = fusq.sqrt().copysign(xu);
|
||||||
|
#[cfg(feature = "cpu_debug")]
|
||||||
check_rare!(1e-6, fu == 0.);
|
check_rare!(1e-6, fu == 0.);
|
||||||
let sqrt = safe_sqrt(difference_of_products(b0, b0, b1, b1) + fusq);
|
let sqrt = safe_sqrt(difference_of_products(b0, b0, b1, b1) + fusq);
|
||||||
let ay = -(b1 * fu) - (b0 * sqrt).copysign(fu * b0);
|
let ay = -(b1 * fu) - (b0 * sqrt).copysign(fu * b0);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
|
use core::iter::{Product, Sum};
|
||||||
|
use core::ops::{Add, Div, Index, IndexMut, Mul};
|
||||||
use num_traits::Float as NumFloat;
|
use num_traits::Float as NumFloat;
|
||||||
use std::iter::{Product, Sum};
|
|
||||||
use std::ops::{Add, Div, Index, IndexMut, Mul};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::math::{SquareMatrix, radians, safe_acos};
|
use super::math::{SquareMatrix, radians, safe_acos};
|
||||||
use super::quaternion::Quaternion;
|
use super::quaternion::Quaternion;
|
||||||
|
|
@ -13,8 +12,7 @@ use crate::core::geometry::{
|
||||||
use crate::core::interaction::{
|
use crate::core::interaction::{
|
||||||
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SurfaceInteraction,
|
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SurfaceInteraction,
|
||||||
};
|
};
|
||||||
use crate::core::pbrt::{Float, gamma};
|
use crate::{Float, gamma};
|
||||||
use crate::utils::error::InversionError;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
|
@ -28,15 +26,15 @@ impl<T: NumFloat + Sum + Product> TransformGeneric<T> {
|
||||||
Self { m, m_inv }
|
Self { m, m_inv }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_matrix(m: SquareMatrix<T, 4>) -> Result<Self, InversionError> {
|
pub fn from_matrix(m: SquareMatrix<T, 4>) -> Option<Self> {
|
||||||
let inv = m.inverse()?;
|
let inv = m.inverse()?;
|
||||||
Ok(Self { m, m_inv: inv })
|
Some(Self { m, m_inv: inv })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_flat(flat: &[T; 16]) -> Result<Self, InversionError> {
|
pub fn from_flat(flat: &[T; 16]) -> Option<Self> {
|
||||||
let m: SquareMatrix<T, 4> = SquareMatrix::from(flat);
|
let m: SquareMatrix<T, 4> = SquareMatrix::from(flat);
|
||||||
let inv = m.inverse()?;
|
let inv = m.inverse()?;
|
||||||
Ok(Self { m, m_inv: inv })
|
Some(Self { m, m_inv: inv })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn identity() -> Self {
|
pub fn identity() -> Self {
|
||||||
|
|
@ -444,11 +442,7 @@ impl TransformGeneric<Float> {
|
||||||
Self { m, m_inv }
|
Self { m, m_inv }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perspective(
|
pub fn perspective(fov: Float, n: Float, f: Float) -> Option<TransformGeneric<Float>> {
|
||||||
fov: Float,
|
|
||||||
n: Float,
|
|
||||||
f: Float,
|
|
||||||
) -> Result<TransformGeneric<Float>, InversionError> {
|
|
||||||
let persp: SquareMatrix<Float, 4> = SquareMatrix::new([
|
let persp: SquareMatrix<Float, 4> = SquareMatrix::new([
|
||||||
[1., 0., 0., 0.],
|
[1., 0., 0., 0.],
|
||||||
[0., 1., 0., 0.],
|
[0., 1., 0., 0.],
|
||||||
|
|
@ -457,7 +451,7 @@ impl TransformGeneric<Float> {
|
||||||
]);
|
]);
|
||||||
let inv_tan_ang = 1. / (radians(fov) / 2.).tan();
|
let inv_tan_ang = 1. / (radians(fov) / 2.).tan();
|
||||||
let persp_transform = TransformGeneric::from_matrix(persp)?;
|
let persp_transform = TransformGeneric::from_matrix(persp)?;
|
||||||
Ok(TransformGeneric::scale(inv_tan_ang, inv_tan_ang, 1.) * persp_transform)
|
Some(TransformGeneric::scale(inv_tan_ang, inv_tan_ang, 1.) * persp_transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn orthographic(z_near: Float, z_far: Float) -> Self {
|
pub fn orthographic(z_near: Float, z_far: Float) -> Self {
|
||||||
|
|
@ -1971,11 +1965,11 @@ impl AnimatedTransform {
|
||||||
(c1, c2, c3, c4, c5)
|
(c1, c2, c3, c4, c5)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
AnimatedTransform {
|
AnimatedTransform {
|
||||||
|
|
@ -2093,7 +2087,7 @@ pub fn look_at(
|
||||||
pos: impl Into<Point3f>,
|
pos: impl Into<Point3f>,
|
||||||
look: impl Into<Point3f>,
|
look: impl Into<Point3f>,
|
||||||
up: impl Into<Point3f>,
|
up: impl Into<Point3f>,
|
||||||
) -> Result<TransformGeneric<Float>, InversionError> {
|
) -> Option<TransformGeneric<Float>> {
|
||||||
let mut world_from_camera: SquareMatrix<Float, 4> = SquareMatrix::default();
|
let mut world_from_camera: SquareMatrix<Float, 4> = SquareMatrix::default();
|
||||||
// Initialize fourth column of viewing matrix
|
// Initialize fourth column of viewing matrix
|
||||||
let pos: Point3f = pos.into();
|
let pos: Point3f = pos.into();
|
||||||
|
|
@ -2133,5 +2127,5 @@ pub fn look_at(
|
||||||
world_from_camera[3][2] = 0.;
|
world_from_camera[3][2] = 0.;
|
||||||
|
|
||||||
let camera_from_world = world_from_camera.inverse()?;
|
let camera_from_world = world_from_camera.inverse()?;
|
||||||
Ok(TransformGeneric::new(camera_from_world, world_from_camera))
|
Some(TransformGeneric::new(camera_from_world, world_from_camera))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
src/cameras/mod.rs
Normal file
3
src/cameras/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod perspective;
|
||||||
|
pub mod realistic;
|
||||||
|
pub mod sherical;
|
||||||
0
src/cameras/perspective.rs
Normal file
0
src/cameras/perspective.rs
Normal file
90
src/cameras/realistic.rs
Normal file
90
src/cameras/realistic.rs
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
use ash::vk::Image;
|
||||||
|
use shared::cameras::{Mapping, RealisticCamera, realistic::LensElementInterface};
|
||||||
|
|
||||||
|
struct RealisticCameraData {
|
||||||
|
aperture_image: Arc<Image>,
|
||||||
|
element_interfaces: Vec<LensElementInterface>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RealisticCameraHost {
|
||||||
|
device: RealisticCamera,
|
||||||
|
data: RealisticCameraData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RealisticCameraHost {
|
||||||
|
pub fn new(
|
||||||
|
base: CameraBase,
|
||||||
|
lens_params: &[Float],
|
||||||
|
focus_distance: Float,
|
||||||
|
set_aperture_diameter: Float,
|
||||||
|
aperture_image: Arc<Image>,
|
||||||
|
) -> Self {
|
||||||
|
let film_ptr = base.film;
|
||||||
|
if film_ptr.is_null() {
|
||||||
|
panic!("Camera must have a film");
|
||||||
|
}
|
||||||
|
let film = &*film_ptr;
|
||||||
|
|
||||||
|
let aspect = film.full_resolution().x() as Float / film.full_resolution().y() as Float;
|
||||||
|
let diagonal = film.diagonal();
|
||||||
|
let x = (square(diagonal) / (1.0 + square(diagonal))).sqrt();
|
||||||
|
let y = x * aspect;
|
||||||
|
let physical_extent =
|
||||||
|
Bounds2f::from_points(Point2f::new(-x / 2., -y / 2.), Point2f::new(x / 2., y / 2.));
|
||||||
|
let mut element_interface: Vec<LensElementInterface> = Vec::new();
|
||||||
|
|
||||||
|
for i in (0..lens_params.len()).step_by(4) {
|
||||||
|
let curvature_radius = lens_params[i] / 1000.0;
|
||||||
|
let thickness = lens_params[i + 1] / 1000.0;
|
||||||
|
let eta = lens_params[i + 2];
|
||||||
|
let mut aperture_diameter = lens_params[i + 3] / 1000.0;
|
||||||
|
|
||||||
|
if curvature_radius == 0.0 {
|
||||||
|
aperture_diameter /= 1000.0;
|
||||||
|
if set_aperture_diameter > aperture_diameter {
|
||||||
|
println!("Aperture is larger than possible")
|
||||||
|
} else {
|
||||||
|
aperture_diameter = set_aperture_diameter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let el_int = LensElementInterface {
|
||||||
|
curvature_radius,
|
||||||
|
thickness,
|
||||||
|
eta,
|
||||||
|
aperture_radius: aperture_diameter / 2.0,
|
||||||
|
};
|
||||||
|
element_interface.push(el_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
let half_diag = film.diagonal() / 2.0;
|
||||||
|
let mut exit_pupil_bounds = [Bounds2f::default(); EXIT_PUPIL_SAMPLES];
|
||||||
|
|
||||||
|
for i in 0..EXIT_PUPIL_SAMPLES {
|
||||||
|
let r0 = (i as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
||||||
|
let r1 = ((i + 1) as Float / EXIT_PUPIL_SAMPLES as Float) * half_diag;
|
||||||
|
exit_pupil_bounds[i] = Self::compute_exit_pupil_bounds(&element_interface, r0, r1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let n_elements = element_interface.len();
|
||||||
|
let element_interfaces = element_interface.as_ptr();
|
||||||
|
std::mem::forget(element_interface);
|
||||||
|
|
||||||
|
let data = RealisticCameraData {
|
||||||
|
element_interfaces,
|
||||||
|
aperture_image,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device = RealisticCamera {
|
||||||
|
base,
|
||||||
|
focus_distance,
|
||||||
|
element_interfaces: Ptr::from(element_interfaces),
|
||||||
|
n_elements,
|
||||||
|
physical_extent,
|
||||||
|
set_aperture_diameter,
|
||||||
|
aperture_image,
|
||||||
|
exit_pupil_bounds,
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { device, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
0
src/cameras/spherical.rs
Normal file
0
src/cameras/spherical.rs
Normal file
|
|
@ -34,9 +34,9 @@ impl CameraBaseParameters {
|
||||||
medium: Arc<Medium>,
|
medium: Arc<Medium>,
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Self {
|
) -> Result<Self> {
|
||||||
let mut shutter_open = params.get_one_float("shutteropen", 0.);
|
let mut shutter_open = params.get_one_float("shutteropen", 0.)?;
|
||||||
let mut shutter_close = params.get_one_float("shutterclose", 1.);
|
let mut shutter_close = params.get_one_float("shutterclose", 1.)?;
|
||||||
if shutter_close < shutter_open {
|
if shutter_close < shutter_open {
|
||||||
eprint!(
|
eprint!(
|
||||||
"{}: Shutter close time {} < shutter open {}. Swapping",
|
"{}: Shutter close time {} < shutter open {}. Swapping",
|
||||||
|
|
@ -44,13 +44,14 @@ impl CameraBaseParameters {
|
||||||
);
|
);
|
||||||
std::mem::swap(&mut shutter_open, &mut shutter_close);
|
std::mem::swap(&mut shutter_open, &mut shutter_close);
|
||||||
}
|
}
|
||||||
CameraBaseParameters {
|
|
||||||
|
Ok(CameraBaseParameters {
|
||||||
camera_transform: camera_transform.clone(),
|
camera_transform: camera_transform.clone(),
|
||||||
shutter_open,
|
shutter_open,
|
||||||
shutter_close,
|
shutter_close,
|
||||||
film,
|
film,
|
||||||
medium,
|
medium,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,14 +121,14 @@ impl CameraFactory for Camera {
|
||||||
"perspective" => {
|
"perspective" => {
|
||||||
let full_res = film.full_resolution();
|
let full_res = film.full_resolution();
|
||||||
let camera_params =
|
let camera_params =
|
||||||
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc);
|
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?;
|
||||||
let base = CameraBase::create(camera_params);
|
let base = CameraBase::create(camera_params);
|
||||||
let lens_radius = params.get_one_float("lensradius", 0.);
|
let lens_radius = params.get_one_float("lensradius", 0.)?;
|
||||||
let focal_distance = params.get_one_float("focaldistance", 1e6);
|
let focal_distance = params.get_one_float("focaldistance", 1e6)?;
|
||||||
let frame = params.get_one_float(
|
let frame = params.get_one_float(
|
||||||
"frameaspectratio",
|
"frameaspectratio",
|
||||||
full_res.x() as Float / full_res.y() as Float,
|
full_res.x() as Float / full_res.y() as Float,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let mut screen = if frame > 1. {
|
let mut screen = if frame > 1. {
|
||||||
Bounds2f::from_points(Point2f::new(-frame, -1.), Point2f::new(frame, 1.))
|
Bounds2f::from_points(Point2f::new(-frame, -1.), Point2f::new(frame, 1.))
|
||||||
|
|
@ -138,7 +139,7 @@ impl CameraFactory for Camera {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let sw = params.get_float_array("screenwindow");
|
let sw = params.get_float_array("screenwindow")?;
|
||||||
if !sw.is_empty() {
|
if !sw.is_empty() {
|
||||||
if get_options().fullscreen {
|
if get_options().fullscreen {
|
||||||
eprint!("Screenwindow is ignored in fullscreen mode");
|
eprint!("Screenwindow is ignored in fullscreen mode");
|
||||||
|
|
@ -157,7 +158,7 @@ impl CameraFactory for Camera {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fov = params.get_one_float("fov", 90.);
|
let fov = params.get_one_float("fov", 90.)?;
|
||||||
let camera = PerspectiveCamera::new(base, fov, screen, lens_radius, focal_distance);
|
let camera = PerspectiveCamera::new(base, fov, screen, lens_radius, focal_distance);
|
||||||
arena.alloc(camera);
|
arena.alloc(camera);
|
||||||
|
|
||||||
|
|
@ -167,14 +168,14 @@ impl CameraFactory for Camera {
|
||||||
"orthographic" => {
|
"orthographic" => {
|
||||||
let full_res = film.full_resolution();
|
let full_res = film.full_resolution();
|
||||||
let camera_params =
|
let camera_params =
|
||||||
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc);
|
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?;
|
||||||
let base = CameraBase::create(camera_params);
|
let base = CameraBase::create(camera_params);
|
||||||
let lens_radius = params.get_one_float("lensradius", 0.);
|
let lens_radius = params.get_one_float("lensradius", 0.)?;
|
||||||
let focal_distance = params.get_one_float("focaldistance", 1e6);
|
let focal_distance = params.get_one_float("focaldistance", 1e6)?;
|
||||||
let frame = params.get_one_float(
|
let frame = params.get_one_float(
|
||||||
"frameaspectratio",
|
"frameaspectratio",
|
||||||
full_res.x() as Float / full_res.y() as Float,
|
full_res.x() as Float / full_res.y() as Float,
|
||||||
);
|
)?;
|
||||||
|
|
||||||
let mut screen = if frame > 1. {
|
let mut screen = if frame > 1. {
|
||||||
Bounds2f::from_points(Point2f::new(-frame, -1.), Point2f::new(frame, 1.))
|
Bounds2f::from_points(Point2f::new(-frame, -1.), Point2f::new(frame, 1.))
|
||||||
|
|
@ -185,7 +186,7 @@ impl CameraFactory for Camera {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let sw = params.get_float_array("screenwindow");
|
let sw = params.get_float_array("screenwindow")?;
|
||||||
if !sw.is_empty() {
|
if !sw.is_empty() {
|
||||||
if get_options().fullscreen {
|
if get_options().fullscreen {
|
||||||
eprint!("Screenwindow is ignored in fullscreen mode");
|
eprint!("Screenwindow is ignored in fullscreen mode");
|
||||||
|
|
@ -209,11 +210,11 @@ impl CameraFactory for Camera {
|
||||||
}
|
}
|
||||||
"realistic" => {
|
"realistic" => {
|
||||||
let camera_params =
|
let camera_params =
|
||||||
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc);
|
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?;
|
||||||
let base = CameraBase::create(camera_params);
|
let base = CameraBase::create(camera_params);
|
||||||
let aperture_diameter = params.get_one_float("aperturediameter", 1.);
|
let aperture_diameter = params.get_one_float("aperturediameter", 1.)?;
|
||||||
let focal_distance = params.get_one_float("focaldistance", 10.);
|
let focal_distance = params.get_one_float("focaldistance", 10.)?;
|
||||||
let lens_file = params.get_one_string("lensfile", "");
|
let lens_file = params.get_one_string("lensfile", "")?;
|
||||||
|
|
||||||
if lens_file.is_empty() {
|
if lens_file.is_empty() {
|
||||||
return Err(anyhow!("{}: No lens file supplied", loc));
|
return Err(anyhow!("{}: No lens file supplied", loc));
|
||||||
|
|
@ -275,7 +276,7 @@ impl CameraFactory for Camera {
|
||||||
image
|
image
|
||||||
};
|
};
|
||||||
|
|
||||||
let aperture_name = params.get_one_string("aperture", "");
|
let aperture_name = params.get_one_string("aperture", "")?;
|
||||||
let mut aperture_image: Option<Image> = None;
|
let mut aperture_image: Option<Image> = None;
|
||||||
|
|
||||||
if !aperture_name.is_empty() {
|
if !aperture_name.is_empty() {
|
||||||
|
|
@ -389,44 +390,9 @@ impl CameraFactory for Camera {
|
||||||
"spherical" => {
|
"spherical" => {
|
||||||
let full_res = film.full_resolution();
|
let full_res = film.full_resolution();
|
||||||
let camera_params =
|
let camera_params =
|
||||||
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc);
|
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?;
|
||||||
let base = CameraBase::create(camera_params);
|
let base = CameraBase::create(camera_params);
|
||||||
let lens_radius = params.get_one_float("lensradius", 0.);
|
let m = params.get_one_string("mapping", "equalarea")?;
|
||||||
let focal_distance = params.get_one_float("focaldistance", 1e30);
|
|
||||||
let frame = params.get_one_float(
|
|
||||||
"frameaspectratio",
|
|
||||||
full_res.x() as Float / full_res.y() as Float,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut screen = if frame > 1. {
|
|
||||||
Bounds2f::from_points(Point2f::new(-frame, -1.), Point2f::new(frame, 1.))
|
|
||||||
} else {
|
|
||||||
Bounds2f::from_points(
|
|
||||||
Point2f::new(-1., -1. / frame),
|
|
||||||
Point2f::new(1., 1. / frame),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let sw = params.get_float_array("screenwindow");
|
|
||||||
if !sw.is_empty() {
|
|
||||||
if get_options().fullscreen {
|
|
||||||
eprint!("Screenwindow is ignored in fullscreen mode");
|
|
||||||
} else {
|
|
||||||
if sw.len() == 4 {
|
|
||||||
screen = Bounds2f::from_points(
|
|
||||||
Point2f::new(sw[0], sw[2]),
|
|
||||||
Point2f::new(sw[1], sw[3]),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"{}: screenwindow param must have four values",
|
|
||||||
loc
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let m = params.get_one_string("mapping", "equalarea");
|
|
||||||
let mapping = match m.as_str() {
|
let mapping = match m.as_str() {
|
||||||
"equalarea" => Mapping::EqualArea,
|
"equalarea" => Mapping::EqualArea,
|
||||||
"equirectangular" => Mapping::EquiRectangular,
|
"equirectangular" => Mapping::EquiRectangular,
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,9 @@ impl PixelSensor {
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
let iso = params.get_one_float("iso", 100.);
|
let iso = params.get_one_float("iso", 100.)?;
|
||||||
let mut white_balance_temp = params.get_one_float("whitebalance", 0.);
|
let mut white_balance_temp = params.get_one_float("whitebalance", 0.)?;
|
||||||
let sensor_name = params.get_one_string("sensor", "cie1931");
|
let sensor_name = params.get_one_string("sensor", "cie1931")?;
|
||||||
if sensor_name != "cie1931" && white_balance_temp == 0. {
|
if sensor_name != "cie1931" && white_balance_temp == 0. {
|
||||||
white_balance_temp = 6500.;
|
white_balance_temp = 6500.;
|
||||||
}
|
}
|
||||||
|
|
@ -238,7 +238,9 @@ pub trait CreateFilmBase {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
sensor: Option<&DevicePixelSensor>,
|
sensor: Option<&DevicePixelSensor>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Self;
|
) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateFilmBase for FilmBase {
|
impl CreateFilmBase for FilmBase {
|
||||||
|
|
@ -247,9 +249,12 @@ impl CreateFilmBase for FilmBase {
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
sensor: Option<&DevicePixelSensor>,
|
sensor: Option<&DevicePixelSensor>,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Self {
|
) -> Result<Self>
|
||||||
let x_res = params.get_one_int("xresolution", 1280);
|
where
|
||||||
let y_res = params.get_one_int("yresolution", 720);
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let x_res = params.get_one_int("xresolution", 1280)?;
|
||||||
|
let y_res = params.get_one_int("yresolution", 720)?;
|
||||||
|
|
||||||
if x_res <= 0 || y_res <= 0 {
|
if x_res <= 0 || y_res <= 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
|
|
@ -259,7 +264,7 @@ impl CreateFilmBase for FilmBase {
|
||||||
}
|
}
|
||||||
let full_resolution = Point2i::new(x_res.max(1), y_res.max(1));
|
let full_resolution = Point2i::new(x_res.max(1), y_res.max(1));
|
||||||
|
|
||||||
let crop_data = params.get_float_array("cropwindow");
|
let crop_data = params.get_float_array("cropwindow")?;
|
||||||
let crop = if crop_data.len() == 4 {
|
let crop = if crop_data.len() == 4 {
|
||||||
Bounds2f::from_points(
|
Bounds2f::from_points(
|
||||||
Point2f::new(crop_data[0], crop_data[2]),
|
Point2f::new(crop_data[0], crop_data[2]),
|
||||||
|
|
@ -288,16 +293,16 @@ impl CreateFilmBase for FilmBase {
|
||||||
let expansion = Point2i::new(rad.x().ceil() as i32, rad.y().ceil() as i32);
|
let expansion = Point2i::new(rad.x().ceil() as i32, rad.y().ceil() as i32);
|
||||||
pixel_bounds = pixel_bounds.expand(expansion);
|
pixel_bounds = pixel_bounds.expand(expansion);
|
||||||
|
|
||||||
let diagonal_mm = params.get_one_float("diagonal", 35.0);
|
let diagonal_mm = params.get_one_float("diagonal", 35.0)?;
|
||||||
// let filename = params.get_one_string("filename", "pbrt.exr");
|
// let filename = params.get_one_string("filename", "pbrt.exr");
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
full_resolution,
|
full_resolution,
|
||||||
pixel_bounds,
|
pixel_bounds,
|
||||||
filter,
|
filter,
|
||||||
diagonal: diagonal_mm * 0.001,
|
diagonal: diagonal_mm * 0.001,
|
||||||
sensor: Ptr::from(sensor.unwrap()),
|
sensor: Ptr::from(sensor.unwrap()),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,53 +2,54 @@ use crate::filters::*;
|
||||||
use crate::utils::containers::Array2D;
|
use crate::utils::containers::Array2D;
|
||||||
use crate::utils::sampling::PiecewiseConstant2D;
|
use crate::utils::sampling::PiecewiseConstant2D;
|
||||||
use crate::utils::{FileLoc, ParameterDictionary};
|
use crate::utils::{FileLoc, ParameterDictionary};
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::filter::{Filter, FilterSampler};
|
use shared::core::filter::{Filter, FilterSampler};
|
||||||
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
||||||
use shared::filters::*;
|
use shared::filters::*;
|
||||||
|
|
||||||
pub trait FilterFactory {
|
pub trait FilterFactory {
|
||||||
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Filter, String>;
|
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Filter>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterFactory for Filter {
|
impl FilterFactory for Filter {
|
||||||
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Self, String> {
|
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Self> {
|
||||||
match name {
|
match name {
|
||||||
"box" => {
|
"box" => {
|
||||||
let xw = params.get_one_float("xradius", 0.5);
|
let xw = params.get_one_float("xradius", 0.5)?;
|
||||||
let yw = params.get_one_float("yradius", 0.5);
|
let yw = params.get_one_float("yradius", 0.5)?;
|
||||||
let filter = BoxFilter::new(Vector2f::new(xw, yw));
|
let filter = BoxFilter::new(Vector2f::new(xw, yw));
|
||||||
Ok(Filter::Box(filter))
|
Ok(Filter::Box(filter))
|
||||||
}
|
}
|
||||||
"gaussian" => {
|
"gaussian" => {
|
||||||
let xw = params.get_one_float("xradius", 1.5);
|
let xw = params.get_one_float("xradius", 1.5)?;
|
||||||
let yw = params.get_one_float("yradius", 1.5);
|
let yw = params.get_one_float("yradius", 1.5)?;
|
||||||
let sigma = params.get_one_float("sigma", 0.5);
|
let sigma = params.get_one_float("sigma", 0.5)?;
|
||||||
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
||||||
Ok(Filter::Gaussian(filter))
|
Ok(Filter::Gaussian(filter))
|
||||||
}
|
}
|
||||||
"mitchell" => {
|
"mitchell" => {
|
||||||
let xw = params.get_one_float("xradius", 2.);
|
let xw = params.get_one_float("xradius", 2.)?;
|
||||||
let yw = params.get_one_float("yradius", 2.);
|
let yw = params.get_one_float("yradius", 2.)?;
|
||||||
let b = params.get_one_float("B", 1. / 3.);
|
let b = params.get_one_float("B", 1. / 3.)?;
|
||||||
let c = params.get_one_float("C", 1. / 3.);
|
let c = params.get_one_float("C", 1. / 3.)?;
|
||||||
let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
|
let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
|
||||||
Ok(Filter::Mitchell(filter))
|
Ok(Filter::Mitchell(filter))
|
||||||
}
|
}
|
||||||
"sinc" => {
|
"sinc" => {
|
||||||
let xw = params.get_one_float("xradius", 4.);
|
let xw = params.get_one_float("xradius", 4.)?;
|
||||||
let yw = params.get_one_float("yradius", 4.);
|
let yw = params.get_one_float("yradius", 4.)?;
|
||||||
let tau = params.get_one_float("tau", 3.);
|
let tau = params.get_one_float("tau", 3.)?;
|
||||||
let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
|
let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
|
||||||
Ok(Filter::LanczosSinc(filter))
|
Ok(Filter::LanczosSinc(filter))
|
||||||
}
|
}
|
||||||
"triangle" => {
|
"triangle" => {
|
||||||
let xw = params.get_one_float("xradius", 2.);
|
let xw = params.get_one_float("xradius", 2.)?;
|
||||||
let yw = params.get_one_float("yradius", 2.);
|
let yw = params.get_one_float("yradius", 2.)?;
|
||||||
let filter = TriangleFilter::new(Vector2f::new(xw, yw));
|
let filter = TriangleFilter::new(Vector2f::new(xw, yw));
|
||||||
Ok(Filter::Triangle(filter))
|
Ok(Filter::Triangle(filter))
|
||||||
}
|
}
|
||||||
_ => Err(format!("Film type '{}' unknown at {}", name, loc)),
|
_ => Err(anyhow!("Film type '{}' unknown at {}", name, loc)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
71
src/core/interaction.rs
Normal file
71
src/core/interaction.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
use crate::globals::get_options;
|
||||||
|
use shared::core::interaction::{
|
||||||
|
InteractionBase, InteractionTrait, MediumInteraction, SimpleInteraction, SurfaceInteraction,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait InteractionGetter {
|
||||||
|
fn get_bsdf(
|
||||||
|
&mut self,
|
||||||
|
r: &Ray,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
camera: &Camera,
|
||||||
|
sampler: &mut Sampler,
|
||||||
|
) -> Option<BSDF> {
|
||||||
|
self.compute_differentials(r, camera, sampler.samples_per_pixel() as i32);
|
||||||
|
|
||||||
|
let material = {
|
||||||
|
let root_mat = self.material;
|
||||||
|
let mut active_mat: &Material = &*root_mat;
|
||||||
|
let tex_eval = UniversalTextureEvaluator;
|
||||||
|
while let Material::Mix(mix) = active_mat {
|
||||||
|
// We need a context to evaluate the 'amount' texture
|
||||||
|
let ctx = MaterialEvalContext::from(&*self);
|
||||||
|
active_mat = mix.choose_material(&tex_eval, &ctx)?;
|
||||||
|
}
|
||||||
|
active_mat.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctx = MaterialEvalContext::from(&*self);
|
||||||
|
let tex_eval = UniversalTextureEvaluator;
|
||||||
|
let displacement = material.get_displacement();
|
||||||
|
let normal_map = Ptr::from(material.get_normal_map().unwrap());
|
||||||
|
if !displacement.is_null() || !normal_map.is_null() {
|
||||||
|
// This calls the function defined above
|
||||||
|
self.compute_bump_geom(&tex_eval, displacement, normal_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut bsdf = material.get_bsdf(&tex_eval, &ctx, lambda);
|
||||||
|
if get_options().force_diffuse {
|
||||||
|
let r = bsdf.rho_wo(self.common.wo, &[sampler.get1d()], &[sampler.get2d()]);
|
||||||
|
let diff_bxdf = BxDF::Diffuse(DiffuseBxDF::new(r));
|
||||||
|
bsdf = BSDF::new(self.shading.n, self.shading.dpdu, Ptr::from(&diff_bxdf));
|
||||||
|
}
|
||||||
|
Some(bsdf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_bssrdf(
|
||||||
|
&self,
|
||||||
|
_ray: &Ray,
|
||||||
|
lambda: &SampledWavelengths,
|
||||||
|
_camera: &Camera,
|
||||||
|
) -> Option<BSSRDF> {
|
||||||
|
let material = {
|
||||||
|
let mut active_mat = unsafe { self.material.as_ref() };
|
||||||
|
let tex_eval = UniversalTextureEvaluator;
|
||||||
|
while let Material::Mix(mix) = active_mat {
|
||||||
|
// We need a context to evaluate the 'amount' texture
|
||||||
|
let ctx = MaterialEvalContext::from(self);
|
||||||
|
active_mat = mix.choose_material(&tex_eval, &ctx)?;
|
||||||
|
}
|
||||||
|
active_mat.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctx = MaterialEvalContext::from(self);
|
||||||
|
let tex_eval = UniversalTextureEvaluator;
|
||||||
|
material.get_bssrdf(&tex_eval, &ctx, lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractionGetter for SurfaceInteraction {}
|
||||||
|
impl InteractionGetter for MediumInteraction {}
|
||||||
|
impl InteractionGetter for SimpleInteraction {}
|
||||||
|
|
@ -5,6 +5,7 @@ pub mod color;
|
||||||
pub mod film;
|
pub mod film;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
pub mod interaction;
|
||||||
pub mod light;
|
pub mod light;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod medium;
|
pub mod medium;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@ use crate::Arena;
|
||||||
use crate::spectra::get_colorspace_device;
|
use crate::spectra::get_colorspace_device;
|
||||||
use crate::utils::error::FileLoc;
|
use crate::utils::error::FileLoc;
|
||||||
use crate::utils::normalize_utf8;
|
use crate::utils::normalize_utf8;
|
||||||
use crate::utils::parameters::error_exit;
|
|
||||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||||
use crate::utils::parser::ParserTarget;
|
use crate::utils::parser::{ParserError, ParserTarget};
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
use shared::core::geometry::Vector3f;
|
use shared::core::geometry::Vector3f;
|
||||||
|
|
@ -170,26 +169,50 @@ impl BasicSceneBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_world(&self, name: &str, loc: &FileLoc) {
|
fn verify_world(&self, name: &str, loc: &FileLoc) -> Result<(), ParserError> {
|
||||||
if self.current_block != BlockState::WorldBlock {
|
if self.current_block != BlockState::WorldBlock {
|
||||||
log::error!("{}: {} not allowed outside WorldBlock", loc, name);
|
return Err(ParserError::Generic(
|
||||||
|
format!("{} not allowed outside WorldBlock", name),
|
||||||
|
loc.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_options(&self, name: &str, loc: &FileLoc) {
|
fn verify_options(&self, name: &str, loc: &FileLoc) -> Result<(), ParserError> {
|
||||||
if self.current_block != BlockState::OptionsBlock {
|
if self.current_block != BlockState::OptionsBlock {
|
||||||
log::error!("{}: {} not allowed inside WorldBlock", loc, name);
|
return Err(ParserError::Generic(
|
||||||
|
format!("{} not allowed inside WorldBlock", name),
|
||||||
|
loc.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_params(
|
||||||
|
&self,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: &FileLoc,
|
||||||
|
) -> Result<ParameterDictionary, ParserError> {
|
||||||
|
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone())
|
||||||
|
.map_err(|e| ParserError::Generic(format!("parameter error: {}", e), loc.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<anyhow::Error> for ParserError {
|
||||||
|
fn from(e: anyhow::Error) -> Self {
|
||||||
|
ParserError::Generic(e.to_string(), FileLoc::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserTarget for BasicSceneBuilder {
|
impl ParserTarget for BasicSceneBuilder {
|
||||||
fn reverse_orientation(&mut self, loc: FileLoc) {
|
fn reverse_orientation(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.verify_world("ReverseOrientation", &loc);
|
self.verify_world("ReverseOrientation", &loc)?;
|
||||||
self.graphics_state.reverse_orientation = !self.graphics_state.reverse_orientation;
|
self.graphics_state.reverse_orientation = !self.graphics_state.reverse_orientation;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_space(&mut self, name: &str, loc: FileLoc) {
|
fn color_space(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
let stdcs = get_colorspace_device();
|
let stdcs = get_colorspace_device();
|
||||||
let _ = match stdcs.get_named(name) {
|
let _ = match stdcs.get_named(name) {
|
||||||
Ok(cs) => {
|
Ok(cs) => {
|
||||||
|
|
@ -199,25 +222,43 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
eprintln!("Error: Color space '{}' unknown at {}", name, loc);
|
eprintln!("Error: Color space '{}' unknown at {}", name, loc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identity(&mut self, _loc: FileLoc) {
|
fn identity(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.for_active_transforms(|_| Transform::identity());
|
self.for_active_transforms(|_| Transform::identity());
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate(&mut self, dx: Float, dy: Float, dz: Float, _loc: FileLoc) {
|
fn translate(
|
||||||
|
&mut self,
|
||||||
|
dx: Float,
|
||||||
|
dy: Float,
|
||||||
|
dz: Float,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
let t = Transform::translate(Vector3f::new(dx, dy, dz));
|
let t = Transform::translate(Vector3f::new(dx, dy, dz));
|
||||||
self.for_active_transforms(|cur| cur * &t);
|
self.for_active_transforms(|cur| cur * &t);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate(&mut self, angle: Float, ax: Float, ay: Float, az: Float, _loc: FileLoc) {
|
fn rotate(
|
||||||
|
&mut self,
|
||||||
|
angle: Float,
|
||||||
|
ax: Float,
|
||||||
|
ay: Float,
|
||||||
|
az: Float,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
let t = Transform::rotate_around_axis(angle, Vector3f::new(ax, ay, az));
|
let t = Transform::rotate_around_axis(angle, Vector3f::new(ax, ay, az));
|
||||||
self.for_active_transforms(|cur| cur * &t);
|
self.for_active_transforms(|cur| cur * &t);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale(&mut self, sx: Float, sy: Float, sz: Float, _loc: FileLoc) {
|
fn scale(&mut self, sx: Float, sy: Float, sz: Float, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
let t = Transform::scale(sx, sy, sz);
|
let t = Transform::scale(sx, sy, sz);
|
||||||
self.for_active_transforms(|cur| cur * &t);
|
self.for_active_transforms(|cur| cur * &t);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn look_at(
|
fn look_at(
|
||||||
|
|
@ -232,7 +273,7 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
uy: Float,
|
uy: Float,
|
||||||
uz: Float,
|
uz: Float,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
) {
|
) -> Result<(), ParserError> {
|
||||||
let result = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz));
|
let result = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz));
|
||||||
match result {
|
match result {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
|
|
@ -242,9 +283,10 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
eprintln!("Error: {} at {}", e, loc);
|
eprintln!("Error: {} at {}", e, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn concat_transform(&mut self, m: &[Float; 16], loc: FileLoc) {
|
fn concat_transform(&mut self, m: &[Float; 16], loc: FileLoc) -> Result<(), ParserError> {
|
||||||
let result = Transform::from_flat(m);
|
let result = Transform::from_flat(m);
|
||||||
match result {
|
match result {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
|
|
@ -254,9 +296,10 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
eprintln!("Error: {} at {}", e, loc);
|
eprintln!("Error: {} at {}", e, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(&mut self, m: &[Float; 16], loc: FileLoc) {
|
fn transform(&mut self, m: &[Float; 16], loc: FileLoc) -> Result<(), ParserError> {
|
||||||
let result = Transform::from_flat(m);
|
let result = Transform::from_flat(m);
|
||||||
match result {
|
match result {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
|
|
@ -266,14 +309,16 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
eprintln!("Error: {} at {}", e, loc);
|
eprintln!("Error: {} at {}", e, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coordinate_system(&mut self, name: &str, _loc: FileLoc) {
|
fn coordinate_system(&mut self, name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.named_coordinate_systems
|
self.named_coordinate_systems
|
||||||
.insert(name.to_string(), self.graphics_state.ctm);
|
.insert(name.to_string(), self.graphics_state.ctm);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coord_sys_transform(&mut self, name: &str, loc: FileLoc) {
|
fn coord_sys_transform(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
if let Some(saved_ctm) = self.named_coordinate_systems.get(name) {
|
if let Some(saved_ctm) = self.named_coordinate_systems.get(name) {
|
||||||
self.graphics_state.ctm = *saved_ctm;
|
self.graphics_state.ctm = *saved_ctm;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -282,10 +327,16 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
name, loc
|
name, loc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn camera(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn camera(
|
||||||
self.verify_options("Camera", &loc);
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("Camera", &loc)?;
|
||||||
|
|
||||||
let camera_from_world = self.graphics_state.ctm;
|
let camera_from_world = self.graphics_state.ctm;
|
||||||
|
|
||||||
|
|
@ -306,8 +357,7 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
CameraTransform::from_world(animated_world_from_cam, rendering_space);
|
CameraTransform::from_world(animated_world_from_cam, rendering_space);
|
||||||
self.render_from_world = camera_from_world.t[0];
|
self.render_from_world = camera_from_world.t[0];
|
||||||
|
|
||||||
let parameters =
|
let parameters = self.make_params(¶ms.clone(), &loc)?;
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
|
||||||
|
|
||||||
self.current_camera = Some(CameraSceneEntity {
|
self.current_camera = Some(CameraSceneEntity {
|
||||||
base: SceneEntity {
|
base: SceneEntity {
|
||||||
|
|
@ -319,85 +369,119 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
|
|
||||||
medium: self.graphics_state.current_outside_medium.clone(),
|
medium: self.graphics_state.current_outside_medium.clone(),
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_transform_all(&mut self, _loc: FileLoc) {
|
fn active_transform_all(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.graphics_state.active_transform_bits = Self::ALL_TRANSFORM_BITS;
|
self.graphics_state.active_transform_bits = Self::ALL_TRANSFORM_BITS;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_transform_end_time(&mut self, _loc: FileLoc) {
|
fn active_transform_end_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.graphics_state.active_transform_bits = Self::END_TRANSFORM_BITS;
|
self.graphics_state.active_transform_bits = Self::END_TRANSFORM_BITS;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn active_transform_start_time(&mut self, _loc: FileLoc) {
|
fn active_transform_start_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.graphics_state.active_transform_bits = Self::START_TRANSFORM_BITS;
|
self.graphics_state.active_transform_bits = Self::START_TRANSFORM_BITS;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_times(&mut self, start: Float, end: Float, loc: FileLoc) {
|
fn transform_times(
|
||||||
self.verify_options("TransformTimes", &loc);
|
&mut self,
|
||||||
|
start: Float,
|
||||||
|
end: Float,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("TransformTimes", &loc)?;
|
||||||
self.graphics_state.transform_start_time = start;
|
self.graphics_state.transform_start_time = start;
|
||||||
self.graphics_state.transform_end_time = end;
|
self.graphics_state.transform_end_time = end;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn option(&mut self, _name: &str, _value: &str, _loc: FileLoc) {
|
fn option(&mut self, _name: &str, _value: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pixel_filter(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn pixel_filter(
|
||||||
let parameters =
|
&mut self,
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
name: &str,
|
||||||
self.verify_options("PixelFilter", &loc);
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("PixelFilter", &loc)?;
|
||||||
|
let parameters = self.make_params(params, &loc)?;
|
||||||
self.current_filter = Some(SceneEntity {
|
self.current_filter = Some(SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters,
|
parameters,
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn film(&mut self, type_name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn film(
|
||||||
let parameters =
|
&mut self,
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
type_name: &str,
|
||||||
self.verify_options("Film", &loc);
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("Film", &loc)?;
|
||||||
|
let parameters = self.make_params(params, &loc)?;
|
||||||
self.current_filter = Some(SceneEntity {
|
self.current_filter = Some(SceneEntity {
|
||||||
name: type_name.to_string(),
|
name: type_name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters,
|
parameters,
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn accelerator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn accelerator(
|
||||||
let parameters =
|
&mut self,
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
name: &str,
|
||||||
self.verify_options("PixelFilter", &loc);
|
params: &ParsedParameterVector,
|
||||||
self.current_filter = Some(SceneEntity {
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("Accelerator", &loc)?;
|
||||||
|
let parameters = self.make_params(params, &loc)?;
|
||||||
|
self.current_accelerator = Some(SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters,
|
parameters,
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn integrator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn integrator(
|
||||||
let parameters =
|
&mut self,
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
name: &str,
|
||||||
self.verify_options("PixelFilter", &loc);
|
params: &ParsedParameterVector,
|
||||||
self.current_filter = Some(SceneEntity {
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("Integrator", &loc)?;
|
||||||
|
let parameters = self.make_params(params, &loc)?;
|
||||||
|
self.current_integrator = Some(SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters,
|
parameters,
|
||||||
});
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_named_medium(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn make_named_medium(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_world("MakeNamedMaterial", &loc)?;
|
||||||
let curr_name = normalize_utf8(name);
|
let curr_name = normalize_utf8(name);
|
||||||
self.verify_world("MakeNamedMaterial", &loc);
|
|
||||||
|
|
||||||
if !self.named_material_names.insert(curr_name.to_string()) {
|
if !self.named_material_names.insert(curr_name.to_string()) {
|
||||||
eprintln!("Error: {}: named material '{}' redefined.", loc, name);
|
return Err(ParserError::Generic(
|
||||||
return;
|
format!("Named material '{}' redefined.", name),
|
||||||
|
loc,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
let parameters = self.make_params(params, &loc)?;
|
||||||
let parameters =
|
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
|
||||||
|
|
||||||
let entity = SceneEntity {
|
let entity = SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
|
|
@ -405,29 +489,41 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.scene.add_named_material(&curr_name, entity);
|
self.scene.add_named_material(&curr_name, entity);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn medium_interface(&mut self, inside_name: &str, outside_name: &str, _loc: FileLoc) {
|
fn medium_interface(
|
||||||
|
&mut self,
|
||||||
|
inside_name: &str,
|
||||||
|
outside_name: &str,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
let inside = normalize_utf8(inside_name);
|
let inside = normalize_utf8(inside_name);
|
||||||
let outside = normalize_utf8(outside_name);
|
let outside = normalize_utf8(outside_name);
|
||||||
|
|
||||||
self.graphics_state.current_inside_medium = inside;
|
self.graphics_state.current_inside_medium = inside;
|
||||||
self.graphics_state.current_outside_medium = outside;
|
self.graphics_state.current_outside_medium = outside;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sampler(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn sampler(
|
||||||
let parameters =
|
&mut self,
|
||||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
name: &str,
|
||||||
self.verify_options("Sampler", &loc);
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
self.verify_options("Sampler", &loc)?;
|
||||||
|
let parameters = self.make_params(¶ms.clone(), &loc)?;
|
||||||
self.current_sampler = Some(SceneEntity {
|
self.current_sampler = Some(SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters,
|
parameters,
|
||||||
})
|
});
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) {
|
fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) -> Result<(), ParserError> {
|
||||||
self.verify_options("WorldBegin", &loc);
|
self.verify_options("WorldBegin", &loc)?;
|
||||||
self.current_block = BlockState::WorldBlock;
|
self.current_block = BlockState::WorldBlock;
|
||||||
for i in 0..MAX_TRANSFORMS {
|
for i in 0..MAX_TRANSFORMS {
|
||||||
self.graphics_state.ctm[i] = Transform::default();
|
self.graphics_state.ctm[i] = Transform::default();
|
||||||
|
|
@ -457,23 +553,24 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
.expect("Accelerator not set before WorldBegin"),
|
.expect("Accelerator not set before WorldBegin"),
|
||||||
arena,
|
arena,
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_begin(&mut self, loc: FileLoc) {
|
fn attribute_begin(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.verify_world("AttributeBegin", &loc);
|
self.verify_world("AttributeBegin", &loc)?;
|
||||||
self.pushed_graphics_states
|
self.pushed_graphics_states
|
||||||
.push(self.graphics_state.clone());
|
.push(self.graphics_state.clone());
|
||||||
self.push_stack.push(('a', loc));
|
self.push_stack.push(('a', loc));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_end(&mut self, loc: FileLoc) {
|
fn attribute_end(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.verify_world("AttributeEnd", &loc);
|
self.verify_world("AttributeEnd", &loc)?;
|
||||||
if self.pushed_graphics_states.is_empty() {
|
if self.pushed_graphics_states.is_empty() {
|
||||||
log::error!(
|
return Err(ParserError::Generic(
|
||||||
"[{:?}] Unmatched AttributeEnd encountered. Ignoring it.",
|
"Unmatched AttributeEnd encountered".into(),
|
||||||
loc
|
loc,
|
||||||
);
|
));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(state) = self.pushed_graphics_states.pop() {
|
if let Some(state) = self.pushed_graphics_states.pop() {
|
||||||
|
|
@ -482,19 +579,24 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
|
|
||||||
if let Some((kind, start_loc)) = self.push_stack.pop() {
|
if let Some((kind, start_loc)) = self.push_stack.pop() {
|
||||||
if kind == 'o' {
|
if kind == 'o' {
|
||||||
log::error!(
|
return Err(ParserError::Generic(
|
||||||
"[{:?}] Mismatched nesting: open ObjectBegin from {} at AttributeEnd",
|
format!("Mismatched nesting: open ObjectBegin from {}", start_loc),
|
||||||
loc,
|
loc,
|
||||||
start_loc
|
));
|
||||||
);
|
|
||||||
std::process::exit(1);
|
|
||||||
} else {
|
} else {
|
||||||
debug_assert_eq!(kind, 'a', "Expected AttributeBegin on the stack");
|
debug_assert_eq!(kind, 'a', "Expected AttributeBegin on the stack");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute(&mut self, target: &str, params: ParsedParameterVector, loc: FileLoc) {
|
fn attribute(
|
||||||
|
&mut self,
|
||||||
|
target: &str,
|
||||||
|
params: ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
let current_attributes = match target {
|
let current_attributes = match target {
|
||||||
"shape" => &mut self.graphics_state.shape_attributes,
|
"shape" => &mut self.graphics_state.shape_attributes,
|
||||||
"light" => &mut self.graphics_state.light_attributes,
|
"light" => &mut self.graphics_state.light_attributes,
|
||||||
|
|
@ -502,13 +604,14 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
"medium" => &mut self.graphics_state.medium_attributes,
|
"medium" => &mut self.graphics_state.medium_attributes,
|
||||||
"texture" => &mut self.graphics_state.texture_attributes,
|
"texture" => &mut self.graphics_state.texture_attributes,
|
||||||
_ => {
|
_ => {
|
||||||
log::error!(
|
return Err(ParserError::Generic(
|
||||||
"[{:?}] Unknown attribute target \"{}\". Must be \"shape\", \"light\", \
|
format!(
|
||||||
|
"Unknown attribute target \"{}\". Must be \"shape\", \"light\", \
|
||||||
\"material\", \"medium\", or \"texture\".",
|
\"material\", \"medium\", or \"texture\".",
|
||||||
loc,
|
|
||||||
target
|
target
|
||||||
);
|
),
|
||||||
return;
|
loc,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -520,6 +623,7 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
|
|
||||||
current_attributes.push(p.clone());
|
current_attributes.push(p.clone());
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn texture(
|
fn texture(
|
||||||
|
|
@ -530,23 +634,23 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
params: &ParsedParameterVector,
|
params: &ParsedParameterVector,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: Arc<Arena>,
|
arena: Arc<Arena>,
|
||||||
) {
|
) -> Result<(), ParserError> {
|
||||||
let name = normalize_utf8(orig_name);
|
let name = normalize_utf8(orig_name);
|
||||||
self.verify_world("Texture", &loc);
|
self.verify_world("Texture", &loc);
|
||||||
let dict = ParameterDictionary::from_array(
|
let dict = ParameterDictionary::from_array(
|
||||||
params.clone(),
|
params.clone(),
|
||||||
&self.graphics_state.texture_attributes,
|
&self.graphics_state.texture_attributes,
|
||||||
self.graphics_state.color_space.clone(),
|
self.graphics_state.color_space.clone(),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
if type_name != "float" && type_name != "spectrum" {
|
if type_name != "float" && type_name != "spectrum" {
|
||||||
error_exit(
|
return Err(ParserError::Generic(
|
||||||
Some(&loc),
|
format!(
|
||||||
&format!(
|
|
||||||
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
|
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
|
||||||
tex_name
|
tex_name
|
||||||
),
|
),
|
||||||
);
|
loc,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -557,7 +661,10 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
};
|
};
|
||||||
|
|
||||||
if names.contains(&name) {
|
if names.contains(&name) {
|
||||||
error_exit(Some(&loc), &format!("Redefining texture \"{}\".", name));
|
return Err(ParserError::Generic(
|
||||||
|
format!("Redefining texture \"{}\".", name),
|
||||||
|
loc,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
names.insert(name.to_string());
|
names.insert(name.to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -579,42 +686,74 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
self.scene
|
self.scene
|
||||||
.add_spectrum_texture(name.to_string(), entity, arena);
|
.add_spectrum_texture(name.to_string(), entity, arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn material(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
fn material(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
self.verify_world("material", &loc);
|
self.verify_world("material", &loc);
|
||||||
let entity = SceneEntity {
|
let entity = SceneEntity {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
loc,
|
loc,
|
||||||
parameters: ParameterDictionary::new(params.clone(), None),
|
parameters: ParameterDictionary::new(params.clone(), None).unwrap(),
|
||||||
};
|
};
|
||||||
self.graphics_state.current_material_name = self.scene.add_material(entity).to_string();
|
self.graphics_state.current_material_name = self.scene.add_material(entity).to_string();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn make_named_material(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
|
fn make_named_material(
|
||||||
|
&mut self,
|
||||||
|
_name: &str,
|
||||||
|
_params: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn named_material(&mut self, _name: &str, _loc: FileLoc) {
|
|
||||||
|
fn named_material(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn light_source(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
|
|
||||||
todo!()
|
fn light_source(
|
||||||
|
&mut self,
|
||||||
|
_name: &str,
|
||||||
|
_params: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn area_light_source(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
|
|
||||||
todo!()
|
fn area_light_source(
|
||||||
|
&mut self,
|
||||||
|
_name: &str,
|
||||||
|
_params: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn shape(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
|
|
||||||
todo!()
|
fn shape(
|
||||||
|
&mut self,
|
||||||
|
_name: &str,
|
||||||
|
_params: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn object_begin(&mut self, _name: &str, _loc: FileLoc) {
|
fn object_begin(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
fn object_end(&mut self, _loc: FileLoc) {
|
fn object_end(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
fn object_instance(&mut self, _name: &str, _loc: FileLoc) {
|
fn object_instance(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
fn end_of_files(&mut self) {
|
fn end_of_files(&mut self) -> Result<(), ParserError> {
|
||||||
todo!()
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ use crate::utils::parallel::{AsyncJob, run_async};
|
||||||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||||
use crate::utils::{Upload, resolve_filename};
|
use crate::utils::{Upload, resolve_filename};
|
||||||
use crate::{Arena, FileLoc};
|
use crate::{Arena, FileLoc};
|
||||||
|
use anyhow::{Result, anyhow};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::core::camera::Camera;
|
use shared::core::camera::Camera;
|
||||||
|
|
@ -110,7 +111,7 @@ impl BasicScene {
|
||||||
integ: SceneEntity,
|
integ: SceneEntity,
|
||||||
accel: SceneEntity,
|
accel: SceneEntity,
|
||||||
arena: Arc<Arena>,
|
arena: Arc<Arena>,
|
||||||
) {
|
) -> Result<()> {
|
||||||
*self.integrator.lock() = Some(integ);
|
*self.integrator.lock() = Some(integ);
|
||||||
*self.accelerator.lock() = Some(accel);
|
*self.accelerator.lock() = Some(accel);
|
||||||
|
|
||||||
|
|
@ -118,9 +119,10 @@ impl BasicScene {
|
||||||
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
||||||
}
|
}
|
||||||
|
|
||||||
let filt = Filter::create(&filter.name, &filter.parameters, &filter.loc);
|
let filter = Filter::create(&filter.name, &filter.parameters, &filter.loc)
|
||||||
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.);
|
.map_err(|e| anyhow!("Failed to create filter: {}", e))?;
|
||||||
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.);
|
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.)?;
|
||||||
|
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.)?;
|
||||||
let exposure_time = shutter_close - shutter_open;
|
let exposure_time = shutter_close - shutter_open;
|
||||||
|
|
||||||
let film_instance = Arc::new(
|
let film_instance = Arc::new(
|
||||||
|
|
@ -128,12 +130,12 @@ impl BasicScene {
|
||||||
&film.name,
|
&film.name,
|
||||||
&film.parameters,
|
&film.parameters,
|
||||||
exposure_time,
|
exposure_time,
|
||||||
filt.expect("Must have a filter"),
|
filter,
|
||||||
Some(camera.camera_transform.clone()),
|
Some(camera.camera_transform.clone()),
|
||||||
&film.loc,
|
&film.loc,
|
||||||
&arena,
|
&arena,
|
||||||
)
|
)
|
||||||
.expect("Must have a film"),
|
.map_err(|e| anyhow!("Failed to create film: {}", e))?,
|
||||||
);
|
);
|
||||||
|
|
||||||
*self.film_state.lock() = SingletonState {
|
*self.film_state.lock() = SingletonState {
|
||||||
|
|
@ -152,7 +154,7 @@ impl BasicScene {
|
||||||
&sampler.loc,
|
&sampler.loc,
|
||||||
&arena_sampler,
|
&arena_sampler,
|
||||||
)
|
)
|
||||||
.expect("Sampler was not correctly created")
|
.map_err(|e| anyhow!("Failed to create sampler: {}", e))
|
||||||
});
|
});
|
||||||
self.sampler_state.lock().job = Some(sampler_job);
|
self.sampler_state.lock().job = Some(sampler_job);
|
||||||
|
|
||||||
|
|
@ -170,9 +172,10 @@ impl BasicScene {
|
||||||
&camera.base.loc,
|
&camera.base.loc,
|
||||||
&arena_camera,
|
&arena_camera,
|
||||||
)
|
)
|
||||||
.expect("Failed to create camera")
|
.map_err(|e| anyhow!("Failed to create camera: {}", e))
|
||||||
});
|
});
|
||||||
self.camera_state.lock().job = Some(camera_job);
|
self.camera_state.lock().job = Some(camera_job);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_named_material(&self, name: &str, material: SceneEntity) {
|
pub fn add_named_material(&self, name: &str, material: SceneEntity) {
|
||||||
|
|
@ -196,7 +199,8 @@ impl BasicScene {
|
||||||
get_serial: impl FnOnce(&mut TextureState) -> &mut Vec<(String, TextureSceneEntity)>,
|
get_serial: impl FnOnce(&mut TextureState) -> &mut Vec<(String, TextureSceneEntity)>,
|
||||||
get_jobs: impl FnOnce(&mut TextureState) -> &mut HashMap<String, AsyncJob<Arc<T>>>,
|
get_jobs: impl FnOnce(&mut TextureState) -> &mut HashMap<String, AsyncJob<Arc<T>>>,
|
||||||
create_fn: F,
|
create_fn: F,
|
||||||
) where
|
) -> Result<()>
|
||||||
|
where
|
||||||
T: Send + Sync + 'static,
|
T: Send + Sync + 'static,
|
||||||
F: FnOnce(TextureSceneEntity) -> T + Send + 'static,
|
F: FnOnce(TextureSceneEntity) -> T + Send + 'static,
|
||||||
{
|
{
|
||||||
|
|
@ -209,23 +213,24 @@ impl BasicScene {
|
||||||
|
|
||||||
if texture.base.name != "imagemap" && texture.base.name != "ptex" {
|
if texture.base.name != "imagemap" && texture.base.name != "ptex" {
|
||||||
get_serial(state).push((name, texture));
|
get_serial(state).push((name, texture));
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename = resolve_filename(&texture.base.parameters.get_one_string("filename", ""));
|
let filename = resolve_filename(&texture.base.parameters.get_one_string("filename", "")?);
|
||||||
if !self.validate_texture_file(&filename, &texture.base.loc, &mut state.n_missing_textures)
|
if !self.validate_texture_file(&filename, &texture.base.loc, &mut state.n_missing_textures)
|
||||||
{
|
{
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.loading_texture_filenames.contains(&filename) {
|
if state.loading_texture_filenames.contains(&filename) {
|
||||||
get_serial(state).push((name, texture));
|
get_serial(state).push((name, texture));
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
state.loading_texture_filenames.insert(filename);
|
state.loading_texture_filenames.insert(filename);
|
||||||
let job = run_async(move || Arc::new(create_fn(texture)));
|
let job = run_async(move || Arc::new(create_fn(texture)));
|
||||||
get_jobs(state).insert(name, job);
|
get_jobs(state).insert(name, job);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool {
|
fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool {
|
||||||
|
|
@ -245,7 +250,12 @@ impl BasicScene {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_float_texture(&self, name: String, texture: TextureSceneEntity, arena: Arc<Arena>) {
|
pub fn add_float_texture(
|
||||||
|
&self,
|
||||||
|
name: String,
|
||||||
|
texture: TextureSceneEntity,
|
||||||
|
arena: Arc<Arena>,
|
||||||
|
) -> Result<()> {
|
||||||
let mut state = self.texture_state.lock();
|
let mut state = self.texture_state.lock();
|
||||||
self.add_texture_generic(
|
self.add_texture_generic(
|
||||||
name,
|
name,
|
||||||
|
|
@ -265,7 +275,7 @@ impl BasicScene {
|
||||||
)
|
)
|
||||||
.expect("Could not create Float texture")
|
.expect("Could not create Float texture")
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_spectrum_texture(
|
pub fn add_spectrum_texture(
|
||||||
|
|
@ -273,7 +283,7 @@ impl BasicScene {
|
||||||
name: String,
|
name: String,
|
||||||
texture: TextureSceneEntity,
|
texture: TextureSceneEntity,
|
||||||
arena: Arc<Arena>,
|
arena: Arc<Arena>,
|
||||||
) {
|
) -> Result<()> {
|
||||||
let mut state = self.texture_state.lock();
|
let mut state = self.texture_state.lock();
|
||||||
self.add_texture_generic(
|
self.add_texture_generic(
|
||||||
name,
|
name,
|
||||||
|
|
@ -294,7 +304,7 @@ impl BasicScene {
|
||||||
)
|
)
|
||||||
.expect("Could not create spectrum texture")
|
.expect("Could not create spectrum texture")
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_area_light(&self, light: SceneEntity) -> usize {
|
pub fn add_area_light(&self, light: SceneEntity) -> usize {
|
||||||
|
|
@ -378,16 +388,25 @@ impl BasicScene {
|
||||||
named
|
named
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assuming that we can carry on if a material is missing.
|
||||||
|
// This might be a bad idea, but testing it for now (2026/02/19)
|
||||||
pub fn create_materials(
|
pub fn create_materials(
|
||||||
&self,
|
&self,
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> (HashMap<String, Material>, Vec<Material>) {
|
) -> Result<(HashMap<String, Material>, Vec<Material>)> {
|
||||||
let mut state = self.material_state.lock();
|
let mut state = self.material_state.lock();
|
||||||
|
|
||||||
let finished: Vec<_> = state.normal_map_jobs.drain().collect();
|
let finished: Vec<_> = state.normal_map_jobs.drain().collect();
|
||||||
for (filename, job) in finished {
|
for (filename, job) in finished {
|
||||||
state.normal_maps.insert(filename, job.wait());
|
match std::panic::catch_unwind(|| job.wait()) {
|
||||||
|
Ok(img) => {
|
||||||
|
state.normal_maps.insert(filename, img);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
log::error!("Failed to load normal map: {}", filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut named_materials: HashMap<String, Material> = HashMap::new();
|
let mut named_materials: HashMap<String, Material> = HashMap::new();
|
||||||
|
|
@ -402,39 +421,46 @@ impl BasicScene {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mat_type = entity.parameters.get_one_string("type", "");
|
let mat_type = entity.parameters.get_one_string("type", "")?;
|
||||||
if mat_type.is_empty() {
|
if mat_type.is_empty() {
|
||||||
log::error!(
|
log::error!("{}: missing material type", entity.loc);
|
||||||
"{}: \"string type\" not provided in named material's parameters.",
|
|
||||||
entity.loc
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let normal_map = self.get_normal_map(&state, &entity.parameters);
|
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||||
let tex_dict = TextureParameterDictionary::new(
|
let tex_dict = TextureParameterDictionary::new(
|
||||||
Arc::new(entity.parameters.clone()),
|
Arc::new(entity.parameters.clone()),
|
||||||
Some(textures),
|
Some(textures),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mat = Material::create(
|
match Material::create(
|
||||||
&mat_type,
|
&mat_type,
|
||||||
&tex_dict,
|
&tex_dict,
|
||||||
normal_map,
|
normal_map,
|
||||||
&named_materials, // Reference for now
|
&named_materials,
|
||||||
entity.loc.clone(),
|
entity.loc.clone(),
|
||||||
arena,
|
arena,
|
||||||
)
|
) {
|
||||||
.expect("Could not create material");
|
Ok(mat) => {
|
||||||
|
|
||||||
named_materials.insert(name.clone(), mat);
|
named_materials.insert(name.clone(), mat);
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"{}: Failed to create material '{}': {}",
|
||||||
|
entity.loc,
|
||||||
|
name,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let materials: Vec<Material> = state
|
let materials: Vec<Material> = state
|
||||||
.materials
|
.materials
|
||||||
.iter()
|
.iter()
|
||||||
.map(|entity| {
|
.filter_map(|entity| {
|
||||||
let normal_map = self.get_normal_map(&state, &entity.parameters);
|
let result: Result<Material> = (|| {
|
||||||
|
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||||
let tex_dict = TextureParameterDictionary::new(
|
let tex_dict = TextureParameterDictionary::new(
|
||||||
entity.parameters.clone().into(),
|
entity.parameters.clone().into(),
|
||||||
Some(textures),
|
Some(textures),
|
||||||
|
|
@ -448,11 +474,19 @@ impl BasicScene {
|
||||||
entity.loc.clone(),
|
entity.loc.clone(),
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not create material")
|
})();
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(mat) => Some(mat),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{}: Failed to create material: {}", entity.loc, e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
(named_materials, materials)
|
Ok((named_materials, materials))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_aggregate(
|
pub fn create_aggregate(
|
||||||
|
|
@ -499,7 +533,7 @@ impl BasicScene {
|
||||||
) -> Vec<Vec<Shape>> {
|
) -> Vec<Vec<Shape>> {
|
||||||
entities
|
entities
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|sh| {
|
.filter_map(|sh| {
|
||||||
Shape::create(
|
Shape::create(
|
||||||
&sh.base.name,
|
&sh.base.name,
|
||||||
*sh.render_from_object.as_ref(),
|
*sh.render_from_object.as_ref(),
|
||||||
|
|
@ -510,7 +544,7 @@ impl BasicScene {
|
||||||
sh.base.loc.clone(),
|
sh.base.loc.clone(),
|
||||||
arena,
|
arena,
|
||||||
)
|
)
|
||||||
.expect("Could not create shape")
|
.ok()
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -621,56 +655,57 @@ impl BasicScene {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// Getters
|
// Getters
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
pub fn get_camera(&self) -> Arc<Camera> {
|
pub fn get_camera(&self) -> Result<Arc<Camera>> {
|
||||||
self.get_singleton(&self.camera_state, "Camera")
|
self.get_singleton(&self.camera_state, "Camera")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sampler(&self) -> Arc<Sampler> {
|
pub fn get_sampler(&self) -> Result<Arc<Sampler>> {
|
||||||
self.get_singleton(&self.sampler_state, "Sampler")
|
self.get_singleton(&self.sampler_state, "Sampler")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_film(&self) -> Arc<Film> {
|
pub fn get_film(&self) -> Result<Arc<Film>> {
|
||||||
self.get_singleton(&self.film_state, "Film")
|
self.get_singleton(&self.film_state, "Film")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// Helpers
|
// Helpers
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
fn get_singleton<T: Send + 'static>(
|
fn get_singleton<T: Send + 'static>(
|
||||||
&self,
|
&self,
|
||||||
state: &Mutex<SingletonState<T>>,
|
state: &Mutex<SingletonState<T>>,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Arc<T> {
|
) -> Result<Arc<T>> {
|
||||||
let mut guard = state.lock();
|
let mut guard = state.lock();
|
||||||
|
|
||||||
if let Some(ref res) = guard.result {
|
if let Some(ref res) = guard.result {
|
||||||
return res.clone();
|
return Ok(res.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(job) = guard.job.take() {
|
if let Some(job) = guard.job.take() {
|
||||||
let res = Arc::new(job.wait());
|
let val = job.wait()?;
|
||||||
|
let res = Arc::new(val);
|
||||||
guard.result = Some(res.clone());
|
guard.result = Some(res.clone());
|
||||||
return res;
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
panic!("{} requested but not initialized!", name);
|
Err(anyhow!("{} requested but not initialized!", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_loading_normal_maps(&self, state: &mut MaterialState, params: &ParameterDictionary) {
|
fn start_loading_normal_maps(
|
||||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", ""));
|
&self,
|
||||||
|
state: &mut MaterialState,
|
||||||
|
params: &ParameterDictionary,
|
||||||
|
) -> Result<()> {
|
||||||
|
let filename = resolve_filename(¶ms.get_one_string("normalmap", "")?);
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.normal_map_jobs.contains_key(&filename)
|
if state.normal_map_jobs.contains_key(&filename)
|
||||||
|| state.normal_maps.contains_key(&filename)
|
|| state.normal_maps.contains_key(&filename)
|
||||||
{
|
{
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let filename_clone = filename.clone();
|
let filename_clone = filename.clone();
|
||||||
|
|
@ -693,18 +728,19 @@ impl BasicScene {
|
||||||
});
|
});
|
||||||
|
|
||||||
state.normal_map_jobs.insert(filename, job);
|
state.normal_map_jobs.insert(filename, job);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_map(
|
fn get_normal_map(
|
||||||
&self,
|
&self,
|
||||||
state: &MaterialState,
|
state: &MaterialState,
|
||||||
params: &ParameterDictionary,
|
params: &ParameterDictionary,
|
||||||
) -> Option<Arc<Image>> {
|
) -> Result<Option<Arc<Image>>> {
|
||||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", ""));
|
let filename = resolve_filename(¶ms.get_one_string("normalmap", "")?);
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
return None;
|
return Ok(None);
|
||||||
}
|
}
|
||||||
state.normal_maps.get(&filename).cloned()
|
Ok(state.normal_maps.get(&filename).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_alpha_texture(
|
fn get_alpha_texture(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use super::{SceneEntity, TextureSceneEntity};
|
||||||
use crate::core::image::Image;
|
use crate::core::image::Image;
|
||||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::parallel::AsyncJob;
|
use crate::utils::parallel::AsyncJob;
|
||||||
|
use anyhow::Result;
|
||||||
use shared::core::light::Light;
|
use shared::core::light::Light;
|
||||||
use shared::core::medium::Medium;
|
use shared::core::medium::Medium;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
@ -41,7 +42,7 @@ pub struct MediaState {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SingletonState<T> {
|
pub struct SingletonState<T> {
|
||||||
pub result: Option<Arc<T>>,
|
pub result: Option<Arc<T>>,
|
||||||
pub job: Option<AsyncJob<T>>,
|
pub job: Option<AsyncJob<Result<T>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for SingletonState<T> {
|
impl<T> Default for SingletonState<T> {
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,9 @@ pub trait CreateTextureMapping {
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
render_from_texture: &Transform,
|
render_from_texture: &Transform,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Self;
|
) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateTextureMapping for TextureMapping2D {
|
impl CreateTextureMapping for TextureMapping2D {
|
||||||
|
|
@ -173,36 +175,39 @@ impl CreateTextureMapping for TextureMapping2D {
|
||||||
params: &TextureParameterDictionary,
|
params: &TextureParameterDictionary,
|
||||||
render_from_texture: &Transform,
|
render_from_texture: &Transform,
|
||||||
loc: &FileLoc,
|
loc: &FileLoc,
|
||||||
) -> Self {
|
) -> Result<Self>
|
||||||
let mtype = params.get_one_string("mapping", "uv");
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let mtype = params.get_one_string("mapping", "uv")?;
|
||||||
match mtype.as_str() {
|
match mtype.as_str() {
|
||||||
"uv" => {
|
"uv" => {
|
||||||
let su = params.get_one_float("uscale", 1.);
|
let su = params.get_one_float("uscale", 1.)?;
|
||||||
let sv = params.get_one_float("vscale", 1.);
|
let sv = params.get_one_float("vscale", 1.)?;
|
||||||
let du = params.get_one_float("udelta", 0.);
|
let du = params.get_one_float("udelta", 0.)?;
|
||||||
let dv = params.get_one_float("vdelta", 0.);
|
let dv = params.get_one_float("vdelta", 0.)?;
|
||||||
let mapping = UVMapping::new(su, sv, du, dv);
|
let mapping = UVMapping::new(su, sv, du, dv);
|
||||||
TextureMapping2D::UV(mapping)
|
Ok(TextureMapping2D::UV(mapping))
|
||||||
}
|
}
|
||||||
"spherical" => {
|
"spherical" => {
|
||||||
let mapping = SphericalMapping::new(&render_from_texture.inverse());
|
let mapping = SphericalMapping::new(&render_from_texture.inverse());
|
||||||
TextureMapping2D::Spherical(mapping)
|
Ok(TextureMapping2D::Spherical(mapping))
|
||||||
}
|
}
|
||||||
"cylindrical" => {
|
"cylindrical" => {
|
||||||
let mapping = CylindricalMapping::new(&render_from_texture.inverse());
|
let mapping = CylindricalMapping::new(&render_from_texture.inverse());
|
||||||
TextureMapping2D::Cylindrical(mapping)
|
Ok(TextureMapping2D::Cylindrical(mapping))
|
||||||
}
|
}
|
||||||
"planar" => {
|
"planar" => {
|
||||||
let vs = params.get_one_vector3f("v1", Vector3f::new(1., 0., 0.));
|
let vs = params.get_one_vector3f("v1", Vector3f::new(1., 0., 0.))?;
|
||||||
let vt = params.get_one_vector3f("v2", Vector3f::new(0., 1., 0.));
|
let vt = params.get_one_vector3f("v2", Vector3f::new(0., 1., 0.))?;
|
||||||
let ds = params.get_one_float("udelta", 0.);
|
let ds = params.get_one_float("udelta", 0.)?;
|
||||||
let dt = params.get_one_float("vdelta", 0.);
|
let dt = params.get_one_float("vdelta", 0.)?;
|
||||||
let mapping = PlanarMapping::new(&render_from_texture.inverse(), vs, vt, ds, dt);
|
let mapping = PlanarMapping::new(&render_from_texture.inverse(), vs, vt, ds, dt);
|
||||||
TextureMapping2D::Planar(mapping)
|
Ok(TextureMapping2D::Planar(mapping))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("{}: 2D texture mapping unknown {}", loc, mtype);
|
log::error!("{}: 2D texture mapping unknown {}", loc, mtype);
|
||||||
TextureMapping2D::UV(UVMapping::default())
|
Ok(TextureMapping2D::UV(UVMapping::default()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,17 +57,17 @@ impl CreateFilm for GBufferFilm {
|
||||||
_arena: &Arena,
|
_arena: &Arena,
|
||||||
) -> Result<Film> {
|
) -> Result<Film> {
|
||||||
let colorspace = params.color_space.as_ref().unwrap();
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true);
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc);
|
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
||||||
|
|
||||||
let filename = params.get_one_string("filename", "pbrt.exr");
|
let filename = params.get_one_string("filename", "pbrt.exr")?;
|
||||||
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
||||||
return Err(anyhow!("{}: EXR is the only format supported by GBufferFilm", loc).into());
|
return Err(anyhow!("{}: EXR is the only format supported by GBufferFilm", loc).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let coords_system = params.get_one_string("coordinatesystem", "camera");
|
let coords_system = params.get_one_string("coordinatesystem", "camera")?;
|
||||||
let mut apply_inverse = false;
|
let mut apply_inverse = false;
|
||||||
let camera_transform =
|
let camera_transform =
|
||||||
camera_transform.ok_or_else(|| anyhow!("GBufferFilm requires a camera_transform"))?;
|
camera_transform.ok_or_else(|| anyhow!("GBufferFilm requires a camera_transform"))?;
|
||||||
|
|
|
||||||
|
|
@ -70,10 +70,10 @@ impl CreateFilm for RGBFilm {
|
||||||
_arena: &Arena,
|
_arena: &Arena,
|
||||||
) -> Result<Film> {
|
) -> Result<Film> {
|
||||||
let colorspace = params.color_space.as_ref().unwrap();
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true);
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc);
|
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
||||||
let film = RGBFilmHost::new(film_base, &colorspace, max_component_value, write_fp16);
|
let film = RGBFilmHost::new(film_base, &colorspace, max_component_value, write_fp16);
|
||||||
Ok(Film::RGB(film.device))
|
Ok(Film::RGB(film.device))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,19 +97,19 @@ impl CreateFilm for SpectralFilm {
|
||||||
_arena: &Arena,
|
_arena: &Arena,
|
||||||
) -> Result<Film> {
|
) -> Result<Film> {
|
||||||
let colorspace = params.color_space.as_ref().unwrap();
|
let colorspace = params.color_space.as_ref().unwrap();
|
||||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||||
let write_fp16 = params.get_one_bool("savefp16", true);
|
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||||
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;
|
||||||
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc);
|
let film_base = FilmBase::create(params, filter, Some(&sensor.device()), loc)?;
|
||||||
|
|
||||||
let filename = params.get_one_string("filename", "pbrt.exr");
|
let filename = params.get_one_string("filename", "pbrt.exr")?;
|
||||||
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
if Path::new(&filename).extension() != Some("exr".as_ref()) {
|
||||||
return Err(anyhow!("{}: EXR is the only format supported by GBufferFilm", loc).into());
|
return Err(anyhow!("{}: EXR is the only format supported by GBufferFilm", loc).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let n_buckets = params.get_one_int("nbuckets", 16) as usize;
|
let n_buckets = params.get_one_int("nbuckets", 16)? as usize;
|
||||||
let lambda_min = params.get_one_float("lambdamin", LAMBDA_MIN as Float);
|
let lambda_min = params.get_one_float("lambdamin", LAMBDA_MIN as Float)?;
|
||||||
let lambda_max = params.get_one_float("lambdamin", LAMBDA_MAX as Float);
|
let lambda_max = params.get_one_float("lambdamin", LAMBDA_MAX as Float)?;
|
||||||
if lambda_min < LAMBDA_MIN as Float && lambda_max > LAMBDA_MAX as Float {
|
if lambda_min < LAMBDA_MIN as Float && lambda_max > LAMBDA_MAX as Float {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"{}: PBRT must be recompiled with different values of LAMBDA_MIN and LAMBDA_MAX",
|
"{}: PBRT must be recompiled with different values of LAMBDA_MIN and LAMBDA_MAX",
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,32 @@ impl LanczosFilterCreator for LanczosSincFilter {
|
||||||
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
|
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let sqrt_samples = 64;
|
||||||
|
let n_samples = sqrt_samples * sqrt_samples;
|
||||||
|
let area = (2.0 * self.radius.x()) * (2.0 * self.radius.y());
|
||||||
|
let mut sum = 0.0;
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
|
for y in 0..sqrt_samples {
|
||||||
|
for x in 0..sqrt_samples {
|
||||||
|
let u = Point2f::new(
|
||||||
|
(x as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
||||||
|
(y as Float + rng.random::<Float>()) / sqrt_samples as Float,
|
||||||
|
);
|
||||||
|
let p = Point2f::new(
|
||||||
|
lerp(u.x(), -self.radius.x(), self.radius.x()),
|
||||||
|
lerp(u.y(), -self.radius.y(), self.radius.y()),
|
||||||
|
);
|
||||||
|
sum += self.evaluate(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let integral = sum / n_samples as Float * area;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
radius,
|
radius,
|
||||||
tau,
|
tau,
|
||||||
sampler,
|
sampler,
|
||||||
|
integral,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,23 @@ use crate::core::color::RGBToSpectrumTableData;
|
||||||
use bytemuck::cast_slice;
|
use bytemuck::cast_slice;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
|
use shared::PBRTOptions;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static OPTIONS: OnceLock<PBRTOptions> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn init_pbrt(options: PBRTOptions) {
|
||||||
|
OPTIONS
|
||||||
|
.set(options)
|
||||||
|
.expect("PBRT has already been initialized!");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_options() -> &'static PBRTOptions {
|
||||||
|
OPTIONS.get().unwrap_or_else(|| {
|
||||||
|
static DEFAULT: OnceLock<PBRTOptions> = OnceLock::new();
|
||||||
|
DEFAULT.get_or_init(PBRTOptions::default)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat");
|
static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat");
|
||||||
static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
|
static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
#![feature(f16)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
pub mod cameras;
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod films;
|
pub mod films;
|
||||||
pub mod filters;
|
pub mod filters;
|
||||||
|
|
|
||||||
|
|
@ -114,10 +114,10 @@ impl CreateLight for DiffuseAreaLight {
|
||||||
) -> Result<Light> {
|
) -> Result<Light> {
|
||||||
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
|
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
|
||||||
let illum_spec = Spectrum::Dense(colorspace.unwrap().illuminant);
|
let illum_spec = Spectrum::Dense(colorspace.unwrap().illuminant);
|
||||||
let mut scale = params.get_one_float("scale", 1.);
|
let mut scale = params.get_one_float("scale", 1.)?;
|
||||||
let two_sided = params.get_one_bool("twosided", false);
|
let two_sided = params.get_one_bool("twosided", false)?;
|
||||||
|
|
||||||
let filename = resolve_filename(¶ms.get_one_string("filename", ""));
|
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||||
let (image, image_color_space) = if !filename.is_empty() {
|
let (image, image_color_space) = if !filename.is_empty() {
|
||||||
if l.is_some() {
|
if l.is_some() {
|
||||||
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
||||||
|
|
@ -151,7 +151,7 @@ impl CreateLight for DiffuseAreaLight {
|
||||||
let l_for_scale = l.as_ref().unwrap_or(&illum_spec);
|
let l_for_scale = l.as_ref().unwrap_or(&illum_spec);
|
||||||
scale /= spectrum_to_photometric(*l_for_scale);
|
scale /= spectrum_to_photometric(*l_for_scale);
|
||||||
|
|
||||||
let phi_v = params.get_one_float("power", -1.0);
|
let phi_v = params.get_one_float("power", -1.0)?;
|
||||||
if phi_v > 0.0 {
|
if phi_v > 0.0 {
|
||||||
// k_e is the emissive power of the light as defined by the spectral
|
// k_e is the emissive power of the light as defined by the spectral
|
||||||
// distribution and texture and is used to normalize the emitted
|
// distribution and texture and is used to normalize the emitted
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,10 @@ impl CreateLight for DistantLight {
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut scale = parameters.get_one_float("scale", 1.);
|
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||||
|
|
||||||
let from = parameters.get_one_point3f("from", Point3f::new(0., 0., 0.));
|
let from = parameters.get_one_point3f("from", Point3f::new(0., 0., 0.))?;
|
||||||
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
|
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.))?;
|
||||||
let w = (from - to).normalize();
|
let w = (from - to).normalize();
|
||||||
let (v1, v2) = w.coordinate_system();
|
let (v1, v2) = w.coordinate_system();
|
||||||
let m: [Float; 16] = [
|
let m: [Float; 16] = [
|
||||||
|
|
@ -82,7 +82,7 @@ impl CreateLight for DistantLight {
|
||||||
let final_render = render_from_light * t;
|
let final_render = render_from_light * t;
|
||||||
scale /= spectrum_to_photometric(l);
|
scale /= spectrum_to_photometric(l);
|
||||||
// Adjust scale to meet target illuminance value
|
// Adjust scale to meet target illuminance value
|
||||||
let e_v = parameters.get_one_float("illuminance", -1.);
|
let e_v = parameters.get_one_float("illuminance", -1.)?;
|
||||||
if e_v > 0. {
|
if e_v > 0. {
|
||||||
scale *= e_v;
|
scale *= e_v;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,8 @@ impl CreateLight for GoniometricLight {
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
)
|
)
|
||||||
.expect("Could not retrieve spectrum");
|
.expect("Could not retrieve spectrum");
|
||||||
let mut scale = params.get_one_float("scale", 1.);
|
let mut scale = params.get_one_float("scale", 1.)?;
|
||||||
let filename = resolve_filename(¶ms.get_one_string("filename", ""));
|
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||||
let image: Ptr<Image> = if filename.is_empty() {
|
let image: Ptr<Image> = if filename.is_empty() {
|
||||||
Ptr::null()
|
Ptr::null()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -102,7 +102,7 @@ impl CreateLight for GoniometricLight {
|
||||||
};
|
};
|
||||||
|
|
||||||
scale /= spectrum_to_photometric(i);
|
scale /= spectrum_to_photometric(i);
|
||||||
let phi_v = params.get_one_float("power", -1.0);
|
let phi_v = params.get_one_float("power", -1.0)?;
|
||||||
|
|
||||||
if phi_v > 0.0 {
|
if phi_v > 0.0 {
|
||||||
let k_e = compute_emissive_power(&image);
|
let k_e = compute_emissive_power(&image);
|
||||||
|
|
|
||||||
|
|
@ -132,10 +132,10 @@ pub fn create(
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Light> {
|
) -> Result<Light> {
|
||||||
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
|
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
|
||||||
let mut scale = parameters.get_one_float("scale", 1.0);
|
let mut scale = parameters.get_one_float("scale", 1.0)?;
|
||||||
let portal = parameters.get_point3f_array("portal");
|
let portal = parameters.get_point3f_array("portal")?;
|
||||||
let filename = resolve_filename(¶meters.get_one_string("filename", ""));
|
let filename = resolve_filename(¶meters.get_one_string("filename", "")?);
|
||||||
let e_v = parameters.get_one_float("illuminance", -1.0);
|
let e_v = parameters.get_one_float("illuminance", -1.0)?;
|
||||||
|
|
||||||
let has_spectrum = !l.is_empty();
|
let has_spectrum = !l.is_empty();
|
||||||
let has_file = !filename.is_empty();
|
let has_file = !filename.is_empty();
|
||||||
|
|
|
||||||
|
|
@ -60,15 +60,15 @@ impl CreateLight for PointLight {
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut scale = parameters.get_one_float("scale", 1.);
|
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||||
scale /= spectrum_to_photometric(l);
|
scale /= spectrum_to_photometric(l);
|
||||||
let phi_v = parameters.get_one_float("power", 1.);
|
let phi_v = parameters.get_one_float("power", 1.)?;
|
||||||
if phi_v > 0. {
|
if phi_v > 0. {
|
||||||
let k_e = 4. * PI;
|
let k_e = 4. * PI;
|
||||||
scale *= phi_v / k_e;
|
scale *= phi_v / k_e;
|
||||||
}
|
}
|
||||||
|
|
||||||
let from = parameters.get_one_point3f("from", Point3f::zero());
|
let from = parameters.get_one_point3f("from", Point3f::zero())?;
|
||||||
let tf = Transform::translate(from.into());
|
let tf = Transform::translate(from.into());
|
||||||
let final_render = render_from_light * tf;
|
let final_render = render_from_light * tf;
|
||||||
let specific = PointLight::new(final_render, medium.into(), l, scale);
|
let specific = PointLight::new(final_render, medium.into(), l, scale);
|
||||||
|
|
|
||||||
|
|
@ -104,11 +104,11 @@ impl CreateLight for ProjectionLight {
|
||||||
_colorspace: Option<&RGBColorSpace>,
|
_colorspace: Option<&RGBColorSpace>,
|
||||||
arena: &Arena,
|
arena: &Arena,
|
||||||
) -> Result<Light> {
|
) -> Result<Light> {
|
||||||
let mut scale = parameters.get_one_float("scale", 1.);
|
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||||
let power = parameters.get_one_float("power", -1.);
|
let power = parameters.get_one_float("power", -1.)?;
|
||||||
let fov = parameters.get_one_float("fov", 90.);
|
let fov = parameters.get_one_float("fov", 90.)?;
|
||||||
|
|
||||||
let filename = resolve_filename(¶meters.get_one_string("filename", ""));
|
let filename = resolve_filename(¶meters.get_one_string("filename", "")?);
|
||||||
if filename.is_empty() {
|
if filename.is_empty() {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"{}: must provide filename for projection light",
|
"{}: must provide filename for projection light",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
// use crate::core::image::{Image, ImageIO, ImageMetadata};
|
|
||||||
use crate::core::light::{CreateLight, lookup_spectrum};
|
use crate::core::light::{CreateLight, lookup_spectrum};
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
|
|
@ -72,17 +71,17 @@ impl CreateLight for SpotLight {
|
||||||
SpectrumType::Illuminant,
|
SpectrumType::Illuminant,
|
||||||
)
|
)
|
||||||
.expect("No spectrum");
|
.expect("No spectrum");
|
||||||
let mut scale = parameters.get_one_float("scale", 1.);
|
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||||
let coneangle = parameters.get_one_float("coneangle", 30.);
|
let coneangle = parameters.get_one_float("coneangle", 30.)?;
|
||||||
let conedelta = parameters.get_one_float("conedelta", 5.);
|
let conedelta = parameters.get_one_float("conedelta", 5.)?;
|
||||||
let from = parameters.get_one_point3f("from", Point3f::zero());
|
let from = parameters.get_one_point3f("from", Point3f::zero())?;
|
||||||
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
|
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.))?;
|
||||||
let dir_to_z = Transform::from(Frame::from_z((to - from).normalize()));
|
let dir_to_z = Transform::from(Frame::from_z((to - from).normalize()));
|
||||||
let t = Transform::translate(from.into()) * dir_to_z.inverse();
|
let t = Transform::translate(from.into()) * dir_to_z.inverse();
|
||||||
let final_render = render_from_light * t;
|
let final_render = render_from_light * t;
|
||||||
scale /= spectrum_to_photometric(i);
|
scale /= spectrum_to_photometric(i);
|
||||||
|
|
||||||
let phi_v = parameters.get_one_float("power", -1.);
|
let phi_v = parameters.get_one_float("power", -1.)?;
|
||||||
if phi_v > 0. {
|
if phi_v > 0. {
|
||||||
let cos_falloff_end = radians(coneangle).cos();
|
let cos_falloff_end = radians(coneangle).cos();
|
||||||
let cos_falloff_start = radians(coneangle - conedelta).cos();
|
let cos_falloff_start = radians(coneangle - conedelta).cos();
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::core::image::Image;
|
||||||
use crate::core::material::CreateMaterial;
|
use crate::core::material::CreateMaterial;
|
||||||
use crate::core::texture::SpectrumTexture;
|
use crate::core::texture::SpectrumTexture;
|
||||||
use crate::spectra::data::get_named_spectrum;
|
use crate::spectra::data::get_named_spectrum;
|
||||||
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload, parameters::error_exit};
|
use crate::utils::{Arena, FileLoc, TextureParameterDictionary, Upload};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::core::shape::{ALL_TRIANGLE_MESHES, CreateShape};
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::mesh::TriangleMesh;
|
use crate::shapes::mesh::TriangleMesh;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, bail};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use shared::core::shape::Shape;
|
use shared::core::shape::Shape;
|
||||||
use shared::shapes::TriangleShape;
|
use shared::shapes::TriangleShape;
|
||||||
|
|
@ -29,9 +29,7 @@ impl CreateShape for TriangleShape {
|
||||||
if vertex_indices.is_empty() {
|
if vertex_indices.is_empty() {
|
||||||
if p.len() == 3 {
|
if p.len() == 3 {
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!(
|
return bail!("Vertex indices \"indices\" must be provided with triangle mesh.");
|
||||||
"Vertex indices \"indices\" must be provided with triangle mesh."
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
} else if vertex_indices.len() % 3 != 0 {
|
} else if vertex_indices.len() % 3 != 0 {
|
||||||
let excess = vertex_indices.len() % 3;
|
let excess = vertex_indices.len() % 3;
|
||||||
|
|
@ -45,9 +43,7 @@ impl CreateShape for TriangleShape {
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.is_empty() {
|
if p.is_empty() {
|
||||||
return Err(anyhow!(
|
return bail!("Vertex positions \"P\" must be provided with triangle mesh.");
|
||||||
"Vertex positions \"P\" must be provided with triangle mesh."
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !uvs.is_empty() && uvs.len() != p.len() {
|
if !uvs.is_empty() && uvs.len() != p.len() {
|
||||||
|
|
@ -68,11 +64,11 @@ impl CreateShape for TriangleShape {
|
||||||
for (_, &index) in vertex_indices.iter().enumerate() {
|
for (_, &index) in vertex_indices.iter().enumerate() {
|
||||||
// Check for negative indices (if keeping i32) or out of bounds
|
// Check for negative indices (if keeping i32) or out of bounds
|
||||||
if index < 0 || index as usize >= p.len() {
|
if index < 0 || index as usize >= p.len() {
|
||||||
return Err(anyhow!(
|
return bail!(
|
||||||
"TriangleMesh has out-of-bounds vertex index {} ({} \"P\" values were given). Discarding this mesh.",
|
"TriangleMesh has out-of-bounds vertex index {} ({} \"P\" values were given). Discarding this mesh.",
|
||||||
index,
|
index,
|
||||||
p.len()
|
p.len()
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub struct SystemAllocator;
|
||||||
|
|
||||||
impl Default for SystemAllocator {
|
impl Default for SystemAllocator {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self
|
Self {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,25 +35,36 @@ impl GpuAllocator for SystemAllocator {
|
||||||
|
|
||||||
/// CUDA unified memory backend using CudaAllocator
|
/// CUDA unified memory backend using CudaAllocator
|
||||||
#[cfg(feature = "cuda")]
|
#[cfg(feature = "cuda")]
|
||||||
pub struct CudaAllocator;
|
pub mod cuda {
|
||||||
|
use super::GpuAllocator;
|
||||||
|
use std::alloc::Layout;
|
||||||
|
|
||||||
#[cfg(feature = "cuda")]
|
pub struct CudaAllocator;
|
||||||
impl Default for CudaAllocator {
|
|
||||||
|
impl Default for CudaAllocator {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self
|
Self {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "cuda")]
|
impl GpuAllocator for CudaAllocator {
|
||||||
impl GpuAllocator for CudaAllocator {
|
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
use cust::memory::cuda_malloc_unified;
|
use cust::memory::cuda_malloc_unified;
|
||||||
|
use cust_raw::driver_sys::*;
|
||||||
|
|
||||||
let size = layout.size().max(layout.align());
|
let size = layout.size().max(layout.align());
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return layout.align() as *mut u8;
|
return layout.align() as *mut u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut ctx: CUcontext = std::ptr::null_mut();
|
||||||
|
cuCtxGetCurrent(&mut ctx);
|
||||||
|
if ctx.is_null() {
|
||||||
|
let mut primary: CUcontext = std::ptr::null_mut();
|
||||||
|
cuDevicePrimaryCtxRetain(&mut primary, 0);
|
||||||
|
cuCtxSetCurrent(primary);
|
||||||
|
}
|
||||||
|
|
||||||
let mut unified_ptr =
|
let mut unified_ptr =
|
||||||
unsafe { cuda_malloc_unified::<u8>(size).expect("cuda_malloc_unified failed") };
|
unsafe { cuda_malloc_unified::<u8>(size).expect("cuda_malloc_unified failed") };
|
||||||
let raw = unified_ptr.as_raw_mut();
|
let raw = unified_ptr.as_raw_mut();
|
||||||
|
|
@ -67,40 +78,87 @@ impl GpuAllocator for CudaAllocator {
|
||||||
let _ = unsafe { cuda_free_unified(UnifiedPointer::wrap(ptr)) };
|
let _ = unsafe { cuda_free_unified(UnifiedPointer::wrap(ptr)) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Vulkan backend using gpu-allocator.
|
/// Vulkan backend (gpu-allocator for now, there might be a better solution)
|
||||||
#[cfg(feature = "vulkan")]
|
#[cfg(feature = "vulkan")]
|
||||||
pub mod vulkan {
|
pub mod vulkan {
|
||||||
use super::GpuAllocator;
|
use super::GpuAllocator;
|
||||||
use ash::vk;
|
use ash::vk;
|
||||||
use gpu_allocator::MemoryLocation;
|
use gpu_allocator::MemoryLocation;
|
||||||
use gpu_allocator::vulkan::{Allocation, AllocationCreateDesc, AllocationScheme, Allocator};
|
use gpu_allocator::vulkan::{
|
||||||
|
Allocation, AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc,
|
||||||
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::alloc::Layout;
|
use std::alloc::Layout;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
pub struct VulkanAllocator {
|
// So, having a static allocator seems like a terrible idea
|
||||||
allocator: Mutex<Allocator>,
|
// But I cant find a way to get a functioning generic Arena constructor
|
||||||
/// Track pointer -> Allocation so we can free later.
|
// That might not even be a necessity, since rust-gpu/rust-cuda might actually handle that
|
||||||
allocations: Mutex<HashMap<usize, Allocation>>,
|
// differently
|
||||||
|
static VK_ALLOCATOR: OnceLock<VulkanAllocatorInner> = OnceLock::new();
|
||||||
|
|
||||||
|
struct VulkanAllocatorInner {
|
||||||
|
state: Mutex<VulkanState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VulkanAllocator {
|
struct VulkanState {
|
||||||
pub fn new(allocator: Allocator) -> Self {
|
allocator: Allocator,
|
||||||
Self {
|
allocations: HashMap<usize, Allocation>,
|
||||||
allocator: Mutex::new(allocator),
|
|
||||||
allocations: Mutex::new(HashMap::new()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_vulkan(
|
||||||
|
instance: &ash::Instance,
|
||||||
|
device: &ash::Device,
|
||||||
|
physical_device: vk::PhysicalDevice,
|
||||||
|
) {
|
||||||
|
VK_ALLOCATOR.get_or_init(|| {
|
||||||
|
let allocator = Allocator::new(&AllocatorCreateDesc {
|
||||||
|
instance: instance.clone(),
|
||||||
|
device: device.clone(),
|
||||||
|
physical_device,
|
||||||
|
debug_settings: Default::default(),
|
||||||
|
buffer_device_address: false,
|
||||||
|
allocation_sizes: Default::default(),
|
||||||
|
})
|
||||||
|
.expect("Failed to create Vulkan allocator");
|
||||||
|
|
||||||
|
VulkanAllocatorInner {
|
||||||
|
state: Mutex::new(VulkanState {
|
||||||
|
allocator,
|
||||||
|
allocations: HashMap::new(),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner() -> &'static VulkanAllocatorInner {
|
||||||
|
VK_ALLOCATOR
|
||||||
|
.get()
|
||||||
|
.expect("Vulkan not initialized — call init_vulkan() before Arena::default()")
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for VulkanAllocator {
|
impl Default for VulkanAllocator {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
let _ = inner();
|
||||||
Self
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct VulkanAllocator;
|
||||||
|
|
||||||
|
// impl VulkanAllocator {
|
||||||
|
// pub fn new(allocator: Allocator) -> Self {
|
||||||
|
// Self {
|
||||||
|
// allocator: Mutex::new(allocator),
|
||||||
|
// allocations: Mutex::new(HashMap::new()),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl GpuAllocator for VulkanAllocator {
|
impl GpuAllocator for VulkanAllocator {
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
let size = layout.size().max(layout.align());
|
let size = layout.size().max(layout.align());
|
||||||
|
|
@ -108,8 +166,10 @@ pub mod vulkan {
|
||||||
return layout.align() as *mut u8;
|
return layout.align() as *mut u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut alloc = self.allocator.lock();
|
let inner = inner();
|
||||||
let allocation = alloc
|
let mut state = inner.state.lock();
|
||||||
|
let allocation = state
|
||||||
|
.allocator
|
||||||
.allocate(&AllocationCreateDesc {
|
.allocate(&AllocationCreateDesc {
|
||||||
name: "arena",
|
name: "arena",
|
||||||
requirements: vk::MemoryRequirements {
|
requirements: vk::MemoryRequirements {
|
||||||
|
|
@ -128,7 +188,7 @@ pub mod vulkan {
|
||||||
.expect("Vulkan allocation not host-mapped")
|
.expect("Vulkan allocation not host-mapped")
|
||||||
.as_ptr() as *mut u8;
|
.as_ptr() as *mut u8;
|
||||||
|
|
||||||
self.allocations.lock().insert(ptr as usize, allocation);
|
state.allocations.insert(ptr as usize, allocation);
|
||||||
ptr
|
ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,9 +196,11 @@ pub mod vulkan {
|
||||||
if layout.size() == 0 {
|
if layout.size() == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Some(allocation) = self.allocations.lock().remove(&(ptr as usize)) {
|
let inner = inner();
|
||||||
self.allocator
|
let mut state = inner.state.lock();
|
||||||
.lock()
|
if let Some(allocation) = state.allocations.remove(&(ptr as usize)) {
|
||||||
|
state
|
||||||
|
.allocator
|
||||||
.free(allocation)
|
.free(allocation)
|
||||||
.expect("Vulkan free failed");
|
.expect("Vulkan free failed");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,33 @@
|
||||||
|
use half::f16;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use shared::utils::hash::hash_buffer;
|
use shared::utils::hash::hash_buffer;
|
||||||
use shared::utils::math::{DeviceDigitPermutation, PRIMES, permutation_element};
|
use shared::utils::math::{
|
||||||
|
DeviceDigitPermutation, PRIMES, f16_to_f32_software, permutation_element,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn f16_to_f32(bits: u16) -> f32 {
|
||||||
|
#[cfg(target_os = "cuda")]
|
||||||
|
{
|
||||||
|
// Use hardware intrinsic on CUDA
|
||||||
|
// Cast bits to cuda_f16, then cast to f32
|
||||||
|
let half_val = unsafe { core::mem::transmute::<u16, cuda_std::f16>(bits) };
|
||||||
|
half_val.to_f32()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "spirv")]
|
||||||
|
{
|
||||||
|
// Use shared logic or spirv-std intrinsics if available.
|
||||||
|
// Sadly, f16 support in rust-gpu is still maturing.
|
||||||
|
// A manual bit-conversion function is often safest here.
|
||||||
|
f16_to_f32_software(bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "cuda", target_arch = "spirv")))]
|
||||||
|
{
|
||||||
|
f16::from_bits(bits).to_f32()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DigitPermutation {
|
pub struct DigitPermutation {
|
||||||
pub permutations: Vec<u16>,
|
pub permutations: Vec<u16>,
|
||||||
|
|
@ -68,13 +95,7 @@ pub fn compute_radical_inverse_permutations(seed: u64) -> (Vec<u16>, Vec<DeviceD
|
||||||
|
|
||||||
// let ptr_to_data = storage_base_ptr.add(current_offset);
|
// let ptr_to_data = storage_base_ptr.add(current_offset);
|
||||||
|
|
||||||
views.push(
|
views.push(DigitPermutation::new(base as i32, n_digits as u64).device);
|
||||||
DigitPermutation::new(
|
|
||||||
base as i32,
|
|
||||||
n_digits as u64,
|
|
||||||
)
|
|
||||||
.device,
|
|
||||||
);
|
|
||||||
|
|
||||||
// current_offset += len;
|
// current_offset += len;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub use strings::*;
|
||||||
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
|
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
|
||||||
|
|
||||||
#[cfg(all(feature = "cuda", not(feature = "vulkan")))]
|
#[cfg(all(feature = "cuda", not(feature = "vulkan")))]
|
||||||
pub type Arena = arena::Arena<backend::CudaAllocator>;
|
pub type Arena = arena::Arena<backend::cuda::CudaAllocator>;
|
||||||
|
|
||||||
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
|
#[cfg(not(any(feature = "cuda", feature = "vulkan")))]
|
||||||
pub type Arena = arena::Arena<backend::SystemAllocator>;
|
pub type Arena = arena::Arena<backend::SystemAllocator>;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::Result;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
@ -12,10 +13,23 @@ use crate::utils::parameters::{ParameterDictionary, ParsedParameter, ParsedParam
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
|
|
||||||
pub trait ParserTarget {
|
pub trait ParserTarget {
|
||||||
fn identity(&mut self, loc: FileLoc);
|
fn identity(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn translate(&mut self, dx: Float, dy: Float, dz: Float, loc: FileLoc);
|
fn translate(
|
||||||
fn rotate(&mut self, angle: Float, ax: Float, ay: Float, az: Float, loc: FileLoc);
|
&mut self,
|
||||||
fn scale(&mut self, sx: Float, sy: Float, sz: Float, loc: FileLoc);
|
dx: Float,
|
||||||
|
dy: Float,
|
||||||
|
dz: Float,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn rotate(
|
||||||
|
&mut self,
|
||||||
|
angle: Float,
|
||||||
|
ax: Float,
|
||||||
|
ay: Float,
|
||||||
|
az: Float,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn scale(&mut self, sx: Float, sy: Float, sz: Float, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn look_at(
|
fn look_at(
|
||||||
&mut self,
|
&mut self,
|
||||||
ex: Float,
|
ex: Float,
|
||||||
|
|
@ -28,32 +42,85 @@ pub trait ParserTarget {
|
||||||
uy: Float,
|
uy: Float,
|
||||||
uz: Float,
|
uz: Float,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
);
|
) -> Result<(), ParserError>;
|
||||||
fn transform(&mut self, transform: &[Float; 16], loc: FileLoc);
|
fn transform(&mut self, transform: &[Float; 16], loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn concat_transform(&mut self, transform: &[Float; 16], loc: FileLoc);
|
fn concat_transform(
|
||||||
|
&mut self,
|
||||||
|
transform: &[Float; 16],
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn coordinate_system(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
fn coord_sys_transform(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
fn active_transform_all(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
fn active_transform_end_time(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
fn active_transform_start_time(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
fn transform_times(
|
||||||
|
&mut self,
|
||||||
|
start: Float,
|
||||||
|
end: Float,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
|
||||||
fn coordinate_system(&mut self, name: &str, loc: FileLoc);
|
fn option(&mut self, name: &str, value: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn coord_sys_transform(&mut self, name: &str, loc: FileLoc);
|
fn color_space(&mut self, n: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn active_transform_all(&mut self, loc: FileLoc);
|
fn pixel_filter(
|
||||||
fn active_transform_end_time(&mut self, loc: FileLoc);
|
&mut self,
|
||||||
fn active_transform_start_time(&mut self, loc: FileLoc);
|
name: &str,
|
||||||
fn transform_times(&mut self, start: Float, end: Float, loc: FileLoc);
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn film(
|
||||||
|
&mut self,
|
||||||
|
type_name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn accelerator(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn integrator(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn camera(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn make_named_medium(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn medium_interface(
|
||||||
|
&mut self,
|
||||||
|
inside_name: &str,
|
||||||
|
outside_name: &str,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn sampler(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
|
||||||
fn option(&mut self, name: &str, value: &str, loc: FileLoc);
|
fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) -> Result<(), ParserError>;
|
||||||
fn color_space(&mut self, n: &str, loc: FileLoc);
|
fn attribute_begin(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn pixel_filter(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
fn attribute_end(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn film(&mut self, type_name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
fn attribute(
|
||||||
fn accelerator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
&mut self,
|
||||||
fn integrator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
target: &str,
|
||||||
fn camera(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
params: ParsedParameterVector,
|
||||||
fn make_named_medium(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
loc: FileLoc,
|
||||||
fn medium_interface(&mut self, inside_name: &str, outside_name: &str, loc: FileLoc);
|
) -> Result<(), ParserError>;
|
||||||
fn sampler(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
|
||||||
|
|
||||||
fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>);
|
|
||||||
fn attribute_begin(&mut self, loc: FileLoc);
|
|
||||||
fn attribute_end(&mut self, loc: FileLoc);
|
|
||||||
fn attribute(&mut self, target: &str, params: ParsedParameterVector, loc: FileLoc);
|
|
||||||
|
|
||||||
fn texture(
|
fn texture(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -63,22 +130,47 @@ pub trait ParserTarget {
|
||||||
params: &ParsedParameterVector,
|
params: &ParsedParameterVector,
|
||||||
loc: FileLoc,
|
loc: FileLoc,
|
||||||
arena: Arc<Arena>,
|
arena: Arc<Arena>,
|
||||||
);
|
) -> Result<(), ParserError>;
|
||||||
fn material(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
fn material(
|
||||||
fn make_named_material(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
&mut self,
|
||||||
fn named_material(&mut self, name: &str, loc: FileLoc);
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn make_named_material(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn named_material(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
|
||||||
fn light_source(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
fn light_source(
|
||||||
fn area_light_source(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn area_light_source(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
|
||||||
fn shape(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
fn shape(
|
||||||
fn reverse_orientation(&mut self, loc: FileLoc);
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError>;
|
||||||
|
fn reverse_orientation(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
|
||||||
fn object_begin(&mut self, name: &str, loc: FileLoc);
|
fn object_begin(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn object_end(&mut self, loc: FileLoc);
|
fn object_end(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
fn object_instance(&mut self, name: &str, loc: FileLoc);
|
fn object_instance(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||||
|
|
||||||
fn end_of_files(&mut self);
|
fn end_of_files(&mut self) -> Result<(), ParserError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -394,24 +486,42 @@ impl FormattingParserTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ParserTarget for FormattingParserTarget {
|
impl ParserTarget for FormattingParserTarget {
|
||||||
fn option(&mut self, name: &str, value: &str, _loc: FileLoc) {
|
fn option(&mut self, name: &str, value: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}Option \"{}\" \"{}\"", self.indent(0), name, value);
|
println!("{}Option \"{}\" \"{}\"", self.indent(0), name, value);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identity(&mut self, _loc: FileLoc) {
|
fn identity(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}Identity", self.indent(0));
|
println!("{}Identity", self.indent(0));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn translate(&mut self, dx: Float, dy: Float, dz: Float, _loc: FileLoc) {
|
fn translate(
|
||||||
|
&mut self,
|
||||||
|
dx: Float,
|
||||||
|
dy: Float,
|
||||||
|
dz: Float,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
println!("{}Translate {} {} {}", self.indent(0), dx, dy, dz);
|
println!("{}Translate {} {} {}", self.indent(0), dx, dy, dz);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rotate(&mut self, angle: Float, ax: Float, ay: Float, az: Float, _loc: FileLoc) {
|
fn rotate(
|
||||||
println!("{}Rotate {} {} {} {}", self.indent(0), angle, ax, ay, az);
|
&mut self,
|
||||||
|
angle: Float,
|
||||||
|
ax: Float,
|
||||||
|
ay: Float,
|
||||||
|
az: Float,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
println!("{} Rotate {} {} {} {}", self.indent(0), angle, ax, ay, az);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale(&mut self, sx: Float, sy: Float, sz: Float, _loc: FileLoc) {
|
fn scale(&mut self, sx: Float, sy: Float, sz: Float, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}Scale {} {} {}", self.indent(0), sx, sy, sz);
|
println!("{}Scale {} {} {}", self.indent(0), sx, sy, sz);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn look_at(
|
fn look_at(
|
||||||
|
|
@ -426,7 +536,7 @@ impl ParserTarget for FormattingParserTarget {
|
||||||
uy: Float,
|
uy: Float,
|
||||||
uz: Float,
|
uz: Float,
|
||||||
_loc: FileLoc,
|
_loc: FileLoc,
|
||||||
) {
|
) -> Result<(), ParserError> {
|
||||||
println!(
|
println!(
|
||||||
"{}LookAt {} {} {} {} {} {} {} {} {}",
|
"{}LookAt {} {} {} {} {} {} {} {} {}",
|
||||||
self.indent(0),
|
self.indent(0),
|
||||||
|
|
@ -440,56 +550,75 @@ impl ParserTarget for FormattingParserTarget {
|
||||||
uy,
|
uy,
|
||||||
uz
|
uz
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn concat_transform(&mut self, t: &[Float; 16], _loc: FileLoc) {
|
fn concat_transform(&mut self, t: &[Float; 16], _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
// Rust arrays verify size at compile time, simpler than C++ pointers
|
|
||||||
println!("{}ConcatTransform [ {:?} ]", self.indent(0), t);
|
println!("{}ConcatTransform [ {:?} ]", self.indent(0), t);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(&mut self, t: &[Float; 16], _loc: FileLoc) {
|
fn transform(&mut self, t: &[Float; 16], _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}Transform [ {:?} ]", self.indent(0), t);
|
println!("{}Transform [ {:?} ]", self.indent(0), t);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coordinate_system(&mut self, name: &str, _loc: FileLoc) {
|
fn coordinate_system(&mut self, name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}CoordinateSystem \"{}\"", self.indent(0), name);
|
println!("{}CoordinateSystem \"{}\"", self.indent(0), name);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coord_sys_transform(&mut self, name: &str, _loc: FileLoc) {
|
fn coord_sys_transform(&mut self, name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}CoordSysTransform \"{}\"", self.indent(0), name);
|
println!("{}CoordSysTransform \"{}\"", self.indent(0), name);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn world_begin(&mut self, _loc: FileLoc, _arena: Arc<Arena>) {
|
fn world_begin(&mut self, _loc: FileLoc, _arena: Arc<Arena>) -> Result<(), ParserError> {
|
||||||
println!("{}WorldBegin", self.indent(0));
|
println!("{}WorldBegin", self.indent(0));
|
||||||
self.cat_indent_count += 4;
|
self.cat_indent_count += 4;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_begin(&mut self, _loc: FileLoc) {
|
fn attribute_begin(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}AttributeBegin", self.indent(0));
|
println!("{}AttributeBegin", self.indent(0));
|
||||||
self.cat_indent_count += 4;
|
self.cat_indent_count += 4;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn attribute_end(&mut self, _loc: FileLoc) {
|
fn attribute_end(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.cat_indent_count = self.cat_indent_count.saturating_sub(4);
|
self.cat_indent_count = self.cat_indent_count.saturating_sub(4);
|
||||||
println!("{}AttributeEnd", self.indent(0));
|
println!("{}AttributeEnd", self.indent(0));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shape(&mut self, name: &str, params: &ParsedParameterVector, _loc: FileLoc) {
|
fn shape(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
println!(
|
println!(
|
||||||
"{}Shape \"{}\" ... ({} params)",
|
"{}Shape \"{}\" ... ({} params)",
|
||||||
self.indent(0),
|
self.indent(0),
|
||||||
name,
|
name,
|
||||||
params.len()
|
params.len()
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn material(&mut self, name: &str, params: &ParsedParameterVector, _loc: FileLoc) {
|
fn material(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
params: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
println!(
|
println!(
|
||||||
"{}Material \"{}\" ... ({} params)",
|
"{}Material \"{}\" ... ({} params)",
|
||||||
self.indent(0),
|
self.indent(0),
|
||||||
name,
|
name,
|
||||||
params.len()
|
params.len()
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn texture(
|
fn texture(
|
||||||
|
|
@ -500,7 +629,7 @@ impl ParserTarget for FormattingParserTarget {
|
||||||
_params: &ParsedParameterVector,
|
_params: &ParsedParameterVector,
|
||||||
_loc: FileLoc,
|
_loc: FileLoc,
|
||||||
_arena: Arc<Arena>,
|
_arena: Arc<Arena>,
|
||||||
) {
|
) -> Result<(), ParserError> {
|
||||||
println!(
|
println!(
|
||||||
"{}Texture \"{}\" \"{}\" \"{}\"",
|
"{}Texture \"{}\" \"{}\" \"{}\"",
|
||||||
self.indent(0),
|
self.indent(0),
|
||||||
|
|
@ -508,38 +637,137 @@ impl ParserTarget for FormattingParserTarget {
|
||||||
type_name,
|
type_name,
|
||||||
tex_name
|
tex_name
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_transform_all(&mut self, _loc: FileLoc) {}
|
fn active_transform_all(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
fn active_transform_end_time(&mut self, _loc: FileLoc) {}
|
Ok(())
|
||||||
fn active_transform_start_time(&mut self, _loc: FileLoc) {}
|
}
|
||||||
fn transform_times(&mut self, _s: Float, _e: Float, _loc: FileLoc) {}
|
fn active_transform_end_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
fn color_space(&mut self, _n: &str, _loc: FileLoc) {}
|
Ok(())
|
||||||
fn pixel_filter(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
}
|
||||||
fn film(&mut self, _t: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
fn active_transform_start_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
fn accelerator(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
Ok(())
|
||||||
fn integrator(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
}
|
||||||
fn camera(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
fn transform_times(&mut self, _s: Float, _e: Float, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
fn make_named_medium(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
Ok(())
|
||||||
fn medium_interface(&mut self, _i: &str, _o: &str, _loc: FileLoc) {}
|
}
|
||||||
fn sampler(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
fn color_space(&mut self, _n: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
fn attribute(&mut self, _t: &str, _p: ParsedParameterVector, _loc: FileLoc) {}
|
Ok(())
|
||||||
fn make_named_material(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
}
|
||||||
fn named_material(&mut self, _n: &str, _loc: FileLoc) {}
|
fn pixel_filter(
|
||||||
fn light_source(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
&mut self,
|
||||||
fn area_light_source(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
_n: &str,
|
||||||
fn reverse_orientation(&mut self, _loc: FileLoc) {}
|
_p: &ParsedParameterVector,
|
||||||
fn object_begin(&mut self, name: &str, _loc: FileLoc) {
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn film(
|
||||||
|
&mut self,
|
||||||
|
_t: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn accelerator(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn integrator(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn camera(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn make_named_medium(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn medium_interface(&mut self, _i: &str, _o: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn sampler(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn attribute(
|
||||||
|
&mut self,
|
||||||
|
_t: &str,
|
||||||
|
_p: ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn make_named_material(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn named_material(&mut self, _n: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn light_source(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn area_light_source(
|
||||||
|
&mut self,
|
||||||
|
_n: &str,
|
||||||
|
_p: &ParsedParameterVector,
|
||||||
|
_loc: FileLoc,
|
||||||
|
) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn reverse_orientation(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn object_begin(&mut self, name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
println!("{}ObjectBegin \"{}\"", self.indent(0), name);
|
println!("{}ObjectBegin \"{}\"", self.indent(0), name);
|
||||||
self.cat_indent_count += 4;
|
self.cat_indent_count += 4;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn object_end(&mut self, _loc: FileLoc) {
|
fn object_end(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
self.cat_indent_count = self.cat_indent_count.saturating_sub(4);
|
self.cat_indent_count = self.cat_indent_count.saturating_sub(4);
|
||||||
println!("{}ObjectEnd", self.indent(0));
|
println!("{}ObjectEnd", self.indent(0));
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
fn object_instance(&mut self, _n: &str, _loc: FileLoc) {}
|
fn object_instance(&mut self, _n: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||||
fn end_of_files(&mut self) {
|
Ok(())
|
||||||
|
}
|
||||||
|
fn end_of_files(&mut self) -> Result<(), ParserError> {
|
||||||
self.cat_indent_count = 0;
|
self.cat_indent_count = 0;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -756,17 +984,17 @@ impl<'a> SceneParser<'a> {
|
||||||
|
|
||||||
match first_char {
|
match first_char {
|
||||||
'A' => match token.text.as_str() {
|
'A' => match token.text.as_str() {
|
||||||
"AttributeBegin" => self.target.attribute_begin(token.loc),
|
"AttributeBegin" => self.target.attribute_begin(token.loc)?,
|
||||||
"AttributeEnd" => self.target.attribute_end(token.loc),
|
"AttributeEnd" => self.target.attribute_end(token.loc)?,
|
||||||
"Attribute" => {
|
"Attribute" => {
|
||||||
self.parse_basic_entry(|t, n, p, l| t.attribute(n, p.to_vec(), l))?
|
self.parse_basic_entry(|t, n, p, l| t.attribute(n, p.to_vec(), l))?
|
||||||
}
|
}
|
||||||
"ActiveTransform" => {
|
"ActiveTransform" => {
|
||||||
let a = self.next_token_required()?;
|
let a = self.next_token_required()?;
|
||||||
match a.text.as_str() {
|
match a.text.as_str() {
|
||||||
"All" => self.target.active_transform_all(token.loc),
|
"All" => self.target.active_transform_all(token.loc)?,
|
||||||
"EndTime" => self.target.active_transform_end_time(token.loc),
|
"EndTime" => self.target.active_transform_end_time(token.loc)?,
|
||||||
"StartTime" => self.target.active_transform_start_time(token.loc),
|
"StartTime" => self.target.active_transform_start_time(token.loc)?,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
"Unknown ActiveTransform type".into(),
|
"Unknown ActiveTransform type".into(),
|
||||||
|
|
@ -796,19 +1024,19 @@ impl<'a> SceneParser<'a> {
|
||||||
m[i] = self.expect_float()?;
|
m[i] = self.expect_float()?;
|
||||||
}
|
}
|
||||||
self.expect_token("]")?;
|
self.expect_token("]")?;
|
||||||
self.target.concat_transform(&m, token.loc);
|
self.target.concat_transform(&m, token.loc)?;
|
||||||
}
|
}
|
||||||
"CoordinateSystem" => {
|
"CoordinateSystem" => {
|
||||||
let n = self.expect_quoted_string()?;
|
let n = self.expect_quoted_string()?;
|
||||||
self.target.coordinate_system(&n, token.loc);
|
self.target.coordinate_system(&n, token.loc)?;
|
||||||
}
|
}
|
||||||
"CoordSysTransform" => {
|
"CoordSysTransform" => {
|
||||||
let n = self.expect_quoted_string()?;
|
let n = self.expect_quoted_string()?;
|
||||||
self.target.coord_sys_transform(&n, token.loc);
|
self.target.coord_sys_transform(&n, token.loc)?;
|
||||||
}
|
}
|
||||||
"ColorSpace" => {
|
"ColorSpace" => {
|
||||||
let n = self.expect_quoted_string()?;
|
let n = self.expect_quoted_string()?;
|
||||||
self.target.color_space(&n, token.loc);
|
self.target.color_space(&n, token.loc)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -846,7 +1074,7 @@ impl<'a> SceneParser<'a> {
|
||||||
|
|
||||||
self.file_stack.push(new_tokenizer);
|
self.file_stack.push(new_tokenizer);
|
||||||
}
|
}
|
||||||
"Identity" => self.target.identity(token.loc),
|
"Identity" => self.target.identity(token.loc)?,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
format!("Unknown directive {}", token.text),
|
format!("Unknown directive {}", token.text),
|
||||||
|
|
@ -892,7 +1120,7 @@ impl<'a> SceneParser<'a> {
|
||||||
self.unget(next);
|
self.unget(next);
|
||||||
inside.clone()
|
inside.clone()
|
||||||
};
|
};
|
||||||
self.target.medium_interface(&inside, &outside, token.loc);
|
self.target.medium_interface(&inside, &outside, token.loc)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -905,7 +1133,7 @@ impl<'a> SceneParser<'a> {
|
||||||
'N' => match token.text.as_str() {
|
'N' => match token.text.as_str() {
|
||||||
"NamedMaterial" => {
|
"NamedMaterial" => {
|
||||||
let n = self.expect_quoted_string()?;
|
let n = self.expect_quoted_string()?;
|
||||||
self.target.named_material(&n, token.loc);
|
self.target.named_material(&n, token.loc)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -918,18 +1146,18 @@ impl<'a> SceneParser<'a> {
|
||||||
'O' => match token.text.as_str() {
|
'O' => match token.text.as_str() {
|
||||||
"ObjectBegin" => {
|
"ObjectBegin" => {
|
||||||
let n = self.expect_quoted_string()?;
|
let n = self.expect_quoted_string()?;
|
||||||
self.target.object_begin(&n, token.loc);
|
self.target.object_begin(&n, token.loc)?;
|
||||||
}
|
}
|
||||||
"ObjectEnd" => self.target.object_end(token.loc),
|
"ObjectEnd" => self.target.object_end(token.loc)?,
|
||||||
"ObjectInstance" => {
|
"ObjectInstance" => {
|
||||||
let n = self.expect_quoted_string()?;
|
let n = self.expect_quoted_string()?;
|
||||||
self.target.object_instance(&n, token.loc);
|
self.target.object_instance(&n, token.loc)?;
|
||||||
}
|
}
|
||||||
"Option" => {
|
"Option" => {
|
||||||
let name = self.expect_quoted_string()?;
|
let name = self.expect_quoted_string()?;
|
||||||
let val_tok = self.next_token_required()?;
|
let val_tok = self.next_token_required()?;
|
||||||
let val = val_tok.dequote().to_string();
|
let val = val_tok.dequote().to_string();
|
||||||
self.target.option(&name, &val, token.loc);
|
self.target.option(&name, &val, token.loc)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -952,7 +1180,7 @@ impl<'a> SceneParser<'a> {
|
||||||
},
|
},
|
||||||
|
|
||||||
'R' => match token.text.as_str() {
|
'R' => match token.text.as_str() {
|
||||||
"ReverseOrientation" => self.target.reverse_orientation(token.loc),
|
"ReverseOrientation" => self.target.reverse_orientation(token.loc)?,
|
||||||
"Rotate" => {
|
"Rotate" => {
|
||||||
let angle = self.expect_float()?;
|
let angle = self.expect_float()?;
|
||||||
let ax = self.expect_float()?;
|
let ax = self.expect_float()?;
|
||||||
|
|
@ -986,8 +1214,8 @@ impl<'a> SceneParser<'a> {
|
||||||
},
|
},
|
||||||
|
|
||||||
'T' => match token.text.as_str() {
|
'T' => match token.text.as_str() {
|
||||||
"TransformBegin" => self.target.attribute_begin(token.loc),
|
"TransformBegin" => self.target.attribute_begin(token.loc)?,
|
||||||
"TransformEnd" => self.target.attribute_end(token.loc),
|
"TransformEnd" => self.target.attribute_end(token.loc)?,
|
||||||
"Transform" => {
|
"Transform" => {
|
||||||
self.expect_token("[")?;
|
self.expect_token("[")?;
|
||||||
let mut m = [0.0; 16];
|
let mut m = [0.0; 16];
|
||||||
|
|
@ -995,18 +1223,18 @@ impl<'a> SceneParser<'a> {
|
||||||
m[i] = self.expect_float()?;
|
m[i] = self.expect_float()?;
|
||||||
}
|
}
|
||||||
self.expect_token("]")?;
|
self.expect_token("]")?;
|
||||||
self.target.transform(&m, token.loc);
|
self.target.transform(&m, token.loc)?;
|
||||||
}
|
}
|
||||||
"Translate" => {
|
"Translate" => {
|
||||||
let x = self.expect_float()?;
|
let x = self.expect_float()?;
|
||||||
let y = self.expect_float()?;
|
let y = self.expect_float()?;
|
||||||
let z = self.expect_float()?;
|
let z = self.expect_float()?;
|
||||||
self.target.translate(x, y, z, token.loc);
|
self.target.translate(x, y, z, token.loc)?;
|
||||||
}
|
}
|
||||||
"TransformTimes" => {
|
"TransformTimes" => {
|
||||||
let s = self.expect_float()?;
|
let s = self.expect_float()?;
|
||||||
let e = self.expect_float()?;
|
let e = self.expect_float()?;
|
||||||
self.target.transform_times(s, e, token.loc);
|
self.target.transform_times(s, e, token.loc)?;
|
||||||
}
|
}
|
||||||
"Texture" => {
|
"Texture" => {
|
||||||
let name = self.expect_quoted_string()?;
|
let name = self.expect_quoted_string()?;
|
||||||
|
|
@ -1020,7 +1248,7 @@ impl<'a> SceneParser<'a> {
|
||||||
¶ms,
|
¶ms,
|
||||||
token.loc,
|
token.loc,
|
||||||
arena.clone(),
|
arena.clone(),
|
||||||
);
|
)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -1031,7 +1259,7 @@ impl<'a> SceneParser<'a> {
|
||||||
},
|
},
|
||||||
|
|
||||||
'W' => match token.text.as_str() {
|
'W' => match token.text.as_str() {
|
||||||
"WorldBegin" => self.target.world_begin(token.loc, arena.clone()),
|
"WorldBegin" => self.target.world_begin(token.loc, arena.clone())?,
|
||||||
"WorldEnd" => {}
|
"WorldEnd" => {}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ParserError::Generic(
|
return Err(ParserError::Generic(
|
||||||
|
|
@ -1050,7 +1278,7 @@ impl<'a> SceneParser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.target.end_of_files();
|
self.target.end_of_files()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1079,7 +1307,12 @@ impl<'a> SceneParser<'a> {
|
||||||
|
|
||||||
fn parse_basic_entry<F>(&mut self, mut func: F) -> Result<(), ParserError>
|
fn parse_basic_entry<F>(&mut self, mut func: F) -> Result<(), ParserError>
|
||||||
where
|
where
|
||||||
F: FnMut(&mut dyn ParserTarget, &str, &ParsedParameterVector, FileLoc),
|
F: FnMut(
|
||||||
|
&mut dyn ParserTarget,
|
||||||
|
&str,
|
||||||
|
&ParsedParameterVector,
|
||||||
|
FileLoc,
|
||||||
|
) -> Result<(), ParserError>,
|
||||||
{
|
{
|
||||||
let type_token = self.next_token_required()?;
|
let type_token = self.next_token_required()?;
|
||||||
let type_name = if type_token.is_quoted() {
|
let type_name = if type_token.is_quoted() {
|
||||||
|
|
@ -1089,7 +1322,6 @@ impl<'a> SceneParser<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let params = self.parse_parameters()?;
|
let params = self.parse_parameters()?;
|
||||||
func(self.target, &type_name, ¶ms, type_token.loc);
|
func(self.target, &type_name, ¶ms, type_token.loc)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue