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
|
||||
.vscode
|
||||
rust-analyzer.json
|
||||
rust-analyzer.toml
|
||||
data/
|
||||
src/gpu/
|
||||
src/tests/
|
||||
|
|
|
|||
33
Cargo.toml
33
Cargo.toml
|
|
@ -6,11 +6,12 @@ edition = "2024"
|
|||
[features]
|
||||
default = []
|
||||
use_f64 = []
|
||||
use_gpu = []
|
||||
use_nvtx = []
|
||||
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw"]
|
||||
use_gpu = ["dep:wgpu"]
|
||||
use_nvtx = ["dep:nvtx"]
|
||||
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw", "dep:cuda-runtime-sys"]
|
||||
vulkan = ["ash", "gpu-allocator"]
|
||||
ash = ["dep:ash"]
|
||||
gpu-allocator = ["dep:gpu-allocator"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.100"
|
||||
|
|
@ -22,7 +23,6 @@ indicatif = "0.18.3"
|
|||
lazy_static = "1.5.0"
|
||||
log = "0.4.29"
|
||||
memmap2 = "0.9.9"
|
||||
nvtx = "1.3.0"
|
||||
parking_lot = "0.12.5"
|
||||
paste = "1.0.15"
|
||||
qoi = "0.4.1"
|
||||
|
|
@ -30,16 +30,6 @@ rand = "0.9.2"
|
|||
rayon = "1.11.0"
|
||||
thiserror = "2.0.17"
|
||||
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"
|
||||
slice = "0.0.4"
|
||||
crossbeam-channel = "0.5.15"
|
||||
|
|
@ -49,7 +39,18 @@ enum_dispatch = "0.3.13"
|
|||
bytemuck = "1.24.0"
|
||||
once_cell = "1.21.3"
|
||||
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 }
|
||||
|
||||
[build-dependencies]
|
||||
|
|
@ -59,7 +60,7 @@ cc = "1.2.53"
|
|||
|
||||
[workspace]
|
||||
members = ["shared"]
|
||||
exclude = ["crates/ptex-filter"]
|
||||
exclude = ["crates/ptex-filter", "kernels"]
|
||||
|
||||
[lints.clippy]
|
||||
excessive_precision = "allow"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
[package]
|
||||
name = "kernels"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
cuda_std = { git = "https://github.com/rust-gpu/rust-cuda", rev = "7fa76f3d717038a92c90bf4a482b0b8dd3259344" }
|
||||
shared = { path = "../shared", features = ["cuda"] }
|
||||
edition = "2021"
|
||||
|
||||
[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 = "nvptx64", feature(abi_ptx))]
|
||||
#![cfg_attr(target_arch = "spirv", no_std)]
|
||||
|
||||
use cuda_std::prelude::*;
|
||||
use spirv_std::spirv;
|
||||
|
||||
/// Scales each element: data[i] *= scale
|
||||
#[kernel]
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
pub unsafe fn scale_array(data: *mut f32, len: u32, scale: f32) {
|
||||
let idx = thread::index_1d() as u32;
|
||||
if idx >= len {
|
||||
return;
|
||||
/// Core logic — testable on CPU
|
||||
pub fn scale_kernel_logic(idx: usize, input: &[f32], output: &mut [f32], scale: f32) {
|
||||
if idx < input.len() {
|
||||
output[idx] = input[idx] * scale;
|
||||
}
|
||||
|
||||
let ptr = unsafe { data.add(idx as usize) };
|
||||
*ptr = *ptr * scale;
|
||||
}
|
||||
|
||||
/// Adds two arrays: c[i] = a[i] + b[i]
|
||||
#[kernel]
|
||||
#[allow(improper_ctypes_definitions)]
|
||||
pub unsafe fn add_arrays(a: *const f32, b: *const f32, c: *mut f32, len: u32) {
|
||||
let idx = thread::index_1d() as u32;
|
||||
if idx >= len {
|
||||
return;
|
||||
}
|
||||
|
||||
let i = idx as usize;
|
||||
*c.add(i) = *a.add(i) + *b.add(i);
|
||||
#[spirv(compute(threads(64)))]
|
||||
pub fn scale_kernel(
|
||||
#[spirv(global_invocation_id)] id: spirv_std::glam::UVec3,
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] input: &[f32],
|
||||
#[spirv(storage_buffer, descriptor_set = 0, binding = 1)] output: &mut [f32],
|
||||
#[spirv(push_constant)] scale: &f32,
|
||||
) {
|
||||
scale_kernel_logic(id.x as usize, input, output, *scale);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,12 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
bitflags = "2.10.0"
|
||||
bumpalo = "3.19.1"
|
||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||
enum_dispatch = "0.3.13"
|
||||
log = "0.4.29"
|
||||
num = "0.4.3"
|
||||
num-integer = "0.1.46"
|
||||
num-traits = "0.2.19"
|
||||
once_cell = "1.21.3"
|
||||
smallvec = "1.15.1"
|
||||
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
|
||||
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]
|
||||
use_f64 = []
|
||||
cuda = ["cuda_std"]
|
||||
cpu_debug = []
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ use crate::utils::sampling::{
|
|||
};
|
||||
use crate::{Float, INV_2_PI, INV_PI, PI};
|
||||
use core::any::Any;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
static P_MAX: usize = 3;
|
||||
#[repr(C)]
|
||||
|
|
@ -81,7 +82,7 @@ impl HairBxDF {
|
|||
let ap0 = SampledSpectrum::new(f);
|
||||
let ap1 = t * (1.0 - f).powi(2);
|
||||
let tf = t * f;
|
||||
std::array::from_fn(|p| match p {
|
||||
core::array::from_fn(|p| match p {
|
||||
0 => ap0,
|
||||
1 => ap1,
|
||||
_ if p < P_MAX => ap1 * tf.pow_int(p - 1),
|
||||
|
|
@ -133,7 +134,7 @@ impl HairBxDF {
|
|||
let t = t_value.exp();
|
||||
let ap = Self::ap(cos_theta_o, self.eta, self.h, t);
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use crate::spectra::SampledSpectrum;
|
|||
use crate::utils::sampling::{cosine_hemisphere_pdf, sample_cosine_hemisphere};
|
||||
use crate::{Float, INV_PI};
|
||||
use core::any::Any;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[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::{Float, INV_PI};
|
||||
use core::any::Any;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use crate::core::geometry::{
|
|||
spherical_direction, spherical_theta,
|
||||
};
|
||||
use crate::core::medium::{HGPhaseFunction, PhaseFunctionTrait};
|
||||
use crate::core::options::get_options;
|
||||
use crate::core::scattering::{
|
||||
TrowbridgeReitzDistribution, fr_complex, fr_complex_from_spectrum, fr_dielectric, reflect,
|
||||
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 core::any::Any;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum TopOrBottom<'a, T, B> {
|
||||
|
|
@ -91,6 +91,7 @@ where
|
|||
albedo: SampledSpectrum,
|
||||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
seed: i32,
|
||||
}
|
||||
|
||||
impl<T, B, const TWO_SIDED: bool> LayeredBxDF<T, B, TWO_SIDED>
|
||||
|
|
@ -106,6 +107,7 @@ where
|
|||
g: Float,
|
||||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
seed: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
top,
|
||||
|
|
@ -115,6 +117,7 @@ where
|
|||
albedo,
|
||||
max_depth,
|
||||
n_samples,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -369,7 +372,7 @@ where
|
|||
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 mut rng = Rng::new_with_offset(hash0, hash1);
|
||||
|
||||
|
|
@ -415,7 +418,7 @@ where
|
|||
let mut specular_path = bs.is_specular();
|
||||
|
||||
// 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 mut rng = Rng::new_with_offset(hash0, hash1);
|
||||
|
||||
|
|
@ -517,7 +520,7 @@ where
|
|||
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 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::{Float, INV_PI, PI, PI_OVER_2};
|
||||
use core::any::Any;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::core::scattering::refract;
|
|||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::Ptr;
|
||||
use crate::utils::math::{lerp, quadratic, square};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
@ -35,87 +36,17 @@ const EXIT_PUPIL_SAMPLES: usize = 64;
|
|||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RealisticCamera {
|
||||
base: CameraBase,
|
||||
focus_distance: Float,
|
||||
set_aperture_diameter: Float,
|
||||
aperture_image: Ptr<DeviceImage>,
|
||||
element_interfaces: Ptr<LensElementInterface>,
|
||||
n_elements: usize,
|
||||
physical_extent: Bounds2f,
|
||||
exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
|
||||
pub base: CameraBase,
|
||||
pub focus_distance: Float,
|
||||
pub set_aperture_diameter: Float,
|
||||
pub aperture_image: Ptr<DeviceImage>,
|
||||
pub element_interfaces: Ptr<LensElementInterface>,
|
||||
pub n_elements: usize,
|
||||
pub physical_extent: Bounds2f,
|
||||
pub exit_pupil_bounds: [Bounds2f; EXIT_PUPIL_SAMPLES],
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "cuda"))]
|
||||
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) {
|
||||
let tf = -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() {
|
||||
print!(
|
||||
"Unable to find exit pupil in x = {},{} on film.",
|
||||
film_x_0, film_x_1
|
||||
);
|
||||
return pupil_bounds;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use crate::core::pbrt::{Float, PI};
|
|||
use crate::core::sampler::CameraSample;
|
||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::math::{equal_area_square_to_sphere, wrap_equal_area_square};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[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::{Float, PI};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use std::sync::Arc;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BSSRDFSample {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@ use crate::core::geometry::{
|
|||
};
|
||||
use crate::core::interaction::Interaction;
|
||||
use crate::core::medium::Medium;
|
||||
use crate::core::options::RenderingCoordinateSystem;
|
||||
use crate::core::pbrt::Float;
|
||||
use crate::core::sampler::CameraSample;
|
||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::math::lerp;
|
||||
use crate::utils::options::RenderingCoordinateSystem;
|
||||
use crate::utils::ptr::Ptr;
|
||||
use crate::utils::transform::{AnimatedTransform, Transform};
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::any::TypeId;
|
||||
use std::fmt;
|
||||
use std::ops::{
|
||||
use core::any::TypeId;
|
||||
use core::fmt;
|
||||
use core::ops::{
|
||||
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::find_interval;
|
||||
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ impl From<[Float; 3]> for XYZ {
|
|||
|
||||
impl<'a> IntoIterator for &'a XYZ {
|
||||
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 {
|
||||
[&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 {
|
||||
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 {
|
||||
[&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::transform::AnimatedTransform;
|
||||
use crate::utils::{AtomicFloat, Ptr};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -298,9 +299,9 @@ pub struct SpectralPixel {
|
|||
impl Clone for SpectralPixel {
|
||||
fn clone(&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_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,
|
||||
}
|
||||
}
|
||||
|
|
@ -309,9 +310,9 @@ impl Clone for SpectralPixel {
|
|||
impl Default for SpectralPixel {
|
||||
fn default() -> 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_splat: std::array::from_fn(|_| AtomicFloat::new(0.0)),
|
||||
rgb_splat: core::array::from_fn(|_| AtomicFloat::new(0.0)),
|
||||
bucket_offset: 0,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use super::{Float, NumFloat};
|
||||
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::utils::interval::Interval;
|
||||
use crate::utils::math::lerp;
|
||||
use core::mem;
|
||||
use core::ops::{Add, Div, DivAssign, Mul, Sub};
|
||||
use num_traits::{Bounded, Num};
|
||||
use std::mem;
|
||||
use std::ops::{Add, Div, DivAssign, Mul, Sub};
|
||||
|
||||
// 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> {
|
||||
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 {
|
||||
[&self.p_min, &self.p_max].into_iter()
|
||||
|
|
@ -137,7 +137,7 @@ where
|
|||
}
|
||||
|
||||
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 {
|
||||
self.p_max[i]
|
||||
} else {
|
||||
|
|
@ -206,7 +206,7 @@ where
|
|||
|
||||
impl<T> Bounds3<T>
|
||||
where
|
||||
T: NumFloat + PartialOrd + Copy + Default + Sqrt,
|
||||
T: NumFloat + PartialOrd + Copy + Default + SqrtExt,
|
||||
{
|
||||
pub fn bounding_sphere(&self) -> (Point3<T>, T) {
|
||||
let two = T::one() + T::one();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::{Bounds3f, Float, PI, Point3f, Vector3f, VectorLike};
|
||||
use crate::utils::math::{degrees, safe_acos, safe_asin, safe_sqrt, square};
|
||||
use crate::utils::transform::TransformGeneric;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DirectionCone {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub use self::primitives::{
|
|||
Vector3i,
|
||||
};
|
||||
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::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 crate::utils::interval::Interval;
|
||||
use crate::utils::math::{clamp, difference_of_products, quadratic, safe_asin};
|
||||
use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::Sum;
|
||||
use std::ops::{
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::iter::Sum;
|
||||
use core::ops::{
|
||||
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> {
|
||||
type Output;
|
||||
|
|
@ -18,7 +18,7 @@ impl MulAdd<Float, Float> for Float {
|
|||
type Output = Float;
|
||||
#[inline(always)]
|
||||
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>
|
||||
+ Sub<Output = T>
|
||||
+ Div<Output = T>
|
||||
+ Sqrt,
|
||||
+ SqrtExt,
|
||||
{
|
||||
type Scalar = 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>
|
||||
where
|
||||
T: NumFloat + Sqrt,
|
||||
T: NumFloat + SqrtExt,
|
||||
{
|
||||
pub fn distance(self, other: Self) -> T {
|
||||
(self - other).norm()
|
||||
|
|
@ -814,7 +814,7 @@ impl<const N: usize> From<Point<i32, N>> for Point<Float, N> {
|
|||
|
||||
impl<T> Normal3<T>
|
||||
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 {
|
||||
let v: Vector3<T> = v.into();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use crate::core::pbrt::Float;
|
||||
use crate::utils::interval::Interval;
|
||||
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 std::ops::{Add, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub};
|
||||
|
||||
pub trait Tuple<T, const N: usize>:
|
||||
Sized + Copy + Index<usize, Output = T> + IndexMut<usize>
|
||||
|
|
@ -77,7 +77,7 @@ pub trait VectorLike:
|
|||
+ Div<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 norm_squared(self) -> Self::Scalar {
|
||||
|
|
@ -96,7 +96,7 @@ pub trait VectorLike:
|
|||
}
|
||||
|
||||
fn norm(&self) -> Self::Scalar {
|
||||
self.norm_squared().sqrt()
|
||||
self.norm_squared().sqrt_ext()
|
||||
}
|
||||
|
||||
fn normalize(self) -> Self
|
||||
|
|
@ -119,36 +119,36 @@ pub trait VectorLike:
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Sqrt {
|
||||
fn sqrt(self) -> Self;
|
||||
pub trait SqrtExt {
|
||||
fn sqrt_ext(self) -> Self;
|
||||
}
|
||||
|
||||
impl Sqrt for Float {
|
||||
fn sqrt(self) -> Self {
|
||||
self.sqrt()
|
||||
impl SqrtExt for Float {
|
||||
fn sqrt_ext(self) -> Self {
|
||||
<Self as num_traits::Float>::sqrt(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sqrt for f64 {
|
||||
fn sqrt(self) -> Self {
|
||||
self.sqrt()
|
||||
impl SqrtExt for f64 {
|
||||
fn sqrt_ext(self) -> Self {
|
||||
<Self as num_traits::Float>::sqrt(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sqrt for i32 {
|
||||
fn sqrt(self) -> Self {
|
||||
impl SqrtExt for i32 {
|
||||
fn sqrt_ext(self) -> Self {
|
||||
self.isqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sqrt for u32 {
|
||||
fn sqrt(self) -> Self {
|
||||
impl SqrtExt for u32 {
|
||||
fn sqrt_ext(self) -> Self {
|
||||
self.isqrt()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sqrt for Interval {
|
||||
fn sqrt(self) -> Self {
|
||||
impl SqrtExt for Interval {
|
||||
fn sqrt_ext(self) -> Self {
|
||||
let low = if self.low < 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@ use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
|
|||
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
|
||||
use crate::utils::Ptr;
|
||||
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 half::f16;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum WrapMode {
|
||||
|
|
@ -36,8 +35,8 @@ pub enum PixelFormat {
|
|||
F32,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PixelFormat {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
impl core::fmt::Display for PixelFormat {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
PixelFormat::U8 => write!(f, "U256"),
|
||||
PixelFormat::F16 => write!(f, "Half"),
|
||||
|
|
@ -72,7 +71,7 @@ impl PixelFormat {
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Pixels {
|
||||
U8(Ptr<u8>),
|
||||
F16(Ptr<f16>),
|
||||
F16(Ptr<u16>),
|
||||
F32(Ptr<f32>),
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +179,7 @@ impl ImageAccess for DeviceImage {
|
|||
}
|
||||
Pixels::F16(ptr) => {
|
||||
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),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use crate::core::material::{
|
|||
Material, MaterialEvalContext, MaterialTrait, NormalBumpEvalContext, bump_map, normal_map,
|
||||
};
|
||||
use crate::core::medium::{Medium, MediumInterface, PhaseFunction};
|
||||
use crate::core::options::get_options;
|
||||
use crate::core::sampler::{Sampler, SamplerTrait};
|
||||
use crate::core::shape::Shape;
|
||||
use crate::core::texture::{GPUFloatTexture, UniversalTextureEvaluator};
|
||||
|
|
@ -21,8 +20,6 @@ use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
|||
use crate::utils::Ptr;
|
||||
use crate::utils::math::{clamp, difference_of_products, square};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use std::any::Any;
|
||||
use std::default;
|
||||
|
||||
#[repr(C)]
|
||||
#[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(
|
||||
&mut self,
|
||||
tex_eval: &UniversalTextureEvaluator,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::materials::*;
|
||||
use core::ops::Deref;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::Float;
|
||||
use crate::bxdfs::{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use enum_dispatch::enum_dispatch;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::core::geometry::{
|
||||
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::rng::Rng;
|
||||
use crate::utils::transform::Transform;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -371,7 +371,7 @@ impl MediumProperties {
|
|||
}
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait MediumTrait: Send + Sync + std::fmt::Debug {
|
||||
pub trait MediumTrait: Send + Sync + core::fmt::Debug {
|
||||
fn is_emissive(&self) -> bool;
|
||||
fn sample_point(&self, p: Point3f, lambda: &SampledWavelengths) -> MediumProperties;
|
||||
fn sample_ray(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ pub mod interaction;
|
|||
pub mod light;
|
||||
pub mod material;
|
||||
pub mod medium;
|
||||
pub mod options;
|
||||
pub mod pbrt;
|
||||
pub mod primitive;
|
||||
pub mod sampler;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
use crate::core::geometry::Lerp;
|
||||
use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
||||
use core::ops::{Add, Mul};
|
||||
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::light::LightTrait;
|
||||
|
|
@ -15,40 +12,6 @@ use crate::utils::Ptr;
|
|||
|
||||
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"))]
|
||||
pub type FloatBits = u32;
|
||||
|
||||
|
|
@ -134,45 +97,50 @@ impl FloatBitOps for f64 {
|
|||
pub const MACHINE_EPSILON: Float = Float::EPSILON * 0.5;
|
||||
pub const SHADOW_EPSILON: Float = 0.0001;
|
||||
pub const ONE_MINUS_EPSILON: Float = 0.99999994;
|
||||
pub const PI: Float = std::f32::consts::PI;
|
||||
pub const INV_PI: Float = 0.318_309_886_183_790_671_54;
|
||||
pub const PI: Float = core::f32::consts::PI;
|
||||
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_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_4: Float = 0.785_398_163_397_448_309_61;
|
||||
pub const SQRT_2: Float = 1.414_213_562_373_095_048_80;
|
||||
pub const PI_OVER_2: Float = core::f32::consts::FRAC_PI_2;
|
||||
pub const PI_OVER_4: Float = core::f32::consts::FRAC_PI_4;
|
||||
pub const SQRT_2: Float = core::f32::consts::SQRT_2;
|
||||
|
||||
#[inline]
|
||||
pub fn gamma(n: i32) -> Float {
|
||||
n as Float * MACHINE_EPSILON / (1. - n as Float * MACHINE_EPSILON)
|
||||
}
|
||||
|
||||
pub static RARE_EVENT_TOTAL_CALLS: AtomicU64 = AtomicU64::new(0);
|
||||
pub static RARE_EVENT_CONDITION_MET: AtomicU64 = AtomicU64::new(0);
|
||||
#[cfg(feature = "cpu_debug")]
|
||||
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_rules! check_rare {
|
||||
($frequency_threshold:expr, $condition:expr) => {
|
||||
use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
||||
const CHECK_INTERVAL: u64 = 4096;
|
||||
#[macro_export]
|
||||
macro_rules! check_rare {
|
||||
($frequency_threshold:expr, $condition:expr) => {
|
||||
use core::sync::atomic::{AtomicU64, Ordering as SyncOrdering};
|
||||
const CHECK_INTERVAL: u64 = 4096;
|
||||
|
||||
let total_calls = RARE_EVENT_TOTAL_CALLS.fetch_add(1, SyncOrdering::Relaxed);
|
||||
let total_calls = RARE_EVENT_TOTAL_CALLS.fetch_add(1, SyncOrdering::Relaxed);
|
||||
|
||||
if $condition {
|
||||
RARE_EVENT_CONDITION_MET.fetch_add(1, SyncOrdering::Relaxed);
|
||||
}
|
||||
if $condition {
|
||||
RARE_EVENT_CONDITION_MET.fetch_add(1, SyncOrdering::Relaxed);
|
||||
}
|
||||
|
||||
if (total_calls + 1) % CHECK_INTERVAL == 0 {
|
||||
let met_count = RARE_EVENT_CONDITION_MET.load(SyncOrdering::Relaxed);
|
||||
if met_count > 0 {
|
||||
let frequency = met_count as f64 / (total_calls + 1) as f64;
|
||||
if frequency > $frequency_threshold {
|
||||
panic!(
|
||||
"Rare event occurred with frequency {} which is > threshold {}",
|
||||
frequency, $frequency_threshold
|
||||
);
|
||||
if (total_calls + 1) % CHECK_INTERVAL == 0 {
|
||||
let met_count = RARE_EVENT_CONDITION_MET.load(SyncOrdering::Relaxed);
|
||||
if met_count > 0 {
|
||||
let frequency = met_count as f64 / (total_calls + 1) as f64;
|
||||
if frequency > $frequency_threshold {
|
||||
panic!(
|
||||
"Rare event occurred with frequency {} which is > threshold {}",
|
||||
frequency, $frequency_threshold
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use crate::utils::hash::hash_float;
|
|||
use crate::utils::transform::{AnimatedTransform, Transform};
|
||||
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait PrimitiveTrait: Send + Sync {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use crate::core::filter::FilterTrait;
|
||||
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::utils::Ptr;
|
||||
use crate::utils::containers::DeviceArray2D;
|
||||
|
|
@ -14,7 +13,6 @@ use crate::utils::rng::Rng;
|
|||
use crate::utils::sobol::N_SOBOL_DIMENSIONS;
|
||||
use crate::utils::{hash::*, sobol};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use rand::seq::index::sample;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ use crate::core::pbrt::{Float, PI};
|
|||
use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum};
|
||||
use crate::utils::math::{clamp, lerp, safe_sqrt, square};
|
||||
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)]
|
||||
#[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.
|
||||
}
|
||||
|
||||
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 sin2_theta_i = 1. - square(cos_corr);
|
||||
let sin2_theta_t: Complex<Float> = sin2_theta_i / square(eta);
|
||||
let cos2_theta_t: Complex<Float> = (1. - sin2_theta_t).sqrt();
|
||||
let sin2_theta_t: Complex = sin2_theta_i / square(eta);
|
||||
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_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::{Float, INV_2_PI, INV_PI, PI};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
pub use crate::textures::*;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use crate::Float;
|
||||
use bytemuck::cast_slice;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
#[repr(C, align(16))]
|
||||
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::geometry::{Point2f, Vector2f};
|
||||
use crate::utils::math::{lerp, windowed_sinc};
|
||||
use rand::Rng;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
|
|
@ -10,6 +9,7 @@ pub struct LanczosSincFilter {
|
|||
pub radius: Vector2f,
|
||||
pub tau: Float,
|
||||
pub sampler: FilterSampler,
|
||||
pub integral: Float,
|
||||
}
|
||||
|
||||
impl FilterTrait for LanczosSincFilter {
|
||||
|
|
@ -23,26 +23,7 @@ impl FilterTrait for LanczosSincFilter {
|
|||
}
|
||||
|
||||
fn integral(&self) -> Float {
|
||||
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);
|
||||
}
|
||||
}
|
||||
sum / n_samples as Float * area
|
||||
self.integral
|
||||
}
|
||||
|
||||
fn sample(&self, u: Point2f) -> FilterSample {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::Float;
|
||||
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
|
||||
use crate::core::geometry::{Point2f, Vector2f};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use crate::Float;
|
|||
use crate::core::filter::{FilterSample, FilterTrait};
|
||||
use crate::core::geometry::{Point2f, Vector2f};
|
||||
use crate::utils::math::sample_tent;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#![allow(unused_imports, dead_code)]
|
||||
#![feature(float_erf)]
|
||||
#![feature(f16)]
|
||||
#![feature(associated_type_defaults)]
|
||||
#![no_std]
|
||||
|
||||
pub mod bxdfs;
|
||||
pub mod cameras;
|
||||
|
|
@ -16,4 +15,5 @@ pub mod textures;
|
|||
pub mod utils;
|
||||
|
||||
pub use core::pbrt::*;
|
||||
pub use utils::PBRTOptions;
|
||||
pub use utils::ptr::Ptr;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use crate::spectra::*;
|
|||
use crate::utils::hash::hash_float;
|
||||
use crate::utils::{Ptr, Transform};
|
||||
use crate::{Float, PI};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::core::spectrum::SpectrumTrait;
|
|||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::Ptr;
|
||||
use crate::{Float, PI};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::utils::sampling::{
|
|||
};
|
||||
use crate::utils::{Ptr, Transform};
|
||||
use crate::{Float, PI};
|
||||
use std::sync::Arc;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use crate::core::spectrum::SpectrumTrait;
|
|||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::Ptr;
|
||||
use crate::{Float, PI};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use crate::{
|
|||
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
|
||||
utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D},
|
||||
};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use crate::utils::ptr::Ptr;
|
|||
use crate::utils::sampling::AliasTable;
|
||||
use crate::{Float, ONE_MINUS_EPSILON, PI};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[repr(C)]
|
||||
|
|
@ -26,7 +27,7 @@ pub struct CompactLightBounds {
|
|||
pub qb: [[u16; 3]; 2],
|
||||
}
|
||||
|
||||
const _: () = assert!(std::mem::size_of::<CompactLightBounds>() == 24);
|
||||
const _: () = assert!(core::mem::size_of::<CompactLightBounds>() == 24);
|
||||
|
||||
impl CompactLightBounds {
|
||||
pub fn new(lb: &LightBounds, all_b: &Bounds3f) -> Self {
|
||||
|
|
@ -291,7 +292,7 @@ pub struct LightBVHNode {
|
|||
packed_data: u32,
|
||||
}
|
||||
|
||||
const _: () = assert!(std::mem::size_of::<LightBVHNode>() == 32);
|
||||
const _: () = assert!(core::mem::size_of::<LightBVHNode>() == 32);
|
||||
|
||||
impl LightBVHNode {
|
||||
/// 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::utils::Ptr;
|
||||
use crate::{Float, PI};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ pub struct CoatedDiffuseMaterial {
|
|||
pub max_depth: u32,
|
||||
pub n_samples: u32,
|
||||
pub remap_roughness: bool,
|
||||
pub seed: i32,
|
||||
}
|
||||
|
||||
impl CoatedDiffuseMaterial {
|
||||
|
|
@ -45,6 +46,7 @@ impl CoatedDiffuseMaterial {
|
|||
remap_roughness: bool,
|
||||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
seed: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
displacement,
|
||||
|
|
@ -59,6 +61,7 @@ impl CoatedDiffuseMaterial {
|
|||
remap_roughness,
|
||||
max_depth,
|
||||
n_samples,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -112,6 +115,7 @@ impl MaterialTrait for CoatedDiffuseMaterial {
|
|||
gg,
|
||||
self.max_depth,
|
||||
self.n_samples,
|
||||
self.seed,
|
||||
));
|
||||
|
||||
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
||||
|
|
@ -165,6 +169,7 @@ pub struct CoatedConductorMaterial {
|
|||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
remap_roughness: bool,
|
||||
seed: i32,
|
||||
}
|
||||
|
||||
impl CoatedConductorMaterial {
|
||||
|
|
@ -186,6 +191,7 @@ impl CoatedConductorMaterial {
|
|||
max_depth: u32,
|
||||
n_samples: u32,
|
||||
remap_roughness: bool,
|
||||
seed: i32,
|
||||
) -> Self {
|
||||
Self {
|
||||
displacement,
|
||||
|
|
@ -204,6 +210,7 @@ impl CoatedConductorMaterial {
|
|||
remap_roughness,
|
||||
max_depth,
|
||||
n_samples,
|
||||
seed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -279,6 +286,7 @@ impl MaterialTrait for CoatedConductorMaterial {
|
|||
gg,
|
||||
self.max_depth,
|
||||
self.n_samples,
|
||||
self.seed,
|
||||
));
|
||||
BSDF::new(ctx.ns, ctx.dpdus, Ptr::from(&bxdf))
|
||||
}
|
||||
|
|
@ -302,20 +310,24 @@ impl MaterialTrait for CoatedConductorMaterial {
|
|||
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() {
|
||||
spectrum_textures.push(self.conductor_eta);
|
||||
spectrum_textures[n] = self.conductor_eta;
|
||||
n += 1;
|
||||
}
|
||||
|
||||
if !self.k.is_null() {
|
||||
spectrum_textures.push(self.k);
|
||||
spectrum_textures[n] = self.k;
|
||||
n += 1;
|
||||
}
|
||||
|
||||
if !self.conductor_eta.is_null() {
|
||||
spectrum_textures.push(self.reflectance);
|
||||
spectrum_textures[n] = self.reflectance;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
use crate::utils::transform::{Transform, look_at};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ use crate::utils::splines::{
|
|||
use crate::utils::transform::{Transform, look_at};
|
||||
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::math::{clamp, difference_of_products, lerp, square};
|
||||
use std::mem;
|
||||
use core::mem;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -68,13 +69,13 @@ impl CylinderShape {
|
|||
let f = b / (2. * a);
|
||||
let vx: Interval = oi.x() - f * di.x();
|
||||
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 =
|
||||
4. * a * (Interval::new(self.radius) * length) * (Interval::new(self.radius) - length);
|
||||
if discrim.low < 0. {
|
||||
return None;
|
||||
}
|
||||
let root_discrim = discrim.sqrt();
|
||||
let root_discrim = discrim.sqrt_ext();
|
||||
let q = if Float::from(b) < 0. {
|
||||
-0.5 * (b - root_discrim)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::utils::Transform;
|
|||
use crate::utils::math::square;
|
||||
use crate::utils::sampling::sample_uniform_disk_concentric;
|
||||
use crate::{Float, PI};
|
||||
use std::sync::Arc;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::core::geometry::{
|
|||
Bounds3f, DirectionCone, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3f,
|
||||
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::pbrt::gamma;
|
||||
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::sampling::sample_uniform_sphere;
|
||||
use crate::{Float, PI};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
use core::mem;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -94,7 +94,7 @@ impl SphereShape {
|
|||
return None;
|
||||
}
|
||||
|
||||
let root_discrim = discrim.sqrt();
|
||||
let root_discrim = discrim.sqrt_ext();
|
||||
|
||||
let q = if Float::from(b) < 0. {
|
||||
-0.5 * (b - root_discrim)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::core::geometry::{
|
|||
Bounds3f, DirectionCone, Normal, Normal3f, Point2f, Point3f, Point3fi, Ray, Vector2f, Vector3,
|
||||
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::{
|
||||
Interaction, InteractionBase, InteractionTrait, SimpleInteraction, SurfaceInteraction,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,9 +4,7 @@ use crate::core::pbrt::Float;
|
|||
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum};
|
||||
use crate::utils::math::SquareMatrix3f;
|
||||
use crate::utils::ptr::Ptr;
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use std::cmp::{Eq, PartialEq};
|
||||
use core::cmp::{Eq, PartialEq};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
|
|
@ -19,13 +17,14 @@ pub struct DeviceStandardColorSpaces {
|
|||
|
||||
impl DeviceStandardColorSpaces {
|
||||
#[cfg(not(target_arch = "nvptx64"))]
|
||||
pub fn get_named(&self, name: &str) -> Result<Ptr<RGBColorSpace>> {
|
||||
match name.to_lowercase().as_str() {
|
||||
"srgb" => Ok(self.srgb),
|
||||
"dci-p3" => Ok(self.dci_p3),
|
||||
"rec2020" => Ok(self.rec2020),
|
||||
"aces2065-1" => Ok(self.aces2065_1),
|
||||
_ => Err(anyhow!("No such spectrum")),
|
||||
pub fn get_named(&self, name: &str) -> Option<Ptr<RGBColorSpace>> {
|
||||
let lower = name.as_bytes();
|
||||
match lower {
|
||||
b if b.eq_ignore_ascii_case(b"srgb") => Some(self.srgb),
|
||||
b if b.eq_ignore_ascii_case(b"dci-p3") => Some(self.dci_p3),
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,11 +50,12 @@ pub enum ColorSpaceId {
|
|||
impl ColorSpaceId {
|
||||
#[cfg(not(target_arch = "nvptx64"))]
|
||||
pub fn from_name(name: &str) -> Option<Self> {
|
||||
match name.to_lowercase().as_str() {
|
||||
"srgb" => Some(Self::SRGB),
|
||||
"dci-p3" => Some(Self::DciP3),
|
||||
"rec2020" => Some(Self::Rec2020),
|
||||
"aces2065-1" => Some(Self::Aces2065_1),
|
||||
let lower = name.as_bytes();
|
||||
match lower {
|
||||
b if b.eq_ignore_ascii_case(b"srgb") => Some(Self::SRGB),
|
||||
b if b.eq_ignore_ascii_case(b"dci-p3") => Some(Self::DciP3),
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use crate::core::pbrt::Float;
|
||||
use crate::core::spectrum::{SpectrumTrait, StandardSpectra};
|
||||
use crate::utils::math::{clamp, lerp};
|
||||
use std::ops::{
|
||||
use core::ops::{
|
||||
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;
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ impl SampledSpectrum {
|
|||
F: FnMut(usize) -> Float,
|
||||
{
|
||||
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 {
|
||||
type Item = &'a Float;
|
||||
type IntoIter = std::slice::Iter<'a, Float>;
|
||||
type IntoIter = core::slice::Iter<'a, Float>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.values.iter()
|
||||
|
|
@ -139,7 +140,7 @@ impl<'a> IntoIterator for &'a SampledSpectrum {
|
|||
|
||||
impl<'a> IntoIterator for &'a mut SampledSpectrum {
|
||||
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 {
|
||||
self.values.iter_mut()
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ use crate::spectra::{N_SPECTRUM_SAMPLES, SampledSpectrum, SampledWavelengths};
|
|||
use crate::utils::find_interval;
|
||||
use crate::utils::ptr::Ptr;
|
||||
use core::slice;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::LazyLock;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -68,17 +67,6 @@ impl PartialEq 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 {
|
||||
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
let mut s = SampledSpectrum::default();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use crate::core::texture::{
|
|||
};
|
||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::{Ptr, math::square};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
fn checkerboard(
|
||||
ctx: &TextureEvalContext,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::spectra::sampled::{SampledSpectrum, SampledWavelengths};
|
|||
use crate::utils::Ptr;
|
||||
use crate::utils::math::square;
|
||||
use crate::utils::noise::noise_2d;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
fn inside_polka_dot(st: Point2f) -> bool {
|
||||
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::ptr::Ptr;
|
||||
use crate::utils::splines::evaluate_cubic_bezier;
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
#[repr(C)]
|
||||
#[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::utils::Ptr;
|
||||
use crate::utils::math::lerp;
|
||||
use std::collections::HashSet;
|
||||
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};
|
||||
use core::ops::{Add, Index, IndexMut, Mul, Sub};
|
||||
|
||||
pub trait Interpolatable:
|
||||
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 {
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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 std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use crate::Float;
|
||||
|
||||
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 {
|
||||
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 bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
|
||||
let bytes = unsafe { core::slice::from_raw_parts(ptr, len) };
|
||||
murmur_hash_64a(bytes, seed)
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +70,7 @@ macro_rules! hash_values {
|
|||
// We use a temporary tuple or array to pack bits
|
||||
#[repr(C, packed)]
|
||||
struct PackedData {
|
||||
$(_field: Box<[u8]>),* // Phantom, logic below is simpler
|
||||
$(_field: Box<[u8]>),*
|
||||
}
|
||||
|
||||
// Calculate total size and create a byte buffer
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::core::pbrt::Float;
|
||||
use crate::utils::math::{next_float_down, next_float_up};
|
||||
use num_traits::Zero;
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use core::ops::{Add, Div, Mul, Neg, Sub};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use super::error::{InversionError, LlsError};
|
||||
use crate::core::color::{RGB, XYZ};
|
||||
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};
|
||||
|
|
@ -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::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 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]
|
||||
pub fn degrees(a: Float) -> Float {
|
||||
|
|
@ -69,7 +66,7 @@ pub fn evaluate_polynomial(t: Float, coeffs: &[Float]) -> Float {
|
|||
assert!(!coeffs.is_empty());
|
||||
let mut result = coeffs[0];
|
||||
for &c in &coeffs[1..] {
|
||||
result = t.mul_add(result, c);
|
||||
result = num_traits::Float::mul_add(t, result, c);
|
||||
}
|
||||
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>(
|
||||
a: [[Float; R]; N],
|
||||
b: [[Float; R]; N],
|
||||
) -> Result<SquareMatrix<Float, R>, Box<dyn Error>> {
|
||||
) -> Option<SquareMatrix<Float, R>> {
|
||||
let am = Matrix::from(a);
|
||||
let bm = Matrix::from(b);
|
||||
let ata = am.transpose() * am;
|
||||
let atb = am.transpose() * bm;
|
||||
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
|
||||
|
|
@ -473,10 +470,24 @@ pub fn gaussian(x: Float, y: Float, sigma: Float) -> Float {
|
|||
(-(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 {
|
||||
assert!(sigma > 0.);
|
||||
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 {
|
||||
|
|
@ -1079,7 +1090,7 @@ impl<T, const R: usize, const C: usize> Matrix<T, R, C> {
|
|||
where
|
||||
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 }
|
||||
}
|
||||
|
||||
|
|
@ -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> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut col_widths = [0; C];
|
||||
|
||||
// Measure without allocation
|
||||
for row in self.m.iter() {
|
||||
for (j, element) in row.iter().enumerate() {
|
||||
let width = format!("{}", element).len();
|
||||
if width > col_widths[j] {
|
||||
col_widths[j] = width;
|
||||
}
|
||||
let mut counter = CountingFmt(0);
|
||||
let _ = write!(counter, "{}", element); // Ignores errors, just counts
|
||||
col_widths[j] = col_widths[j].max(counter.0);
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..R {
|
||||
write!(f, "[")?;
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for j in 0..C {
|
||||
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,
|
||||
{
|
||||
Self {
|
||||
m: std::array::from_fn(|i| {
|
||||
std::array::from_fn(|j| if i == j { T::one() } else { T::zero() })
|
||||
m: core::array::from_fn(|i| {
|
||||
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
|
||||
T: NumFloat + Sum + Product + Copy,
|
||||
{
|
||||
pub fn inverse(&self) -> Result<Self, InversionError> {
|
||||
pub fn inverse(&self) -> Option<Self> {
|
||||
if N == 0 {
|
||||
return Err(InversionError::EmptyMatrix);
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut mat = self.m;
|
||||
|
|
@ -1322,7 +1342,7 @@ where
|
|||
|
||||
let pivot = mat[i][i];
|
||||
if pivot.is_zero() {
|
||||
return Err(InversionError::SingularMatrix);
|
||||
return None;
|
||||
}
|
||||
|
||||
let inv_pivot = T::one() / pivot;
|
||||
|
|
@ -1344,7 +1364,8 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(inv)
|
||||
|
||||
Some(inv)
|
||||
}
|
||||
|
||||
pub fn determinant(&self) -> T {
|
||||
|
|
@ -1448,7 +1469,8 @@ where
|
|||
{
|
||||
type Output = Vector<T, N>;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1459,7 +1481,8 @@ where
|
|||
{
|
||||
type Output = Point<T, N>;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -1540,31 +1563,7 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
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 {
|
||||
pub fn f16_to_f32_software(h: u16) -> f32 {
|
||||
let sign = ((h >> 15) & 1) as u32;
|
||||
let exp = ((h >> 10) & 0x1F) as u32;
|
||||
let mant = (h & 0x3FF) as u32;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
pub mod complex;
|
||||
pub mod containers;
|
||||
pub mod error;
|
||||
pub mod hash;
|
||||
pub mod interval;
|
||||
pub mod math;
|
||||
pub mod mesh;
|
||||
pub mod noise;
|
||||
pub mod options;
|
||||
pub mod ptr;
|
||||
pub mod quaternion;
|
||||
pub mod rng;
|
||||
|
|
@ -13,11 +14,12 @@ pub mod sobol;
|
|||
pub mod splines;
|
||||
pub mod transform;
|
||||
|
||||
pub use options::PBRTOptions;
|
||||
pub use ptr::Ptr;
|
||||
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
|
||||
|
||||
use crate::Float;
|
||||
use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
#[inline]
|
||||
pub fn find_interval<F>(sz: u32, pred: F) -> u32
|
||||
|
|
@ -113,37 +115,37 @@ impl AtomicFloat {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct AtomicDouble {
|
||||
bits: AtomicU64,
|
||||
}
|
||||
|
||||
impl AtomicDouble {
|
||||
pub fn new(val: f64) -> Self {
|
||||
Self {
|
||||
bits: AtomicU64::new(val.to_bits()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> f64 {
|
||||
f64::from_bits(self.bits.load(Ordering::Relaxed))
|
||||
}
|
||||
|
||||
pub fn add(&self, val: f64) {
|
||||
let mut current_bits = self.bits.load(Ordering::Relaxed);
|
||||
loop {
|
||||
let current_val = f64::from_bits(current_bits);
|
||||
let new_val = current_val + val;
|
||||
let new_bits = new_val.to_bits();
|
||||
|
||||
match self.bits.compare_exchange_weak(
|
||||
current_bits,
|
||||
new_bits,
|
||||
Ordering::Relaxed,
|
||||
Ordering::Relaxed,
|
||||
) {
|
||||
Ok(_) => break,
|
||||
Err(x) => current_bits = x,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// pub struct AtomicDouble {
|
||||
// bits: AtomicU64,
|
||||
// }
|
||||
//
|
||||
// impl AtomicDouble {
|
||||
// pub fn new(val: f64) -> Self {
|
||||
// Self {
|
||||
// bits: AtomicU64::new(val.to_bits()),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// pub fn get(&self) -> f64 {
|
||||
// f64::from_bits(self.bits.load(Ordering::Relaxed))
|
||||
// }
|
||||
//
|
||||
// pub fn add(&self, val: f64) {
|
||||
// let mut current_bits = self.bits.load(Ordering::Relaxed);
|
||||
// loop {
|
||||
// let current_val = f64::from_bits(current_bits);
|
||||
// let new_val = current_val + val;
|
||||
// let new_bits = new_val.to_bits();
|
||||
//
|
||||
// match self.bits.compare_exchange_weak(
|
||||
// current_bits,
|
||||
// new_bits,
|
||||
// Ordering::Relaxed,
|
||||
// Ordering::Relaxed,
|
||||
// ) {
|
||||
// Ok(_) => break,
|
||||
// Err(x) => current_bits = x,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::Float;
|
||||
use crate::core::geometry::{Point3f, Vector3f, VectorLike};
|
||||
use crate::utils::math::{clamp, lerp, smooth_step};
|
||||
use num_traits::Float as NumFloat;
|
||||
|
||||
static NOISE_PERM_SIZE: usize = 256;
|
||||
static NOISE_PERM: [i32; 2 * NOISE_PERM_SIZE] = [
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
||||
use crate::Float;
|
||||
use std::ops::Deref;
|
||||
use std::sync::OnceLock;
|
||||
use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i};
|
||||
use core::ops::Deref;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum RenderingCoordinateSystem {
|
||||
|
|
@ -48,17 +47,17 @@ pub struct PBRTOptions {
|
|||
pub basic: BasicPBRTOptions,
|
||||
|
||||
pub n_threads: usize,
|
||||
pub log_level: String,
|
||||
pub log_level: &'static str,
|
||||
pub write_partial_images: bool,
|
||||
pub image_file: String,
|
||||
pub image_file: &'static str,
|
||||
pub pixel_samples: Option<i32>,
|
||||
pub gpu_device: Option<u32>,
|
||||
pub mse_reference_image: Option<String>,
|
||||
pub mse_reference_output: Option<String>,
|
||||
pub mse_reference_image: Option<&'static str>,
|
||||
pub mse_reference_output: Option<&'static str>,
|
||||
pub debug_start: Option<(Point2i, i32)>,
|
||||
pub quick_render: bool,
|
||||
pub upgrade: bool,
|
||||
pub display_server: String,
|
||||
pub display_server: &'static str,
|
||||
pub crop_window: Option<Bounds2f>,
|
||||
pub pixel_bounds: Option<Bounds2i>,
|
||||
pub pixel_material: Option<Point2i>,
|
||||
|
|
@ -70,17 +69,17 @@ impl Default for PBRTOptions {
|
|||
Self {
|
||||
basic: BasicPBRTOptions::default(),
|
||||
n_threads: 0,
|
||||
log_level: "info".to_string(),
|
||||
log_level: "info",
|
||||
write_partial_images: false,
|
||||
pixel_samples: None,
|
||||
gpu_device: None,
|
||||
quick_render: false,
|
||||
upgrade: false,
|
||||
image_file: "output.exr".to_string(),
|
||||
image_file: "output.exr",
|
||||
mse_reference_image: None,
|
||||
mse_reference_output: None,
|
||||
debug_start: Some((Point2i::default(), 0)),
|
||||
display_server: "".to_string(),
|
||||
display_server: "",
|
||||
crop_window: None,
|
||||
pixel_bounds: None,
|
||||
pixel_material: None,
|
||||
|
|
@ -96,22 +95,3 @@ impl Deref for PBRTOptions {
|
|||
&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> {
|
||||
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> {
|
||||
pub const fn null() -> Self {
|
||||
Self {
|
||||
ptr: std::ptr::null(),
|
||||
ptr: core::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ impl<T> Ptr<T> {
|
|||
if len == 0 {
|
||||
&[]
|
||||
} 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 len == 0 { Some(&[]) } else { None }
|
||||
} 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;
|
||||
|
||||
#[inline(always)]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use std::ops::{Index, IndexMut};
|
||||
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
use crate::core::geometry::{Vector3f, VectorLike};
|
||||
use crate::utils::math::{safe_asin, sinx_over_x};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
use crate::check_rare;
|
||||
use crate::core::geometry::{
|
||||
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::find_interval;
|
||||
use crate::utils::math::{
|
||||
|
|
@ -12,8 +10,12 @@ use crate::utils::math::{
|
|||
use crate::utils::ptr::Ptr;
|
||||
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 num_traits::Float as NumFloat;
|
||||
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
|
||||
where
|
||||
T: Num + Copy + PartialOrd,
|
||||
|
|
@ -267,7 +269,7 @@ pub fn invert_spherical_rectangle_sample(
|
|||
let b0sq = square(b0) + square(b1);
|
||||
|
||||
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;
|
||||
|
||||
if solid_angle < 1e-3 {
|
||||
|
|
@ -289,6 +291,7 @@ pub fn invert_spherical_rectangle_sample(
|
|||
let invcusq = 1. + z0sq / square(xu);
|
||||
let fusq = invcusq - b0sq;
|
||||
let fu = fusq.sqrt().copysign(xu);
|
||||
#[cfg(feature = "cpu_debug")]
|
||||
check_rare!(1e-6, fu == 0.);
|
||||
let sqrt = safe_sqrt(difference_of_products(b0, b0, b1, b1) + fusq);
|
||||
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 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::quaternion::Quaternion;
|
||||
|
|
@ -13,8 +12,7 @@ use crate::core::geometry::{
|
|||
use crate::core::interaction::{
|
||||
Interaction, InteractionBase, InteractionTrait, MediumInteraction, SurfaceInteraction,
|
||||
};
|
||||
use crate::core::pbrt::{Float, gamma};
|
||||
use crate::utils::error::InversionError;
|
||||
use crate::{Float, gamma};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
@ -28,15 +26,15 @@ impl<T: NumFloat + Sum + Product> TransformGeneric<T> {
|
|||
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()?;
|
||||
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 inv = m.inverse()?;
|
||||
Ok(Self { m, m_inv: inv })
|
||||
Some(Self { m, m_inv: inv })
|
||||
}
|
||||
|
||||
pub fn identity() -> Self {
|
||||
|
|
@ -444,11 +442,7 @@ impl TransformGeneric<Float> {
|
|||
Self { m, m_inv }
|
||||
}
|
||||
|
||||
pub fn perspective(
|
||||
fov: Float,
|
||||
n: Float,
|
||||
f: Float,
|
||||
) -> Result<TransformGeneric<Float>, InversionError> {
|
||||
pub fn perspective(fov: Float, n: Float, f: Float) -> Option<TransformGeneric<Float>> {
|
||||
let persp: SquareMatrix<Float, 4> = SquareMatrix::new([
|
||||
[1., 0., 0., 0.],
|
||||
[0., 1., 0., 0.],
|
||||
|
|
@ -457,7 +451,7 @@ impl TransformGeneric<Float> {
|
|||
]);
|
||||
let inv_tan_ang = 1. / (radians(fov) / 2.).tan();
|
||||
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 {
|
||||
|
|
@ -1971,11 +1965,11 @@ impl AnimatedTransform {
|
|||
(c1, c2, c3, c4, c5)
|
||||
} else {
|
||||
(
|
||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
||||
std::array::from_fn(|_| DerivativeTerm::default()),
|
||||
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||
core::array::from_fn(|_| DerivativeTerm::default()),
|
||||
)
|
||||
};
|
||||
AnimatedTransform {
|
||||
|
|
@ -2093,7 +2087,7 @@ pub fn look_at(
|
|||
pos: impl Into<Point3f>,
|
||||
look: impl Into<Point3f>,
|
||||
up: impl Into<Point3f>,
|
||||
) -> Result<TransformGeneric<Float>, InversionError> {
|
||||
) -> Option<TransformGeneric<Float>> {
|
||||
let mut world_from_camera: SquareMatrix<Float, 4> = SquareMatrix::default();
|
||||
// Initialize fourth column of viewing matrix
|
||||
let pos: Point3f = pos.into();
|
||||
|
|
@ -2133,5 +2127,5 @@ pub fn look_at(
|
|||
world_from_camera[3][2] = 0.;
|
||||
|
||||
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>,
|
||||
params: &ParameterDictionary,
|
||||
loc: &FileLoc,
|
||||
) -> Self {
|
||||
let mut shutter_open = params.get_one_float("shutteropen", 0.);
|
||||
let mut shutter_close = params.get_one_float("shutterclose", 1.);
|
||||
) -> Result<Self> {
|
||||
let mut shutter_open = params.get_one_float("shutteropen", 0.)?;
|
||||
let mut shutter_close = params.get_one_float("shutterclose", 1.)?;
|
||||
if shutter_close < shutter_open {
|
||||
eprint!(
|
||||
"{}: Shutter close time {} < shutter open {}. Swapping",
|
||||
|
|
@ -44,13 +44,14 @@ impl CameraBaseParameters {
|
|||
);
|
||||
std::mem::swap(&mut shutter_open, &mut shutter_close);
|
||||
}
|
||||
CameraBaseParameters {
|
||||
|
||||
Ok(CameraBaseParameters {
|
||||
camera_transform: camera_transform.clone(),
|
||||
shutter_open,
|
||||
shutter_close,
|
||||
film,
|
||||
medium,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,14 +121,14 @@ impl CameraFactory for Camera {
|
|||
"perspective" => {
|
||||
let full_res = film.full_resolution();
|
||||
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 lens_radius = params.get_one_float("lensradius", 0.);
|
||||
let focal_distance = params.get_one_float("focaldistance", 1e6);
|
||||
let lens_radius = params.get_one_float("lensradius", 0.)?;
|
||||
let focal_distance = params.get_one_float("focaldistance", 1e6)?;
|
||||
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.))
|
||||
|
|
@ -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 get_options().fullscreen {
|
||||
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);
|
||||
arena.alloc(camera);
|
||||
|
||||
|
|
@ -167,14 +168,14 @@ impl CameraFactory for Camera {
|
|||
"orthographic" => {
|
||||
let full_res = film.full_resolution();
|
||||
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 lens_radius = params.get_one_float("lensradius", 0.);
|
||||
let focal_distance = params.get_one_float("focaldistance", 1e6);
|
||||
let lens_radius = params.get_one_float("lensradius", 0.)?;
|
||||
let focal_distance = params.get_one_float("focaldistance", 1e6)?;
|
||||
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.))
|
||||
|
|
@ -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 get_options().fullscreen {
|
||||
eprint!("Screenwindow is ignored in fullscreen mode");
|
||||
|
|
@ -209,11 +210,11 @@ impl CameraFactory for Camera {
|
|||
}
|
||||
"realistic" => {
|
||||
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 aperture_diameter = params.get_one_float("aperturediameter", 1.);
|
||||
let focal_distance = params.get_one_float("focaldistance", 10.);
|
||||
let lens_file = params.get_one_string("lensfile", "");
|
||||
let aperture_diameter = params.get_one_float("aperturediameter", 1.)?;
|
||||
let focal_distance = params.get_one_float("focaldistance", 10.)?;
|
||||
let lens_file = params.get_one_string("lensfile", "")?;
|
||||
|
||||
if lens_file.is_empty() {
|
||||
return Err(anyhow!("{}: No lens file supplied", loc));
|
||||
|
|
@ -275,7 +276,7 @@ impl CameraFactory for Camera {
|
|||
image
|
||||
};
|
||||
|
||||
let aperture_name = params.get_one_string("aperture", "");
|
||||
let aperture_name = params.get_one_string("aperture", "")?;
|
||||
let mut aperture_image: Option<Image> = None;
|
||||
|
||||
if !aperture_name.is_empty() {
|
||||
|
|
@ -389,44 +390,9 @@ impl CameraFactory for Camera {
|
|||
"spherical" => {
|
||||
let full_res = film.full_resolution();
|
||||
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 lens_radius = params.get_one_float("lensradius", 0.);
|
||||
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 m = params.get_one_string("mapping", "equalarea")?;
|
||||
let mapping = match m.as_str() {
|
||||
"equalarea" => Mapping::EqualArea,
|
||||
"equirectangular" => Mapping::EquiRectangular,
|
||||
|
|
|
|||
|
|
@ -56,9 +56,9 @@ impl PixelSensor {
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let iso = params.get_one_float("iso", 100.);
|
||||
let mut white_balance_temp = params.get_one_float("whitebalance", 0.);
|
||||
let sensor_name = params.get_one_string("sensor", "cie1931");
|
||||
let iso = params.get_one_float("iso", 100.)?;
|
||||
let mut white_balance_temp = params.get_one_float("whitebalance", 0.)?;
|
||||
let sensor_name = params.get_one_string("sensor", "cie1931")?;
|
||||
if sensor_name != "cie1931" && white_balance_temp == 0. {
|
||||
white_balance_temp = 6500.;
|
||||
}
|
||||
|
|
@ -238,7 +238,9 @@ pub trait CreateFilmBase {
|
|||
filter: Filter,
|
||||
sensor: Option<&DevicePixelSensor>,
|
||||
loc: &FileLoc,
|
||||
) -> Self;
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl CreateFilmBase for FilmBase {
|
||||
|
|
@ -247,9 +249,12 @@ impl CreateFilmBase for FilmBase {
|
|||
filter: Filter,
|
||||
sensor: Option<&DevicePixelSensor>,
|
||||
loc: &FileLoc,
|
||||
) -> Self {
|
||||
let x_res = params.get_one_int("xresolution", 1280);
|
||||
let y_res = params.get_one_int("yresolution", 720);
|
||||
) -> Result<Self>
|
||||
where
|
||||
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 {
|
||||
eprintln!(
|
||||
|
|
@ -259,7 +264,7 @@ impl CreateFilmBase for FilmBase {
|
|||
}
|
||||
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 {
|
||||
Bounds2f::from_points(
|
||||
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);
|
||||
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");
|
||||
|
||||
Self {
|
||||
Ok(Self {
|
||||
full_resolution,
|
||||
pixel_bounds,
|
||||
filter,
|
||||
diagonal: diagonal_mm * 0.001,
|
||||
sensor: Ptr::from(sensor.unwrap()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,53 +2,54 @@ use crate::filters::*;
|
|||
use crate::utils::containers::Array2D;
|
||||
use crate::utils::sampling::PiecewiseConstant2D;
|
||||
use crate::utils::{FileLoc, ParameterDictionary};
|
||||
use anyhow::{Result, anyhow};
|
||||
use shared::Float;
|
||||
use shared::core::filter::{Filter, FilterSampler};
|
||||
use shared::core::geometry::{Bounds2f, Point2f, Vector2f};
|
||||
use shared::filters::*;
|
||||
|
||||
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 {
|
||||
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Self, String> {
|
||||
fn create(name: &str, params: &ParameterDictionary, loc: &FileLoc) -> Result<Self> {
|
||||
match name {
|
||||
"box" => {
|
||||
let xw = params.get_one_float("xradius", 0.5);
|
||||
let yw = params.get_one_float("yradius", 0.5);
|
||||
let xw = params.get_one_float("xradius", 0.5)?;
|
||||
let yw = params.get_one_float("yradius", 0.5)?;
|
||||
let filter = BoxFilter::new(Vector2f::new(xw, yw));
|
||||
Ok(Filter::Box(filter))
|
||||
}
|
||||
"gaussian" => {
|
||||
let xw = params.get_one_float("xradius", 1.5);
|
||||
let yw = params.get_one_float("yradius", 1.5);
|
||||
let sigma = params.get_one_float("sigma", 0.5);
|
||||
let xw = params.get_one_float("xradius", 1.5)?;
|
||||
let yw = params.get_one_float("yradius", 1.5)?;
|
||||
let sigma = params.get_one_float("sigma", 0.5)?;
|
||||
let filter = GaussianFilter::new(Vector2f::new(xw, yw), sigma);
|
||||
Ok(Filter::Gaussian(filter))
|
||||
}
|
||||
"mitchell" => {
|
||||
let xw = params.get_one_float("xradius", 2.);
|
||||
let yw = params.get_one_float("yradius", 2.);
|
||||
let b = params.get_one_float("B", 1. / 3.);
|
||||
let c = params.get_one_float("C", 1. / 3.);
|
||||
let xw = params.get_one_float("xradius", 2.)?;
|
||||
let yw = params.get_one_float("yradius", 2.)?;
|
||||
let b = params.get_one_float("B", 1. / 3.)?;
|
||||
let c = params.get_one_float("C", 1. / 3.)?;
|
||||
let filter = MitchellFilter::new(Vector2f::new(xw, yw), b, c);
|
||||
Ok(Filter::Mitchell(filter))
|
||||
}
|
||||
"sinc" => {
|
||||
let xw = params.get_one_float("xradius", 4.);
|
||||
let yw = params.get_one_float("yradius", 4.);
|
||||
let tau = params.get_one_float("tau", 3.);
|
||||
let xw = params.get_one_float("xradius", 4.)?;
|
||||
let yw = params.get_one_float("yradius", 4.)?;
|
||||
let tau = params.get_one_float("tau", 3.)?;
|
||||
let filter = LanczosSincFilter::new(Vector2f::new(xw, yw), tau);
|
||||
Ok(Filter::LanczosSinc(filter))
|
||||
}
|
||||
"triangle" => {
|
||||
let xw = params.get_one_float("xradius", 2.);
|
||||
let yw = params.get_one_float("yradius", 2.);
|
||||
let xw = params.get_one_float("xradius", 2.)?;
|
||||
let yw = params.get_one_float("yradius", 2.)?;
|
||||
let filter = TriangleFilter::new(Vector2f::new(xw, yw));
|
||||
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 filter;
|
||||
pub mod image;
|
||||
pub mod interaction;
|
||||
pub mod light;
|
||||
pub mod material;
|
||||
pub mod medium;
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ use crate::Arena;
|
|||
use crate::spectra::get_colorspace_device;
|
||||
use crate::utils::error::FileLoc;
|
||||
use crate::utils::normalize_utf8;
|
||||
use crate::utils::parameters::error_exit;
|
||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||
use crate::utils::parser::ParserTarget;
|
||||
use crate::utils::parser::{ParserError, ParserTarget};
|
||||
use shared::Float;
|
||||
use shared::core::camera::CameraTransform;
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
fn reverse_orientation(&mut self, loc: FileLoc) {
|
||||
self.verify_world("ReverseOrientation", &loc);
|
||||
fn reverse_orientation(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||
self.verify_world("ReverseOrientation", &loc)?;
|
||||
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 _ = match stdcs.get_named(name) {
|
||||
Ok(cs) => {
|
||||
|
|
@ -199,25 +222,43 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
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());
|
||||
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));
|
||||
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));
|
||||
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);
|
||||
self.for_active_transforms(|cur| cur * &t);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn look_at(
|
||||
|
|
@ -232,7 +273,7 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
uy: Float,
|
||||
uz: Float,
|
||||
loc: FileLoc,
|
||||
) {
|
||||
) -> Result<(), ParserError> {
|
||||
let result = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz));
|
||||
match result {
|
||||
Ok(t) => {
|
||||
|
|
@ -242,9 +283,10 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
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);
|
||||
match result {
|
||||
Ok(t) => {
|
||||
|
|
@ -254,9 +296,10 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
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);
|
||||
match result {
|
||||
Ok(t) => {
|
||||
|
|
@ -266,14 +309,16 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
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
|
||||
.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) {
|
||||
self.graphics_state.ctm = *saved_ctm;
|
||||
} else {
|
||||
|
|
@ -282,10 +327,16 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
name, loc
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn camera(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
||||
self.verify_options("Camera", &loc);
|
||||
fn camera(
|
||||
&mut self,
|
||||
name: &str,
|
||||
params: &ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
) -> Result<(), ParserError> {
|
||||
self.verify_options("Camera", &loc)?;
|
||||
|
||||
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);
|
||||
self.render_from_world = camera_from_world.t[0];
|
||||
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
let parameters = self.make_params(¶ms.clone(), &loc)?;
|
||||
|
||||
self.current_camera = Some(CameraSceneEntity {
|
||||
base: SceneEntity {
|
||||
|
|
@ -319,85 +369,119 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transform_times(&mut self, start: Float, end: Float, loc: FileLoc) {
|
||||
self.verify_options("TransformTimes", &loc);
|
||||
fn transform_times(
|
||||
&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_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!()
|
||||
}
|
||||
|
||||
fn pixel_filter(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
self.verify_options("PixelFilter", &loc);
|
||||
fn pixel_filter(
|
||||
&mut self,
|
||||
name: &str,
|
||||
params: &ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
) -> Result<(), ParserError> {
|
||||
self.verify_options("PixelFilter", &loc)?;
|
||||
let parameters = self.make_params(params, &loc)?;
|
||||
self.current_filter = Some(SceneEntity {
|
||||
name: name.to_string(),
|
||||
loc,
|
||||
parameters,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn film(&mut self, type_name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
self.verify_options("Film", &loc);
|
||||
fn film(
|
||||
&mut self,
|
||||
type_name: &str,
|
||||
params: &ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
) -> Result<(), ParserError> {
|
||||
self.verify_options("Film", &loc)?;
|
||||
let parameters = self.make_params(params, &loc)?;
|
||||
self.current_filter = Some(SceneEntity {
|
||||
name: type_name.to_string(),
|
||||
loc,
|
||||
parameters,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn accelerator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
self.verify_options("PixelFilter", &loc);
|
||||
self.current_filter = Some(SceneEntity {
|
||||
fn accelerator(
|
||||
&mut self,
|
||||
name: &str,
|
||||
params: &ParsedParameterVector,
|
||||
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(),
|
||||
loc,
|
||||
parameters,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn integrator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
self.verify_options("PixelFilter", &loc);
|
||||
self.current_filter = Some(SceneEntity {
|
||||
fn integrator(
|
||||
&mut self,
|
||||
name: &str,
|
||||
params: &ParsedParameterVector,
|
||||
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(),
|
||||
loc,
|
||||
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);
|
||||
self.verify_world("MakeNamedMaterial", &loc);
|
||||
|
||||
if !self.named_material_names.insert(curr_name.to_string()) {
|
||||
eprintln!("Error: {}: named material '{}' redefined.", loc, name);
|
||||
return;
|
||||
return Err(ParserError::Generic(
|
||||
format!("Named material '{}' redefined.", name),
|
||||
loc,
|
||||
));
|
||||
}
|
||||
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
|
||||
let parameters = self.make_params(params, &loc)?;
|
||||
let entity = SceneEntity {
|
||||
name: name.to_string(),
|
||||
loc,
|
||||
|
|
@ -405,29 +489,41 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
};
|
||||
|
||||
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 outside = normalize_utf8(outside_name);
|
||||
|
||||
self.graphics_state.current_inside_medium = inside;
|
||||
self.graphics_state.current_outside_medium = outside;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sampler(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
|
||||
let parameters =
|
||||
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
|
||||
self.verify_options("Sampler", &loc);
|
||||
fn sampler(
|
||||
&mut self,
|
||||
name: &str,
|
||||
params: &ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
) -> Result<(), ParserError> {
|
||||
self.verify_options("Sampler", &loc)?;
|
||||
let parameters = self.make_params(¶ms.clone(), &loc)?;
|
||||
self.current_sampler = Some(SceneEntity {
|
||||
name: name.to_string(),
|
||||
loc,
|
||||
parameters,
|
||||
})
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) {
|
||||
self.verify_options("WorldBegin", &loc);
|
||||
fn world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) -> Result<(), ParserError> {
|
||||
self.verify_options("WorldBegin", &loc)?;
|
||||
self.current_block = BlockState::WorldBlock;
|
||||
for i in 0..MAX_TRANSFORMS {
|
||||
self.graphics_state.ctm[i] = Transform::default();
|
||||
|
|
@ -457,23 +553,24 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
.expect("Accelerator not set before WorldBegin"),
|
||||
arena,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn attribute_begin(&mut self, loc: FileLoc) {
|
||||
self.verify_world("AttributeBegin", &loc);
|
||||
fn attribute_begin(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||
self.verify_world("AttributeBegin", &loc)?;
|
||||
self.pushed_graphics_states
|
||||
.push(self.graphics_state.clone());
|
||||
self.push_stack.push(('a', loc));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn attribute_end(&mut self, loc: FileLoc) {
|
||||
self.verify_world("AttributeEnd", &loc);
|
||||
fn attribute_end(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
||||
self.verify_world("AttributeEnd", &loc)?;
|
||||
if self.pushed_graphics_states.is_empty() {
|
||||
log::error!(
|
||||
"[{:?}] Unmatched AttributeEnd encountered. Ignoring it.",
|
||||
loc
|
||||
);
|
||||
return;
|
||||
return Err(ParserError::Generic(
|
||||
"Unmatched AttributeEnd encountered".into(),
|
||||
loc,
|
||||
));
|
||||
}
|
||||
|
||||
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 kind == 'o' {
|
||||
log::error!(
|
||||
"[{:?}] Mismatched nesting: open ObjectBegin from {} at AttributeEnd",
|
||||
return Err(ParserError::Generic(
|
||||
format!("Mismatched nesting: open ObjectBegin from {}", start_loc),
|
||||
loc,
|
||||
start_loc
|
||||
);
|
||||
std::process::exit(1);
|
||||
));
|
||||
} else {
|
||||
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 {
|
||||
"shape" => &mut self.graphics_state.shape_attributes,
|
||||
"light" => &mut self.graphics_state.light_attributes,
|
||||
|
|
@ -502,13 +604,14 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
"medium" => &mut self.graphics_state.medium_attributes,
|
||||
"texture" => &mut self.graphics_state.texture_attributes,
|
||||
_ => {
|
||||
log::error!(
|
||||
"[{:?}] Unknown attribute target \"{}\". Must be \"shape\", \"light\", \
|
||||
return Err(ParserError::Generic(
|
||||
format!(
|
||||
"Unknown attribute target \"{}\". Must be \"shape\", \"light\", \
|
||||
\"material\", \"medium\", or \"texture\".",
|
||||
target
|
||||
),
|
||||
loc,
|
||||
target
|
||||
);
|
||||
return;
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -520,6 +623,7 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
|
||||
current_attributes.push(p.clone());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn texture(
|
||||
|
|
@ -530,23 +634,23 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
params: &ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
arena: Arc<Arena>,
|
||||
) {
|
||||
) -> Result<(), ParserError> {
|
||||
let name = normalize_utf8(orig_name);
|
||||
self.verify_world("Texture", &loc);
|
||||
let dict = ParameterDictionary::from_array(
|
||||
params.clone(),
|
||||
&self.graphics_state.texture_attributes,
|
||||
self.graphics_state.color_space.clone(),
|
||||
);
|
||||
)?;
|
||||
|
||||
if type_name != "float" && type_name != "spectrum" {
|
||||
error_exit(
|
||||
Some(&loc),
|
||||
&format!(
|
||||
return Err(ParserError::Generic(
|
||||
format!(
|
||||
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
|
||||
tex_name
|
||||
),
|
||||
);
|
||||
loc,
|
||||
));
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -557,7 +661,10 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
};
|
||||
|
||||
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());
|
||||
}
|
||||
|
|
@ -579,42 +686,74 @@ impl ParserTarget for BasicSceneBuilder {
|
|||
self.scene
|
||||
.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);
|
||||
let entity = SceneEntity {
|
||||
name: name.to_string(),
|
||||
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();
|
||||
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!()
|
||||
}
|
||||
fn named_material(&mut self, _name: &str, _loc: FileLoc) {
|
||||
|
||||
fn named_material(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
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) {
|
||||
todo!()
|
||||
fn object_begin(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn object_end(&mut self, _loc: FileLoc) {
|
||||
todo!()
|
||||
fn object_end(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn object_instance(&mut self, _name: &str, _loc: FileLoc) {
|
||||
todo!()
|
||||
fn object_instance(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn end_of_files(&mut self) {
|
||||
todo!()
|
||||
fn end_of_files(&mut self) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use crate::utils::parallel::{AsyncJob, run_async};
|
|||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||
use crate::utils::{Upload, resolve_filename};
|
||||
use crate::{Arena, FileLoc};
|
||||
use anyhow::{Result, anyhow};
|
||||
use parking_lot::Mutex;
|
||||
use rayon::prelude::*;
|
||||
use shared::core::camera::Camera;
|
||||
|
|
@ -110,7 +111,7 @@ impl BasicScene {
|
|||
integ: SceneEntity,
|
||||
accel: SceneEntity,
|
||||
arena: Arc<Arena>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
*self.integrator.lock() = Some(integ);
|
||||
*self.accelerator.lock() = Some(accel);
|
||||
|
||||
|
|
@ -118,9 +119,10 @@ impl BasicScene {
|
|||
*self.film_colorspace.lock() = Some(Arc::clone(cs));
|
||||
}
|
||||
|
||||
let filt = Filter::create(&filter.name, &filter.parameters, &filter.loc);
|
||||
let shutter_close = camera.base.parameters.get_one_float("shutterclose", 1.);
|
||||
let shutter_open = camera.base.parameters.get_one_float("shutteropen", 0.);
|
||||
let filter = Filter::create(&filter.name, &filter.parameters, &filter.loc)
|
||||
.map_err(|e| anyhow!("Failed to create filter: {}", e))?;
|
||||
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 film_instance = Arc::new(
|
||||
|
|
@ -128,12 +130,12 @@ impl BasicScene {
|
|||
&film.name,
|
||||
&film.parameters,
|
||||
exposure_time,
|
||||
filt.expect("Must have a filter"),
|
||||
filter,
|
||||
Some(camera.camera_transform.clone()),
|
||||
&film.loc,
|
||||
&arena,
|
||||
)
|
||||
.expect("Must have a film"),
|
||||
.map_err(|e| anyhow!("Failed to create film: {}", e))?,
|
||||
);
|
||||
|
||||
*self.film_state.lock() = SingletonState {
|
||||
|
|
@ -152,7 +154,7 @@ impl BasicScene {
|
|||
&sampler.loc,
|
||||
&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);
|
||||
|
||||
|
|
@ -170,9 +172,10 @@ impl BasicScene {
|
|||
&camera.base.loc,
|
||||
&arena_camera,
|
||||
)
|
||||
.expect("Failed to create camera")
|
||||
.map_err(|e| anyhow!("Failed to create camera: {}", e))
|
||||
});
|
||||
self.camera_state.lock().job = Some(camera_job);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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_jobs: impl FnOnce(&mut TextureState) -> &mut HashMap<String, AsyncJob<Arc<T>>>,
|
||||
create_fn: F,
|
||||
) where
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
F: FnOnce(TextureSceneEntity) -> T + Send + 'static,
|
||||
{
|
||||
|
|
@ -209,23 +213,24 @@ impl BasicScene {
|
|||
|
||||
if texture.base.name != "imagemap" && texture.base.name != "ptex" {
|
||||
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)
|
||||
{
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if state.loading_texture_filenames.contains(&filename) {
|
||||
get_serial(state).push((name, texture));
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
state.loading_texture_filenames.insert(filename);
|
||||
let job = run_async(move || Arc::new(create_fn(texture)));
|
||||
get_jobs(state).insert(name, job);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool {
|
||||
|
|
@ -245,7 +250,12 @@ impl BasicScene {
|
|||
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();
|
||||
self.add_texture_generic(
|
||||
name,
|
||||
|
|
@ -265,7 +275,7 @@ impl BasicScene {
|
|||
)
|
||||
.expect("Could not create Float texture")
|
||||
},
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_spectrum_texture(
|
||||
|
|
@ -273,7 +283,7 @@ impl BasicScene {
|
|||
name: String,
|
||||
texture: TextureSceneEntity,
|
||||
arena: Arc<Arena>,
|
||||
) {
|
||||
) -> Result<()> {
|
||||
let mut state = self.texture_state.lock();
|
||||
self.add_texture_generic(
|
||||
name,
|
||||
|
|
@ -294,7 +304,7 @@ impl BasicScene {
|
|||
)
|
||||
.expect("Could not create spectrum texture")
|
||||
},
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_area_light(&self, light: SceneEntity) -> usize {
|
||||
|
|
@ -378,16 +388,25 @@ impl BasicScene {
|
|||
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(
|
||||
&self,
|
||||
textures: &NamedTextures,
|
||||
arena: &mut Arena,
|
||||
) -> (HashMap<String, Material>, Vec<Material>) {
|
||||
) -> Result<(HashMap<String, Material>, Vec<Material>)> {
|
||||
let mut state = self.material_state.lock();
|
||||
|
||||
let finished: Vec<_> = state.normal_map_jobs.drain().collect();
|
||||
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();
|
||||
|
|
@ -402,57 +421,72 @@ impl BasicScene {
|
|||
continue;
|
||||
}
|
||||
|
||||
let mat_type = entity.parameters.get_one_string("type", "");
|
||||
let mat_type = entity.parameters.get_one_string("type", "")?;
|
||||
if mat_type.is_empty() {
|
||||
log::error!(
|
||||
"{}: \"string type\" not provided in named material's parameters.",
|
||||
entity.loc
|
||||
);
|
||||
log::error!("{}: missing material type", entity.loc);
|
||||
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(
|
||||
Arc::new(entity.parameters.clone()),
|
||||
Some(textures),
|
||||
);
|
||||
|
||||
let mat = Material::create(
|
||||
match Material::create(
|
||||
&mat_type,
|
||||
&tex_dict,
|
||||
normal_map,
|
||||
&named_materials, // Reference for now
|
||||
&named_materials,
|
||||
entity.loc.clone(),
|
||||
arena,
|
||||
)
|
||||
.expect("Could not create material");
|
||||
|
||||
named_materials.insert(name.clone(), mat);
|
||||
) {
|
||||
Ok(mat) => {
|
||||
named_materials.insert(name.clone(), mat);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"{}: Failed to create material '{}': {}",
|
||||
entity.loc,
|
||||
name,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let materials: Vec<Material> = state
|
||||
.materials
|
||||
.iter()
|
||||
.map(|entity| {
|
||||
let normal_map = self.get_normal_map(&state, &entity.parameters);
|
||||
let tex_dict = TextureParameterDictionary::new(
|
||||
entity.parameters.clone().into(),
|
||||
Some(textures),
|
||||
);
|
||||
.filter_map(|entity| {
|
||||
let result: Result<Material> = (|| {
|
||||
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||
let tex_dict = TextureParameterDictionary::new(
|
||||
entity.parameters.clone().into(),
|
||||
Some(textures),
|
||||
);
|
||||
|
||||
Material::create(
|
||||
&entity.name,
|
||||
&tex_dict,
|
||||
normal_map,
|
||||
&named_materials,
|
||||
entity.loc.clone(),
|
||||
arena,
|
||||
)
|
||||
.expect("Could not create material")
|
||||
Material::create(
|
||||
&entity.name,
|
||||
&tex_dict,
|
||||
normal_map,
|
||||
&named_materials,
|
||||
entity.loc.clone(),
|
||||
arena,
|
||||
)
|
||||
})();
|
||||
|
||||
match result {
|
||||
Ok(mat) => Some(mat),
|
||||
Err(e) => {
|
||||
log::error!("{}: Failed to create material: {}", entity.loc, e);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
(named_materials, materials)
|
||||
Ok((named_materials, materials))
|
||||
}
|
||||
|
||||
pub fn create_aggregate(
|
||||
|
|
@ -499,7 +533,7 @@ impl BasicScene {
|
|||
) -> Vec<Vec<Shape>> {
|
||||
entities
|
||||
.par_iter()
|
||||
.map(|sh| {
|
||||
.filter_map(|sh| {
|
||||
Shape::create(
|
||||
&sh.base.name,
|
||||
*sh.render_from_object.as_ref(),
|
||||
|
|
@ -510,7 +544,7 @@ impl BasicScene {
|
|||
sh.base.loc.clone(),
|
||||
arena,
|
||||
)
|
||||
.expect("Could not create shape")
|
||||
.ok()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -621,56 +655,57 @@ impl BasicScene {
|
|||
Vec::new()
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Getters
|
||||
// ========================================================================
|
||||
|
||||
pub fn get_camera(&self) -> Arc<Camera> {
|
||||
pub fn get_camera(&self) -> Result<Arc<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")
|
||||
}
|
||||
|
||||
pub fn get_film(&self) -> Arc<Film> {
|
||||
pub fn get_film(&self) -> Result<Arc<Film>> {
|
||||
self.get_singleton(&self.film_state, "Film")
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Helpers
|
||||
// ========================================================================
|
||||
|
||||
fn get_singleton<T: Send + 'static>(
|
||||
&self,
|
||||
state: &Mutex<SingletonState<T>>,
|
||||
name: &str,
|
||||
) -> Arc<T> {
|
||||
) -> Result<Arc<T>> {
|
||||
let mut guard = state.lock();
|
||||
|
||||
if let Some(ref res) = guard.result {
|
||||
return res.clone();
|
||||
return Ok(res.clone());
|
||||
}
|
||||
|
||||
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());
|
||||
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) {
|
||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", ""));
|
||||
fn start_loading_normal_maps(
|
||||
&self,
|
||||
state: &mut MaterialState,
|
||||
params: &ParameterDictionary,
|
||||
) -> Result<()> {
|
||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", "")?);
|
||||
if filename.is_empty() {
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if state.normal_map_jobs.contains_key(&filename)
|
||||
|| state.normal_maps.contains_key(&filename)
|
||||
{
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let filename_clone = filename.clone();
|
||||
|
|
@ -693,18 +728,19 @@ impl BasicScene {
|
|||
});
|
||||
|
||||
state.normal_map_jobs.insert(filename, job);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_normal_map(
|
||||
&self,
|
||||
state: &MaterialState,
|
||||
params: &ParameterDictionary,
|
||||
) -> Option<Arc<Image>> {
|
||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", ""));
|
||||
) -> Result<Option<Arc<Image>>> {
|
||||
let filename = resolve_filename(¶ms.get_one_string("normalmap", "")?);
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use super::{SceneEntity, TextureSceneEntity};
|
|||
use crate::core::image::Image;
|
||||
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||
use crate::utils::parallel::AsyncJob;
|
||||
use anyhow::Result;
|
||||
use shared::core::light::Light;
|
||||
use shared::core::medium::Medium;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
|
@ -41,7 +42,7 @@ pub struct MediaState {
|
|||
#[derive(Debug)]
|
||||
pub struct SingletonState<T> {
|
||||
pub result: Option<Arc<T>>,
|
||||
pub job: Option<AsyncJob<T>>,
|
||||
pub job: Option<AsyncJob<Result<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Default for SingletonState<T> {
|
||||
|
|
|
|||
|
|
@ -165,7 +165,9 @@ pub trait CreateTextureMapping {
|
|||
params: &TextureParameterDictionary,
|
||||
render_from_texture: &Transform,
|
||||
loc: &FileLoc,
|
||||
) -> Self;
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl CreateTextureMapping for TextureMapping2D {
|
||||
|
|
@ -173,36 +175,39 @@ impl CreateTextureMapping for TextureMapping2D {
|
|||
params: &TextureParameterDictionary,
|
||||
render_from_texture: &Transform,
|
||||
loc: &FileLoc,
|
||||
) -> Self {
|
||||
let mtype = params.get_one_string("mapping", "uv");
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let mtype = params.get_one_string("mapping", "uv")?;
|
||||
match mtype.as_str() {
|
||||
"uv" => {
|
||||
let su = params.get_one_float("uscale", 1.);
|
||||
let sv = params.get_one_float("vscale", 1.);
|
||||
let du = params.get_one_float("udelta", 0.);
|
||||
let dv = params.get_one_float("vdelta", 0.);
|
||||
let su = params.get_one_float("uscale", 1.)?;
|
||||
let sv = params.get_one_float("vscale", 1.)?;
|
||||
let du = params.get_one_float("udelta", 0.)?;
|
||||
let dv = params.get_one_float("vdelta", 0.)?;
|
||||
let mapping = UVMapping::new(su, sv, du, dv);
|
||||
TextureMapping2D::UV(mapping)
|
||||
Ok(TextureMapping2D::UV(mapping))
|
||||
}
|
||||
"spherical" => {
|
||||
let mapping = SphericalMapping::new(&render_from_texture.inverse());
|
||||
TextureMapping2D::Spherical(mapping)
|
||||
Ok(TextureMapping2D::Spherical(mapping))
|
||||
}
|
||||
"cylindrical" => {
|
||||
let mapping = CylindricalMapping::new(&render_from_texture.inverse());
|
||||
TextureMapping2D::Cylindrical(mapping)
|
||||
Ok(TextureMapping2D::Cylindrical(mapping))
|
||||
}
|
||||
"planar" => {
|
||||
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 ds = params.get_one_float("udelta", 0.);
|
||||
let dt = params.get_one_float("vdelta", 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 ds = params.get_one_float("udelta", 0.)?;
|
||||
let dt = params.get_one_float("vdelta", 0.)?;
|
||||
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);
|
||||
TextureMapping2D::UV(UVMapping::default())
|
||||
Ok(TextureMapping2D::UV(UVMapping::default()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,17 +57,17 @@ impl CreateFilm for GBufferFilm {
|
|||
_arena: &Arena,
|
||||
) -> Result<Film> {
|
||||
let colorspace = params.color_space.as_ref().unwrap();
|
||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
||||
let write_fp16 = params.get_one_bool("savefp16", true);
|
||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||
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()) {
|
||||
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 camera_transform =
|
||||
camera_transform.ok_or_else(|| anyhow!("GBufferFilm requires a camera_transform"))?;
|
||||
|
|
|
|||
|
|
@ -70,10 +70,10 @@ impl CreateFilm for RGBFilm {
|
|||
_arena: &Arena,
|
||||
) -> Result<Film> {
|
||||
let colorspace = params.color_space.as_ref().unwrap();
|
||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
||||
let write_fp16 = params.get_one_bool("savefp16", true);
|
||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||
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);
|
||||
Ok(Film::RGB(film.device))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,19 +97,19 @@ impl CreateFilm for SpectralFilm {
|
|||
_arena: &Arena,
|
||||
) -> Result<Film> {
|
||||
let colorspace = params.color_space.as_ref().unwrap();
|
||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY);
|
||||
let write_fp16 = params.get_one_bool("savefp16", true);
|
||||
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
|
||||
let write_fp16 = params.get_one_bool("savefp16", true)?;
|
||||
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()) {
|
||||
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 lambda_min = params.get_one_float("lambdamin", LAMBDA_MIN as Float);
|
||||
let lambda_max = params.get_one_float("lambdamin", LAMBDA_MAX as Float);
|
||||
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_max = params.get_one_float("lambdamin", LAMBDA_MAX as Float)?;
|
||||
if lambda_min < LAMBDA_MIN as Float && lambda_max > LAMBDA_MAX as Float {
|
||||
return Err(anyhow!(
|
||||
"{}: 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)
|
||||
});
|
||||
|
||||
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 {
|
||||
radius,
|
||||
tau,
|
||||
sampler,
|
||||
integral,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,23 @@ use crate::core::color::RGBToSpectrumTableData;
|
|||
use bytemuck::cast_slice;
|
||||
use once_cell::sync::Lazy;
|
||||
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_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#![feature(f16)]
|
||||
#[allow(dead_code)]
|
||||
pub mod cameras;
|
||||
pub mod core;
|
||||
pub mod films;
|
||||
pub mod filters;
|
||||
|
|
|
|||
|
|
@ -114,10 +114,10 @@ impl CreateLight for DiffuseAreaLight {
|
|||
) -> Result<Light> {
|
||||
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
|
||||
let illum_spec = Spectrum::Dense(colorspace.unwrap().illuminant);
|
||||
let mut scale = params.get_one_float("scale", 1.);
|
||||
let two_sided = params.get_one_bool("twosided", false);
|
||||
let mut scale = params.get_one_float("scale", 1.)?;
|
||||
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() {
|
||||
if l.is_some() {
|
||||
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);
|
||||
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 {
|
||||
// k_e is the emissive power of the light as defined by the spectral
|
||||
// distribution and texture and is used to normalize the emitted
|
||||
|
|
|
|||
|
|
@ -54,10 +54,10 @@ impl CreateLight for DistantLight {
|
|||
SpectrumType::Illuminant,
|
||||
)
|
||||
.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 to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
|
||||
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 w = (from - to).normalize();
|
||||
let (v1, v2) = w.coordinate_system();
|
||||
let m: [Float; 16] = [
|
||||
|
|
@ -82,7 +82,7 @@ impl CreateLight for DistantLight {
|
|||
let final_render = render_from_light * t;
|
||||
scale /= spectrum_to_photometric(l);
|
||||
// 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. {
|
||||
scale *= e_v;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,8 +72,8 @@ impl CreateLight for GoniometricLight {
|
|||
SpectrumType::Illuminant,
|
||||
)
|
||||
.expect("Could not retrieve spectrum");
|
||||
let mut scale = params.get_one_float("scale", 1.);
|
||||
let filename = resolve_filename(¶ms.get_one_string("filename", ""));
|
||||
let mut scale = params.get_one_float("scale", 1.)?;
|
||||
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||
let image: Ptr<Image> = if filename.is_empty() {
|
||||
Ptr::null()
|
||||
} else {
|
||||
|
|
@ -102,7 +102,7 @@ impl CreateLight for GoniometricLight {
|
|||
};
|
||||
|
||||
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 {
|
||||
let k_e = compute_emissive_power(&image);
|
||||
|
|
|
|||
|
|
@ -132,10 +132,10 @@ pub fn create(
|
|||
arena: &Arena,
|
||||
) -> Result<Light> {
|
||||
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
|
||||
let mut scale = parameters.get_one_float("scale", 1.0);
|
||||
let portal = parameters.get_point3f_array("portal");
|
||||
let filename = resolve_filename(¶meters.get_one_string("filename", ""));
|
||||
let e_v = parameters.get_one_float("illuminance", -1.0);
|
||||
let mut scale = parameters.get_one_float("scale", 1.0)?;
|
||||
let portal = parameters.get_point3f_array("portal")?;
|
||||
let filename = resolve_filename(¶meters.get_one_string("filename", "")?);
|
||||
let e_v = parameters.get_one_float("illuminance", -1.0)?;
|
||||
|
||||
let has_spectrum = !l.is_empty();
|
||||
let has_file = !filename.is_empty();
|
||||
|
|
|
|||
|
|
@ -60,15 +60,15 @@ impl CreateLight for PointLight {
|
|||
SpectrumType::Illuminant,
|
||||
)
|
||||
.unwrap();
|
||||
let mut scale = parameters.get_one_float("scale", 1.);
|
||||
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||
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. {
|
||||
let k_e = 4. * PI;
|
||||
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 final_render = render_from_light * tf;
|
||||
let specific = PointLight::new(final_render, medium.into(), l, scale);
|
||||
|
|
|
|||
|
|
@ -104,11 +104,11 @@ impl CreateLight for ProjectionLight {
|
|||
_colorspace: Option<&RGBColorSpace>,
|
||||
arena: &Arena,
|
||||
) -> Result<Light> {
|
||||
let mut scale = parameters.get_one_float("scale", 1.);
|
||||
let power = parameters.get_one_float("power", -1.);
|
||||
let fov = parameters.get_one_float("fov", 90.);
|
||||
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||
let power = parameters.get_one_float("power", -1.)?;
|
||||
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() {
|
||||
return Err(anyhow!(
|
||||
"{}: 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::spectrum::spectrum_to_photometric;
|
||||
use crate::core::texture::FloatTexture;
|
||||
|
|
@ -72,17 +71,17 @@ impl CreateLight for SpotLight {
|
|||
SpectrumType::Illuminant,
|
||||
)
|
||||
.expect("No spectrum");
|
||||
let mut scale = parameters.get_one_float("scale", 1.);
|
||||
let coneangle = parameters.get_one_float("coneangle", 30.);
|
||||
let conedelta = parameters.get_one_float("conedelta", 5.);
|
||||
let from = parameters.get_one_point3f("from", Point3f::zero());
|
||||
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.));
|
||||
let mut scale = parameters.get_one_float("scale", 1.)?;
|
||||
let coneangle = parameters.get_one_float("coneangle", 30.)?;
|
||||
let conedelta = parameters.get_one_float("conedelta", 5.)?;
|
||||
let from = parameters.get_one_point3f("from", Point3f::zero())?;
|
||||
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 t = Transform::translate(from.into()) * dir_to_z.inverse();
|
||||
let final_render = render_from_light * t;
|
||||
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. {
|
||||
let cos_falloff_end = radians(coneangle).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::texture::SpectrumTexture;
|
||||
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 shared::core::material::Material;
|
||||
use shared::core::spectrum::Spectrum;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::core::shape::{ALL_TRIANGLE_MESHES, CreateShape};
|
|||
use crate::core::texture::FloatTexture;
|
||||
use crate::shapes::mesh::TriangleMesh;
|
||||
use crate::utils::{Arena, FileLoc, ParameterDictionary};
|
||||
use anyhow::{Result, anyhow};
|
||||
use anyhow::{Result, bail};
|
||||
use log::warn;
|
||||
use shared::core::shape::Shape;
|
||||
use shared::shapes::TriangleShape;
|
||||
|
|
@ -29,9 +29,7 @@ impl CreateShape for TriangleShape {
|
|||
if vertex_indices.is_empty() {
|
||||
if p.len() == 3 {
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"Vertex indices \"indices\" must be provided with triangle mesh."
|
||||
));
|
||||
return bail!("Vertex indices \"indices\" must be provided with triangle mesh.");
|
||||
}
|
||||
} else if vertex_indices.len() % 3 != 0 {
|
||||
let excess = vertex_indices.len() % 3;
|
||||
|
|
@ -45,9 +43,7 @@ impl CreateShape for TriangleShape {
|
|||
}
|
||||
|
||||
if p.is_empty() {
|
||||
return Err(anyhow!(
|
||||
"Vertex positions \"P\" must be provided with triangle mesh."
|
||||
));
|
||||
return bail!("Vertex positions \"P\" must be provided with triangle mesh.");
|
||||
}
|
||||
|
||||
if !uvs.is_empty() && uvs.len() != p.len() {
|
||||
|
|
@ -68,11 +64,11 @@ impl CreateShape for TriangleShape {
|
|||
for (_, &index) in vertex_indices.iter().enumerate() {
|
||||
// Check for negative indices (if keeping i32) or out of bounds
|
||||
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.",
|
||||
index,
|
||||
p.len()
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub struct SystemAllocator;
|
|||
|
||||
impl Default for SystemAllocator {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -35,72 +35,130 @@ impl GpuAllocator for SystemAllocator {
|
|||
|
||||
/// CUDA unified memory backend using CudaAllocator
|
||||
#[cfg(feature = "cuda")]
|
||||
pub struct CudaAllocator;
|
||||
pub mod cuda {
|
||||
use super::GpuAllocator;
|
||||
use std::alloc::Layout;
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
impl Default for CudaAllocator {
|
||||
fn default() -> Self {
|
||||
Self
|
||||
pub struct CudaAllocator;
|
||||
|
||||
impl Default for CudaAllocator {
|
||||
fn default() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cuda")]
|
||||
impl GpuAllocator for CudaAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
use cust::memory::cuda_malloc_unified;
|
||||
impl GpuAllocator for CudaAllocator {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
use cust::memory::cuda_malloc_unified;
|
||||
use cust_raw::driver_sys::*;
|
||||
|
||||
let size = layout.size().max(layout.align());
|
||||
if size == 0 {
|
||||
return layout.align() as *mut u8;
|
||||
let size = layout.size().max(layout.align());
|
||||
if size == 0 {
|
||||
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 =
|
||||
unsafe { cuda_malloc_unified::<u8>(size).expect("cuda_malloc_unified failed") };
|
||||
let raw = unified_ptr.as_raw_mut();
|
||||
std::mem::forget(unified_ptr);
|
||||
raw
|
||||
}
|
||||
|
||||
let mut unified_ptr =
|
||||
unsafe { cuda_malloc_unified::<u8>(size).expect("cuda_malloc_unified failed") };
|
||||
let raw = unified_ptr.as_raw_mut();
|
||||
std::mem::forget(unified_ptr);
|
||||
raw
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
use cust::memory::{UnifiedPointer, cuda_free_unified};
|
||||
if layout.size() > 0 {
|
||||
let _ = unsafe { cuda_free_unified(UnifiedPointer::wrap(ptr)) };
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
|
||||
use cust::memory::{UnifiedPointer, cuda_free_unified};
|
||||
if layout.size() > 0 {
|
||||
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")]
|
||||
pub mod vulkan {
|
||||
use super::GpuAllocator;
|
||||
use ash::vk;
|
||||
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 std::alloc::Layout;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
pub struct VulkanAllocator {
|
||||
allocator: Mutex<Allocator>,
|
||||
/// Track pointer -> Allocation so we can free later.
|
||||
allocations: Mutex<HashMap<usize, Allocation>>,
|
||||
// So, having a static allocator seems like a terrible idea
|
||||
// But I cant find a way to get a functioning generic Arena constructor
|
||||
// That might not even be a necessity, since rust-gpu/rust-cuda might actually handle that
|
||||
// differently
|
||||
static VK_ALLOCATOR: OnceLock<VulkanAllocatorInner> = OnceLock::new();
|
||||
|
||||
struct VulkanAllocatorInner {
|
||||
state: Mutex<VulkanState>,
|
||||
}
|
||||
|
||||
impl VulkanAllocator {
|
||||
pub fn new(allocator: Allocator) -> Self {
|
||||
Self {
|
||||
allocator: Mutex::new(allocator),
|
||||
allocations: Mutex::new(HashMap::new()),
|
||||
struct VulkanState {
|
||||
allocator: Allocator,
|
||||
allocations: HashMap<usize, Allocation>,
|
||||
}
|
||||
|
||||
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 {
|
||||
fn default() -> Self {
|
||||
let _ = inner();
|
||||
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 {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
let size = layout.size().max(layout.align());
|
||||
|
|
@ -108,8 +166,10 @@ pub mod vulkan {
|
|||
return layout.align() as *mut u8;
|
||||
}
|
||||
|
||||
let mut alloc = self.allocator.lock();
|
||||
let allocation = alloc
|
||||
let inner = inner();
|
||||
let mut state = inner.state.lock();
|
||||
let allocation = state
|
||||
.allocator
|
||||
.allocate(&AllocationCreateDesc {
|
||||
name: "arena",
|
||||
requirements: vk::MemoryRequirements {
|
||||
|
|
@ -128,7 +188,7 @@ pub mod vulkan {
|
|||
.expect("Vulkan allocation not host-mapped")
|
||||
.as_ptr() as *mut u8;
|
||||
|
||||
self.allocations.lock().insert(ptr as usize, allocation);
|
||||
state.allocations.insert(ptr as usize, allocation);
|
||||
ptr
|
||||
}
|
||||
|
||||
|
|
@ -136,9 +196,11 @@ pub mod vulkan {
|
|||
if layout.size() == 0 {
|
||||
return;
|
||||
}
|
||||
if let Some(allocation) = self.allocations.lock().remove(&(ptr as usize)) {
|
||||
self.allocator
|
||||
.lock()
|
||||
let inner = inner();
|
||||
let mut state = inner.state.lock();
|
||||
if let Some(allocation) = state.allocations.remove(&(ptr as usize)) {
|
||||
state
|
||||
.allocator
|
||||
.free(allocation)
|
||||
.expect("Vulkan free failed");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,33 @@
|
|||
use half::f16;
|
||||
use shared::Float;
|
||||
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 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);
|
||||
|
||||
views.push(
|
||||
DigitPermutation::new(
|
||||
base as i32,
|
||||
n_digits as u64,
|
||||
)
|
||||
.device,
|
||||
);
|
||||
views.push(DigitPermutation::new(base as i32, n_digits as u64).device);
|
||||
|
||||
// current_offset += len;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub use strings::*;
|
|||
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
|
||||
|
||||
#[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")))]
|
||||
pub type Arena = arena::Arena<backend::SystemAllocator>;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::Result;
|
||||
use flate2::read::GzDecoder;
|
||||
use memmap2::Mmap;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -12,10 +13,23 @@ use crate::utils::parameters::{ParameterDictionary, ParsedParameter, ParsedParam
|
|||
use shared::Float;
|
||||
|
||||
pub trait ParserTarget {
|
||||
fn identity(&mut self, loc: FileLoc);
|
||||
fn translate(&mut self, dx: Float, dy: Float, dz: Float, loc: FileLoc);
|
||||
fn rotate(&mut self, angle: Float, ax: Float, ay: Float, az: Float, loc: FileLoc);
|
||||
fn scale(&mut self, sx: Float, sy: Float, sz: Float, loc: FileLoc);
|
||||
fn identity(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||
fn translate(
|
||||
&mut self,
|
||||
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(
|
||||
&mut self,
|
||||
ex: Float,
|
||||
|
|
@ -28,32 +42,85 @@ pub trait ParserTarget {
|
|||
uy: Float,
|
||||
uz: Float,
|
||||
loc: FileLoc,
|
||||
);
|
||||
fn transform(&mut self, transform: &[Float; 16], loc: FileLoc);
|
||||
fn concat_transform(&mut self, transform: &[Float; 16], loc: FileLoc);
|
||||
) -> Result<(), ParserError>;
|
||||
fn transform(&mut self, transform: &[Float; 16], loc: FileLoc) -> Result<(), ParserError>;
|
||||
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 coord_sys_transform(&mut self, name: &str, loc: FileLoc);
|
||||
fn active_transform_all(&mut self, loc: FileLoc);
|
||||
fn active_transform_end_time(&mut self, loc: FileLoc);
|
||||
fn active_transform_start_time(&mut self, loc: FileLoc);
|
||||
fn transform_times(&mut self, start: Float, end: Float, loc: FileLoc);
|
||||
fn option(&mut self, name: &str, value: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||
fn color_space(&mut self, n: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||
fn pixel_filter(
|
||||
&mut self,
|
||||
name: &str,
|
||||
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 color_space(&mut self, n: &str, loc: FileLoc);
|
||||
fn pixel_filter(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn film(&mut self, type_name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn accelerator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn integrator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn camera(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn make_named_medium(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn medium_interface(&mut self, inside_name: &str, outside_name: &str, loc: FileLoc);
|
||||
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 world_begin(&mut self, loc: FileLoc, arena: Arc<Arena>) -> Result<(), ParserError>;
|
||||
fn attribute_begin(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||
fn attribute_end(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||
fn attribute(
|
||||
&mut self,
|
||||
target: &str,
|
||||
params: ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
) -> Result<(), ParserError>;
|
||||
|
||||
fn texture(
|
||||
&mut self,
|
||||
|
|
@ -63,22 +130,47 @@ pub trait ParserTarget {
|
|||
params: &ParsedParameterVector,
|
||||
loc: FileLoc,
|
||||
arena: Arc<Arena>,
|
||||
);
|
||||
fn material(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn make_named_material(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn named_material(&mut self, name: &str, loc: FileLoc);
|
||||
) -> Result<(), ParserError>;
|
||||
fn material(
|
||||
&mut self,
|
||||
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 area_light_source(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc);
|
||||
fn light_source(
|
||||
&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 reverse_orientation(&mut self, loc: FileLoc);
|
||||
fn shape(
|
||||
&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_end(&mut self, loc: FileLoc);
|
||||
fn object_instance(&mut self, name: &str, loc: FileLoc);
|
||||
fn object_begin(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError>;
|
||||
fn object_end(&mut self, loc: FileLoc) -> Result<(), ParserError>;
|
||||
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)]
|
||||
|
|
@ -394,24 +486,42 @@ impl 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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn identity(&mut self, _loc: FileLoc) {
|
||||
fn identity(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rotate(&mut self, angle: Float, ax: Float, ay: Float, az: Float, _loc: FileLoc) {
|
||||
println!("{}Rotate {} {} {} {}", self.indent(0), angle, ax, ay, az);
|
||||
fn rotate(
|
||||
&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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn look_at(
|
||||
|
|
@ -426,7 +536,7 @@ impl ParserTarget for FormattingParserTarget {
|
|||
uy: Float,
|
||||
uz: Float,
|
||||
_loc: FileLoc,
|
||||
) {
|
||||
) -> Result<(), ParserError> {
|
||||
println!(
|
||||
"{}LookAt {} {} {} {} {} {} {} {} {}",
|
||||
self.indent(0),
|
||||
|
|
@ -440,56 +550,75 @@ impl ParserTarget for FormattingParserTarget {
|
|||
uy,
|
||||
uz
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn concat_transform(&mut self, t: &[Float; 16], _loc: FileLoc) {
|
||||
// Rust arrays verify size at compile time, simpler than C++ pointers
|
||||
fn concat_transform(&mut self, t: &[Float; 16], _loc: FileLoc) -> Result<(), ParserError> {
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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));
|
||||
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));
|
||||
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);
|
||||
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!(
|
||||
"{}Shape \"{}\" ... ({} params)",
|
||||
self.indent(0),
|
||||
name,
|
||||
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!(
|
||||
"{}Material \"{}\" ... ({} params)",
|
||||
self.indent(0),
|
||||
name,
|
||||
params.len()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn texture(
|
||||
|
|
@ -500,7 +629,7 @@ impl ParserTarget for FormattingParserTarget {
|
|||
_params: &ParsedParameterVector,
|
||||
_loc: FileLoc,
|
||||
_arena: Arc<Arena>,
|
||||
) {
|
||||
) -> Result<(), ParserError> {
|
||||
println!(
|
||||
"{}Texture \"{}\" \"{}\" \"{}\"",
|
||||
self.indent(0),
|
||||
|
|
@ -508,38 +637,137 @@ impl ParserTarget for FormattingParserTarget {
|
|||
type_name,
|
||||
tex_name
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn active_transform_all(&mut self, _loc: FileLoc) {}
|
||||
fn active_transform_end_time(&mut self, _loc: FileLoc) {}
|
||||
fn active_transform_start_time(&mut self, _loc: FileLoc) {}
|
||||
fn transform_times(&mut self, _s: Float, _e: Float, _loc: FileLoc) {}
|
||||
fn color_space(&mut self, _n: &str, _loc: FileLoc) {}
|
||||
fn pixel_filter(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn film(&mut self, _t: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn accelerator(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn integrator(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn camera(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn make_named_medium(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn medium_interface(&mut self, _i: &str, _o: &str, _loc: FileLoc) {}
|
||||
fn sampler(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn attribute(&mut self, _t: &str, _p: ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn make_named_material(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn named_material(&mut self, _n: &str, _loc: FileLoc) {}
|
||||
fn light_source(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn area_light_source(&mut self, _n: &str, _p: &ParsedParameterVector, _loc: FileLoc) {}
|
||||
fn reverse_orientation(&mut self, _loc: FileLoc) {}
|
||||
fn object_begin(&mut self, name: &str, _loc: FileLoc) {
|
||||
fn active_transform_all(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn active_transform_end_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn active_transform_start_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn transform_times(&mut self, _s: Float, _e: Float, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn color_space(&mut self, _n: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn pixel_filter(
|
||||
&mut self,
|
||||
_n: &str,
|
||||
_p: &ParsedParameterVector,
|
||||
_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);
|
||||
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);
|
||||
println!("{}ObjectEnd", self.indent(0));
|
||||
Ok(())
|
||||
}
|
||||
fn object_instance(&mut self, _n: &str, _loc: FileLoc) {}
|
||||
fn end_of_files(&mut self) {
|
||||
fn object_instance(&mut self, _n: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
||||
Ok(())
|
||||
}
|
||||
fn end_of_files(&mut self) -> Result<(), ParserError> {
|
||||
self.cat_indent_count = 0;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -756,17 +984,17 @@ impl<'a> SceneParser<'a> {
|
|||
|
||||
match first_char {
|
||||
'A' => match token.text.as_str() {
|
||||
"AttributeBegin" => self.target.attribute_begin(token.loc),
|
||||
"AttributeEnd" => self.target.attribute_end(token.loc),
|
||||
"AttributeBegin" => self.target.attribute_begin(token.loc)?,
|
||||
"AttributeEnd" => self.target.attribute_end(token.loc)?,
|
||||
"Attribute" => {
|
||||
self.parse_basic_entry(|t, n, p, l| t.attribute(n, p.to_vec(), l))?
|
||||
}
|
||||
"ActiveTransform" => {
|
||||
let a = self.next_token_required()?;
|
||||
match a.text.as_str() {
|
||||
"All" => self.target.active_transform_all(token.loc),
|
||||
"EndTime" => self.target.active_transform_end_time(token.loc),
|
||||
"StartTime" => self.target.active_transform_start_time(token.loc),
|
||||
"All" => self.target.active_transform_all(token.loc)?,
|
||||
"EndTime" => self.target.active_transform_end_time(token.loc)?,
|
||||
"StartTime" => self.target.active_transform_start_time(token.loc)?,
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
"Unknown ActiveTransform type".into(),
|
||||
|
|
@ -796,19 +1024,19 @@ impl<'a> SceneParser<'a> {
|
|||
m[i] = self.expect_float()?;
|
||||
}
|
||||
self.expect_token("]")?;
|
||||
self.target.concat_transform(&m, token.loc);
|
||||
self.target.concat_transform(&m, token.loc)?;
|
||||
}
|
||||
"CoordinateSystem" => {
|
||||
let n = self.expect_quoted_string()?;
|
||||
self.target.coordinate_system(&n, token.loc);
|
||||
self.target.coordinate_system(&n, token.loc)?;
|
||||
}
|
||||
"CoordSysTransform" => {
|
||||
let n = self.expect_quoted_string()?;
|
||||
self.target.coord_sys_transform(&n, token.loc);
|
||||
self.target.coord_sys_transform(&n, token.loc)?;
|
||||
}
|
||||
"ColorSpace" => {
|
||||
let n = self.expect_quoted_string()?;
|
||||
self.target.color_space(&n, token.loc);
|
||||
self.target.color_space(&n, token.loc)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
|
|
@ -846,7 +1074,7 @@ impl<'a> SceneParser<'a> {
|
|||
|
||||
self.file_stack.push(new_tokenizer);
|
||||
}
|
||||
"Identity" => self.target.identity(token.loc),
|
||||
"Identity" => self.target.identity(token.loc)?,
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
format!("Unknown directive {}", token.text),
|
||||
|
|
@ -892,7 +1120,7 @@ impl<'a> SceneParser<'a> {
|
|||
self.unget(next);
|
||||
inside.clone()
|
||||
};
|
||||
self.target.medium_interface(&inside, &outside, token.loc);
|
||||
self.target.medium_interface(&inside, &outside, token.loc)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
|
|
@ -905,7 +1133,7 @@ impl<'a> SceneParser<'a> {
|
|||
'N' => match token.text.as_str() {
|
||||
"NamedMaterial" => {
|
||||
let n = self.expect_quoted_string()?;
|
||||
self.target.named_material(&n, token.loc);
|
||||
self.target.named_material(&n, token.loc)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
|
|
@ -918,18 +1146,18 @@ impl<'a> SceneParser<'a> {
|
|||
'O' => match token.text.as_str() {
|
||||
"ObjectBegin" => {
|
||||
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" => {
|
||||
let n = self.expect_quoted_string()?;
|
||||
self.target.object_instance(&n, token.loc);
|
||||
self.target.object_instance(&n, token.loc)?;
|
||||
}
|
||||
"Option" => {
|
||||
let name = self.expect_quoted_string()?;
|
||||
let val_tok = self.next_token_required()?;
|
||||
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(
|
||||
|
|
@ -952,7 +1180,7 @@ impl<'a> SceneParser<'a> {
|
|||
},
|
||||
|
||||
'R' => match token.text.as_str() {
|
||||
"ReverseOrientation" => self.target.reverse_orientation(token.loc),
|
||||
"ReverseOrientation" => self.target.reverse_orientation(token.loc)?,
|
||||
"Rotate" => {
|
||||
let angle = self.expect_float()?;
|
||||
let ax = self.expect_float()?;
|
||||
|
|
@ -986,8 +1214,8 @@ impl<'a> SceneParser<'a> {
|
|||
},
|
||||
|
||||
'T' => match token.text.as_str() {
|
||||
"TransformBegin" => self.target.attribute_begin(token.loc),
|
||||
"TransformEnd" => self.target.attribute_end(token.loc),
|
||||
"TransformBegin" => self.target.attribute_begin(token.loc)?,
|
||||
"TransformEnd" => self.target.attribute_end(token.loc)?,
|
||||
"Transform" => {
|
||||
self.expect_token("[")?;
|
||||
let mut m = [0.0; 16];
|
||||
|
|
@ -995,18 +1223,18 @@ impl<'a> SceneParser<'a> {
|
|||
m[i] = self.expect_float()?;
|
||||
}
|
||||
self.expect_token("]")?;
|
||||
self.target.transform(&m, token.loc);
|
||||
self.target.transform(&m, token.loc)?;
|
||||
}
|
||||
"Translate" => {
|
||||
let x = self.expect_float()?;
|
||||
let y = 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" => {
|
||||
let s = 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" => {
|
||||
let name = self.expect_quoted_string()?;
|
||||
|
|
@ -1020,7 +1248,7 @@ impl<'a> SceneParser<'a> {
|
|||
¶ms,
|
||||
token.loc,
|
||||
arena.clone(),
|
||||
);
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
|
|
@ -1031,7 +1259,7 @@ impl<'a> SceneParser<'a> {
|
|||
},
|
||||
|
||||
'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" => {}
|
||||
_ => {
|
||||
return Err(ParserError::Generic(
|
||||
|
|
@ -1050,7 +1278,7 @@ impl<'a> SceneParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
self.target.end_of_files();
|
||||
self.target.end_of_files()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1079,7 +1307,12 @@ impl<'a> SceneParser<'a> {
|
|||
|
||||
fn parse_basic_entry<F>(&mut self, mut func: F) -> Result<(), ParserError>
|
||||
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_name = if type_token.is_quoted() {
|
||||
|
|
@ -1089,7 +1322,6 @@ impl<'a> SceneParser<'a> {
|
|||
};
|
||||
|
||||
let params = self.parse_parameters()?;
|
||||
func(self.target, &type_name, ¶ms, type_token.loc);
|
||||
Ok(())
|
||||
func(self.target, &type_name, ¶ms, type_token.loc)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue