Compare commits

..

No commits in common. "1b8ca71b0ebba1d7a57736ccc2a90e6a9ce14f77" and "8e8a3845f83b942b1bb7fbc086ca3fe7bdd60353" have entirely different histories.

40 changed files with 377 additions and 509 deletions

View file

@ -18,6 +18,8 @@ pub struct ConductorBxDF {
pub k: SampledSpectrum, pub k: SampledSpectrum,
} }
unsafe impl Send for ConductorBxDF {}
unsafe impl Sync for ConductorBxDF {}
impl ConductorBxDF { impl ConductorBxDF {
pub fn new( pub fn new(

View file

@ -32,6 +32,8 @@ pub struct MeasuredBxDF {
pub lambda: SampledWavelengths, pub lambda: SampledWavelengths,
} }
unsafe impl Send for MeasuredBxDF {}
unsafe impl Sync for MeasuredBxDF {}
impl MeasuredBxDF { impl MeasuredBxDF {
pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self { pub fn new(brdf: &MeasuredBxDFData, lambda: &SampledWavelengths) -> Self {

View file

@ -1117,6 +1117,8 @@ pub struct RGBToSpectrumTable {
pub n_nodes: u32, pub n_nodes: u32,
} }
unsafe impl Send for RGBToSpectrumTable {}
unsafe impl Sync for RGBToSpectrumTable {}
impl RGBToSpectrumTable { impl RGBToSpectrumTable {
#[inline(always)] #[inline(always)]

View file

@ -436,6 +436,8 @@ pub struct SpectralFilm {
pub bucket_splats: GVec<AtomicFloat>, pub bucket_splats: GVec<AtomicFloat>,
} }
unsafe impl Send for SpectralFilm {}
unsafe impl Sync for SpectralFilm {}
impl SpectralFilm { impl SpectralFilm {
pub fn new( pub fn new(
@ -616,6 +618,8 @@ pub enum Film {
Spectral(SpectralFilm), Spectral(SpectralFilm),
} }
unsafe impl Send for Film {}
unsafe impl Sync for Film {}
impl Film { impl Film {
pub fn base(&self) -> &FilmBase { pub fn base(&self) -> &FilmBase {

View file

@ -261,15 +261,6 @@ impl Bounds2f {
} }
impl Bounds3f { impl Bounds3f {
/// SAH bucket index for `p` along `dim`, in `[0, n_buckets)`. `self` is the
/// centroid bounds, so `offset` is in [0,1] and only `offset == 1` needs the
/// clamp -- same as pbrt's `if (b == nBuckets) b = nBuckets - 1`.
#[inline]
pub fn sah_bucket(&self, p: &Point3f, dim: usize, n_buckets: usize) -> usize {
let offset = self.offset(p)[dim];
((n_buckets as Float * offset) as usize).min(n_buckets - 1)
}
#[inline(always)] #[inline(always)]
pub fn intersect_p( pub fn intersect_p(
&self, &self,

View file

@ -1,5 +1,4 @@
use crate::core::light::Light; use crate::core::light::Light;
use crate::core::material::Material;
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -17,18 +16,8 @@ impl Default for LightIdx {
impl LightIdx { impl LightIdx {
#[inline] #[inline]
pub fn get(self, lights: &[Light]) -> &Light { pub fn get(self, lights: &[Light]) -> &Light {
debug_assert!(!self.is_none(), "LightIdx::get on NONE handle");
&lights[self.0 as usize] &lights[self.0 as usize]
} }
#[inline]
pub fn try_get(self, lights: &[Light]) -> Option<&Light> {
if self.is_none() {
None
} else {
lights.get(self.0 as usize)
}
}
} }
#[repr(C)] #[repr(C)]
@ -38,21 +27,6 @@ pub struct MaterialIdx(pub u32);
impl MaterialIdx { impl MaterialIdx {
pub const NONE: Self = MaterialIdx(u32::MAX); pub const NONE: Self = MaterialIdx(u32::MAX);
pub fn is_none(self) -> bool { self.0 == u32::MAX } pub fn is_none(self) -> bool { self.0 == u32::MAX }
#[inline]
pub fn get(self, materials: &[Material]) -> &Material {
debug_assert!(!self.is_none(), "MaterialIdx::get on NONE handle");
&materials[self.0 as usize]
}
#[inline]
pub fn try_get(self, materials: &[Material]) -> Option<&Material> {
if self.is_none() {
None
} else {
materials.get(self.0 as usize)
}
}
} }
impl Default for MaterialIdx { impl Default for MaterialIdx {

View file

@ -122,13 +122,10 @@ impl Pixels {
} }
pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float { pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float {
// SAFETY: `texel_offset` is in range by this fn's own contract. match self.format {
unsafe { PixelFormat::U8 => encoding.to_linear_scalar(self.read_u8(texel_offset)),
match self.format { PixelFormat::F16 => f16_to_f32_software(self.read_f16(texel_offset)),
PixelFormat::U8 => encoding.to_linear_scalar(self.read_u8(texel_offset)), PixelFormat::F32 => self.read_f32(texel_offset),
PixelFormat::F16 => f16_to_f32_software(self.read_f16(texel_offset)),
PixelFormat::F32 => self.read_f32(texel_offset),
}
} }
} }
@ -508,3 +505,12 @@ impl FilterFunction {
} }
} }
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct ImagePyramid {
pub levels: *const Ptr<Image>,
pub level_count: u32,
pub wrap_mode: WrapMode,
pub filter: FilterFunction,
pub max_aniso: f32,
}

View file

@ -235,6 +235,8 @@ pub struct SurfaceInteraction {
pub dvdy: Float, pub dvdy: Float,
} }
unsafe impl Send for SurfaceInteraction {}
unsafe impl Sync for SurfaceInteraction {}
impl SurfaceInteraction { impl SurfaceInteraction {
pub fn le( pub fn le(

View file

@ -176,9 +176,7 @@ impl LightBase {
} }
#[repr(C)] #[repr(C)]
// Default gives phi == 0, which `union` treats as empty -- that is what the SAH #[derive(Debug, Copy, Clone)]
// bucket accumulation starts from.
#[derive(Debug, Copy, Clone, Default)]
pub struct LightBounds { pub struct LightBounds {
pub bounds: Bounds3f, pub bounds: Bounds3f,
pub phi: Float, pub phi: Float,
@ -211,8 +209,7 @@ impl LightBounds {
impl LightBounds { impl LightBounds {
pub fn centroid(&self) -> Point3f { pub fn centroid(&self) -> Point3f {
// (pMin + pMax) / 2 -- Point has no scalar Div, so go via Vector. self.bounds.p_min + Vector3f::from(self.bounds.p_max) / 2.
Point3f::from((Vector3f::from(self.bounds.p_min) + Vector3f::from(self.bounds.p_max)) / 2.)
} }
pub fn importance(&self, p: Point3f, n: Normal3f) -> Float { pub fn importance(&self, p: Point3f, n: Normal3f) -> Float {
@ -270,12 +267,11 @@ impl LightBounds {
} }
pub fn union(a: &Self, b: &Self) -> Self { pub fn union(a: &Self, b: &Self) -> Self {
// If one LightBounds has zero power, return the *other* (lights.h:137).
if a.phi == 0. { if a.phi == 0. {
return *b; return a.clone();
} }
if b.phi == 0. { if b.phi == 0. {
return *a; return b.clone();
} }
let a_cone = DirectionCone::new(a.w, a.cos_theta_o); let a_cone = DirectionCone::new(a.w, a.cos_theta_o);
@ -315,13 +311,9 @@ pub trait LightTrait {
uv: Point2f, uv: Point2f,
w: Vector3f, w: Vector3f,
lambda: &SampledWavelengths, lambda: &SampledWavelengths,
) -> SampledSpectrum { ) -> SampledSpectrum;
self.base().l(p, n, uv, w, lambda)
}
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum { fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum;
self.base().le(ray, lambda)
}
fn light_type(&self) -> LightType { fn light_type(&self) -> LightType {
self.base().light_type self.base().light_type

View file

@ -97,6 +97,8 @@ pub struct MajorantGrid {
pub n_voxels: u32, pub n_voxels: u32,
} }
unsafe impl Send for MajorantGrid {}
unsafe impl Sync for MajorantGrid {}
impl MajorantGrid { impl MajorantGrid {
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
@ -713,6 +715,8 @@ pub struct MediumInterface {
pub outside: Ptr<Medium>, pub outside: Ptr<Medium>,
} }
unsafe impl Send for MediumInterface {}
unsafe impl Sync for MediumInterface {}
impl Default for MediumInterface { impl Default for MediumInterface {
fn default() -> Self { fn default() -> Self {

View file

@ -32,6 +32,8 @@ pub struct GeometricPrimitive {
pub alpha: Ptr<FloatTexture>, pub alpha: Ptr<FloatTexture>,
} }
unsafe impl Send for GeometricPrimitive {}
unsafe impl Sync for GeometricPrimitive {}
impl PrimitiveTrait for GeometricPrimitive { impl PrimitiveTrait for GeometricPrimitive {
fn bounds(&self) -> Bounds3f { fn bounds(&self) -> Bounds3f {

View file

@ -22,6 +22,8 @@ pub struct StandardSpectra {
pub d65: Ptr<DenselySampledSpectrum>, pub d65: Ptr<DenselySampledSpectrum>,
} }
unsafe impl Send for StandardSpectra {}
unsafe impl Sync for StandardSpectra {}
#[repr(C)] #[repr(C)]
#[enum_dispatch(SpectrumTrait)] #[enum_dispatch(SpectrumTrait)]

View file

@ -33,7 +33,17 @@ pub struct DiffuseAreaLight {
pub scale: Float, pub scale: Float,
} }
unsafe impl Send for DiffuseAreaLight {}
unsafe impl Sync for DiffuseAreaLight {}
impl DiffuseAreaLight { impl DiffuseAreaLight {
// fn l_base(&self, n: Normal3f, wo: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
// if !self.two_sided && n.dot(wo.into()) <= 0.0 {
// return SampledSpectrum::new(0.0);
// }
// self.lemit.sample(lambda) * self.scale
// }
fn alpha_masked(&self, intr: &Interaction) -> bool { fn alpha_masked(&self, intr: &Interaction) -> bool {
if self.alpha.is_null() { if self.alpha.is_null() {
return false; return false;
@ -119,7 +129,7 @@ impl LightTrait for DiffuseAreaLight {
let mut rgb = RGB::default(); let mut rgb = RGB::default();
uv[1] = 1. - uv[1]; uv[1] = 1. - uv[1];
for c in 0..3 { for c in 0..3 {
rgb[c] = self.image.bilerp_channel(uv, c); rgb[c] = self.image.bilerp_channel(uv, c as i32);
} }
let spec = RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero()); let spec = RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero());
@ -130,6 +140,10 @@ impl LightTrait for DiffuseAreaLight {
} }
} }
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
todo!()
}
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
let mut l = SampledSpectrum::new(0.); let mut l = SampledSpectrum::new(0.);
@ -138,7 +152,7 @@ impl LightTrait for DiffuseAreaLight {
for x in 0..self.image.resolution().x() { for x in 0..self.image.resolution().x() {
let mut rgb = RGB::default(); let mut rgb = RGB::default();
for c in 0..3 { for c in 0..3 {
rgb[c] = self.image.get_channel(Point2i::new(x, y), c); rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32);
} }
l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero()) l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero())
@ -154,7 +168,9 @@ impl LightTrait for DiffuseAreaLight {
} }
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {} fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
return;
}
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {

View file

@ -6,7 +6,7 @@ use crate::core::light::{LightBase, LightBounds, LightLiSample, LightSampleConte
use crate::core::spectrum::SpectrumTrait; use crate::core::spectrum::SpectrumTrait;
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths}; use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
use crate::utils::math::square; use crate::utils::math::square;
use crate::{Float, PI, Ptr}; use crate::{Float, Ptr, PI};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
#[repr(C)] #[repr(C)]
@ -75,6 +75,21 @@ impl LightTrait for DistantLight {
0. 0.
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
todo!()
}
fn preprocess(&mut self, scene_bounds: &Bounds3f) { fn preprocess(&mut self, scene_bounds: &Bounds3f) {
let (center, radius) = scene_bounds.bounding_sphere(); let (center, radius) = scene_bounds.bounding_sphere();
self.scene_center = center; self.scene_center = center;

View file

@ -51,29 +51,28 @@ impl LightTrait for GoniometricLight {
0. 0.
} }
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {} fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
todo!()
}
#[cfg(not(target_os = "cuda"))]
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
todo!()
}
#[cfg(not(target_os = "cuda"))]
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {
let mut sum_y = 0.; todo!()
for x in 0..self.image.resolution().x() {
for y in 0..self.image.resolution().y() {
sum_y += self.image.get_channel(Point2i::new(x, y), 0);
}
}
let phi = self.scale * self.iemit.max_value() * 4. * PI * sum_y
/ (self.image.resolution().x() * self.image.resolution().y()) as f32;
let p = self
.base()
.render_from_light
.apply_to_point(Point3f::new(0., 0., 0.));
Some(LightBounds::new(
&Bounds3f::from_points(p, p),
Vector3f::new(0., 0., 1.),
phi,
PI.cos(),
(PI / 2.).cos(),
false,
))
} }
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]

View file

@ -15,8 +15,8 @@ use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum}; use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square}; use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
use crate::utils::sampling::{ use crate::utils::sampling::{
AliasTable, PiecewiseConstant2D, WindowedPiecewiseConstant2D, sample_uniform_sphere, sample_uniform_sphere, uniform_sphere_pdf, AliasTable, PiecewiseConstant2D,
uniform_sphere_pdf, WindowedPiecewiseConstant2D,
}; };
use crate::utils::{Ptr, Transform}; use crate::utils::{Ptr, Transform};
use crate::{Float, PI}; use crate::{Float, PI};
@ -32,6 +32,9 @@ pub struct UniformInfiniteLight {
pub scene_radius: Float, pub scene_radius: Float,
} }
unsafe impl Send for UniformInfiniteLight {}
unsafe impl Sync for UniformInfiniteLight {}
impl UniformInfiniteLight { impl UniformInfiniteLight {
pub fn new( pub fn new(
render_from_light: Transform, render_from_light: Transform,
@ -97,16 +100,29 @@ impl LightTrait for UniformInfiniteLight {
uniform_sphere_pdf() uniform_sphere_pdf()
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, _ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum { fn le(&self, _ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
self.scale * self.lemit.sample(lambda) self.scale * self.lemit.sample(lambda)
} }
fn preprocess(&mut self, scene_bounds: &Bounds3f) { #[cfg(not(target_os = "cuda"))]
(self.scene_center, self.scene_radius) = scene_bounds.bounding_sphere(); fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
todo!()
} }
#[cfg(not(target_os = "cuda"))]
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {
None todo!()
} }
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
@ -128,6 +144,9 @@ pub struct ImageInfiniteLight {
pub scene_center: Point3f, pub scene_center: Point3f,
} }
unsafe impl Send for ImageInfiniteLight {}
unsafe impl Sync for ImageInfiniteLight {}
impl ImageInfiniteLight { impl ImageInfiniteLight {
pub fn new( pub fn new(
render_from_light: Transform, render_from_light: Transform,
@ -217,6 +236,17 @@ impl LightTrait for ImageInfiniteLight {
pdf / (4. * PI) pdf / (4. * PI)
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum { fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
let w_light = self let w_light = self
.base .base
@ -249,10 +279,14 @@ impl LightTrait for ImageInfiniteLight {
4. * PI * PI * square(self.scene_radius) * self.scale * sum_l / (width * height) as Float 4. * PI * PI * square(self.scene_radius) * self.scale * sum_l / (width * height) as Float
} }
#[cfg(not(target_os = "cuda"))]
fn preprocess(&mut self, scene_bounds: &Bounds3f) { fn preprocess(&mut self, scene_bounds: &Bounds3f) {
(self.scene_center, self.scene_radius) = scene_bounds.bounding_sphere(); let (scene_center, scene_radius) = scene_bounds.bounding_sphere();
self.scene_center = scene_center;
self.scene_radius = scene_radius;
} }
#[cfg(not(target_os = "cuda"))]
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {
None None
} }
@ -394,6 +428,17 @@ impl LightTrait for PortalInfiniteLight {
pdf / duv_dw pdf / duv_dw
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum { fn le(&self, ray: &Ray, lambda: &SampledWavelengths) -> SampledSpectrum {
let uv = self.image_from_render(ray.d.normalize()); let uv = self.image_from_render(ray.d.normalize());
let b = self.image_bounds(ray.o); let b = self.image_bounds(ray.o);

View file

@ -7,7 +7,7 @@ use crate::core::light::{
}; };
use crate::core::spectrum::SpectrumTrait; use crate::core::spectrum::SpectrumTrait;
use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths}; use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths};
use crate::{Float, INV_2_PI, PI, Ptr, Transform}; use crate::{Float, PI, Ptr, Transform};
use num_traits::Float as NumFloat; use num_traits::Float as NumFloat;
#[repr(C)] #[repr(C)]
@ -51,12 +51,30 @@ impl LightTrait for PointLight {
0. 0.
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
todo!()
}
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
4. * PI * self.scale * self.i.sample(&lambda) 4. * PI * self.scale * self.i.sample(&lambda)
} }
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {} #[cfg(not(target_os = "cuda"))]
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
todo!()
}
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {
@ -70,7 +88,7 @@ impl LightTrait for PointLight {
Vector3f::new(0., 0., 1.), Vector3f::new(0., 0., 1.),
phi, phi,
PI.cos(), PI.cos(),
INV_2_PI.cos(), (PI / 2.).cos(),
false, false,
)) ))
} }

View file

@ -75,6 +75,21 @@ impl LightTrait for ProjectionLight {
todo!() todo!()
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
todo!()
}
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
let mut sum = SampledSpectrum::new(0.); let mut sum = SampledSpectrum::new(0.);
let res = self.image.resolution(); let res = self.image.resolution();
@ -103,48 +118,11 @@ impl LightTrait for ProjectionLight {
self.scale * self.a * sum / (res.x() * res.y()) as Float self.scale * self.a * sum / (res.x() * res.y()) as Float
} }
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {} fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
todo!()
}
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {
let mut sum = 0.; todo!()
for v in 0..self.image.resolution().y() {
for u in 0..self.image.resolution().x() {
let uv = Point2i::new(u, v);
sum += self.image.get_channel(uv, 0).max(
self.image
.get_channel(uv, 1)
.max(self.image.get_channel(uv, 2)),
);
}
}
let phi =
self.scale * sum / (self.image.resolution().x() * self.image.resolution().y()) as f32;
let p_corner = Point3f::new(
self.screen_bounds.p_max.x(),
self.screen_bounds.p_max.y(),
0.,
);
let w_corner = Vector3f::from(self.light_from_screen.apply_to_point(p_corner)).normalize();
let cos_total_width = cos_theta(w_corner);
let p = self
.base
.render_from_light
.apply_to_point(Point3f::new(0., 0., 0.));
let w = self
.base
.render_from_light
.apply_to_vector(Vector3f::new(0., 0., 1.));
Some(LightBounds::new(
&Bounds3f::from_points(p, p),
w,
phi,
1.,
cos_total_width,
false,
))
} }
} }

View file

@ -1,7 +1,7 @@
use crate::core::LightIdx;
use crate::core::geometry::primitives::OctahedralVector; use crate::core::geometry::primitives::OctahedralVector;
use crate::core::geometry::{Bounds3f, DirectionCone, Normal3f, Point3f, Vector3f, VectorLike}; use crate::core::geometry::{Bounds3f, DirectionCone, Normal3f, Point3f, Vector3f, VectorLike};
use crate::core::light::{Light, LightBounds, LightSampleContext}; use crate::core::light::{Light, LightBounds, LightSampleContext};
use crate::core::LightIdx;
use crate::spectra::{SampledSpectrum, SampledWavelengths}; use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::{clamp, lerp, sample_discrete}; use crate::utils::math::{clamp, lerp, sample_discrete};
use crate::utils::math::{safe_sqrt, square}; use crate::utils::math::{safe_sqrt, square};
@ -164,25 +164,27 @@ impl CompactLightBounds {
} }
} }
#[repr(C)] #[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct SampledLight { pub struct SampledLight {
pub light: LightIdx, pub light: LightIdx,
pub p: Float, pub p: Float,
} }
// impl SampledLight {
// pub fn new(light: Light, p: Float) -> Self {
// Self {
// light: Ptr::from(&light),
// p,
// }
// }
// }
//
#[enum_dispatch] #[enum_dispatch]
pub trait LightSamplerTrait { pub trait LightSamplerTrait {
fn sample_with_context(&self, ctx: &LightSampleContext, u: Float) -> Option<SampledLight>;
fn pmf_with_context(&self, ctx: &LightSampleContext, idx: LightIdx) -> Float;
fn sample(&self, u: Float) -> Option<SampledLight>; fn sample(&self, u: Float) -> Option<SampledLight>;
fn pmf(&self, idx: LightIdx) -> Float; fn pmf(&self, idx: LightIdx) -> Float;
/// Samplers that ignore the shading context inherit these.
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
self.sample(u)
}
fn pmf_with_context(&self, _ctx: &LightSampleContext, idx: LightIdx) -> Float {
self.pmf(idx)
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -193,8 +195,7 @@ pub enum LightSampler {
BVH(BVHLightSampler), BVH(BVHLightSampler),
} }
#[repr(C)] #[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug)]
pub struct UniformLightSampler { pub struct UniformLightSampler {
lights_len: u32, lights_len: u32,
} }
@ -206,6 +207,10 @@ impl UniformLightSampler {
} }
impl LightSamplerTrait for UniformLightSampler { impl LightSamplerTrait for UniformLightSampler {
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
self.sample(u)
}
fn sample(&self, u: Float) -> Option<SampledLight> { fn sample(&self, u: Float) -> Option<SampledLight> {
if self.lights_len == 0 { if self.lights_len == 0 {
return None; return None;
@ -217,6 +222,10 @@ impl LightSamplerTrait for UniformLightSampler {
}) })
} }
fn pmf_with_context(&self, _ctx: &LightSampleContext, _idx: LightIdx) -> Float {
self.pmf(_idx)
}
fn pmf(&self, _idx: LightIdx) -> Float { fn pmf(&self, _idx: LightIdx) -> Float {
if self.lights_len == 0 { if self.lights_len == 0 {
return 0.0; return 0.0;
@ -228,10 +237,22 @@ impl LightSamplerTrait for UniformLightSampler {
#[repr(C)] #[repr(C)]
#[derive(Clone, Debug, Copy)] #[derive(Clone, Debug, Copy)]
pub struct PowerLightSampler { pub struct PowerLightSampler {
pub lights_len: u32,
pub alias_table: Ptr<AliasTable>, pub alias_table: Ptr<AliasTable>,
} }
unsafe impl Send for PowerLightSampler {}
unsafe impl Sync for PowerLightSampler {}
impl LightSamplerTrait for PowerLightSampler { impl LightSamplerTrait for PowerLightSampler {
fn sample_with_context(&self, _ctx: &LightSampleContext, u: Float) -> Option<SampledLight> {
self.sample(u)
}
fn pmf_with_context(&self, _ctx: &LightSampleContext, idx: LightIdx) -> Float {
self.pmf(idx)
}
fn sample(&self, u: Float) -> Option<SampledLight> { fn sample(&self, u: Float) -> Option<SampledLight> {
if self.alias_table.size() == 0 { if self.alias_table.size() == 0 {
return None; return None;
@ -312,20 +333,17 @@ impl LightBVHNode {
pub fn child_or_light_index(&self) -> u32 { pub fn child_or_light_index(&self) -> u32 {
self.packed_data & Self::INDEX_MASK self.packed_data & Self::INDEX_MASK
} }
}
/// Canary value stored in `bit_trails` for a light that is not a BVH leaf, i.e. an pub fn sample(&self, _ctx: &LightSampleContext, _u: Float) -> Option<SampledLight> {
/// infinite light or one with negative `phi`. Stands in for pbrt's todo!("Implement LightBVHNode::Sample logic")
/// `lightToBitTrail.HasKey(light)`. }
pub const NO_BIT_TRAIL: u64 = u64::MAX; }
#[derive(Clone, Debug, Copy)] #[derive(Clone, Debug, Copy)]
pub struct BVHLightSampler { pub struct BVHLightSampler {
pub nodes: Ptr<LightBVHNode>, pub nodes: Ptr<LightBVHNode>,
/// Handles of the infinite lights, in scene order. pub lights: Ptr<Light>,
pub infinite_lights: Ptr<LightIdx>, pub infinite_lights: Ptr<Light>,
/// Indexed by *global* light index, matching the leaf indices stored in
/// `nodes`; `NO_BIT_TRAIL` where the light has no leaf.
pub bit_trails: Ptr<u64>, pub bit_trails: Ptr<u64>,
pub nodes_len: u32, pub nodes_len: u32,
pub lights_len: u32, pub lights_len: u32,
@ -333,46 +351,42 @@ pub struct BVHLightSampler {
pub all_light_bounds: Bounds3f, pub all_light_bounds: Bounds3f,
} }
unsafe impl Send for BVHLightSampler {}
unsafe impl Sync for BVHLightSampler {}
impl BVHLightSampler { impl BVHLightSampler {
// Each array is paired with the length stored alongside it, so the slice can
// only be formed one way and indexing past the end is a bounds check rather
// than a silent read. These three are the only `unsafe` in the sampler.
#[inline(always)]
fn nodes(&self) -> &[LightBVHNode] {
unsafe { self.nodes.as_slice(self.nodes_len as usize) }
}
#[inline(always)]
fn infinite_lights(&self) -> &[LightIdx] {
unsafe {
self.infinite_lights
.as_slice(self.infinite_lights_len as usize)
}
}
/// One bit trail per light, indexed by global light index.
#[inline(always)]
fn bit_trails(&self) -> &[u64] {
unsafe { self.bit_trails.as_slice(self.lights_len as usize) }
}
#[inline(always)] #[inline(always)]
fn node(&self, idx: usize) -> &LightBVHNode { fn node(&self, idx: usize) -> &LightBVHNode {
&self.nodes()[idx] unsafe { self.nodes.at(idx) }
} }
#[inline(always)] #[inline(always)]
fn infinite_light(&self, idx: usize) -> LightIdx { fn light(&self, idx: usize) -> Light {
self.infinite_lights()[idx] unsafe { *self.lights.at(idx) }
}
#[inline(always)]
fn infinite_light(&self, idx: usize) -> Light {
unsafe { *self.infinite_lights.at(idx) }
} }
#[inline(always)] #[inline(always)]
fn bit_trail(&self, idx: usize) -> u64 { fn bit_trail(&self, idx: usize) -> u64 {
self.bit_trails()[idx] unsafe { *self.bit_trails.at(idx) }
} }
pub fn evaluate_cost(b: &LightBounds, bounds: &Bounds3f, dim: usize) -> Float { #[inline(always)]
fn light_index_in(&self, base: Ptr<Light>, len: u32, light: &Light) -> Option<usize> {
let target = light as *const Light;
for i in 0..len as usize {
if unsafe { base.add(i) }.as_raw() == target {
return Some(i);
}
}
None
}
fn evaluate_cost(&self, b: &LightBounds, bounds: &Bounds3f, dim: usize) -> Float {
let theta_o = b.cos_theta_o.acos(); let theta_o = b.cos_theta_o.acos();
let theta_e = b.cos_theta_e.acos(); let theta_e = b.cos_theta_e.acos();
let theta_w = (theta_o + theta_e).min(PI); let theta_w = (theta_o + theta_e).min(PI);
@ -395,14 +409,12 @@ impl LightSamplerTrait for BVHLightSampler {
if u < p_inf { if u < p_inf {
u /= p_inf; u /= p_inf;
// Uniformly sample an infinite light and return its handle // sample uniformly from infinite lights; their global index equals their
// (`lightsamplers.h:277`: `infiniteLights[index]`). // position in the infinite_lights array (infinite lights are at 0..n_inf
let ind = ((u * inf_size) as usize).min(self.infinite_lights_len as usize - 1); // in the scene lights array by construction)
let ind = (u * inf_size).min(inf_size - 1.) as u32;
let pmf = p_inf / inf_size; let pmf = p_inf / inf_size;
return Some(SampledLight { return Some(SampledLight { light: LightIdx(ind), p: pmf });
light: self.infinite_light(ind),
p: pmf,
});
} }
if self.nodes_len == 0 { if self.nodes_len == 0 {
@ -452,18 +464,18 @@ impl LightSamplerTrait for BVHLightSampler {
let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. }; let empty_nodes = if self.nodes_len == 0 { 0. } else { 1. };
let n_infinite = self.infinite_lights_len as Float; let n_infinite = self.infinite_lights_len as Float;
// Infinite lights occupy indices 0..infinite_lights_len in the global array
if idx.0 < self.infinite_lights_len {
return 1.0 / (n_infinite + empty_nodes);
}
let light_index = idx.0 as usize; let light_index = idx.0 as usize;
if light_index >= self.lights_len as usize { if light_index >= self.lights_len as usize {
return 0.0; return 0.0;
} }
// bit_trail[light_index] encodes the path from root to this light's leaf. // bit_trail[light_index] encodes the path from root to the leaf for this light
// Canary value to check if no leaf. No leaf, it his infinite, or its power
// was zero
let mut bit_trail = self.bit_trail(light_index); let mut bit_trail = self.bit_trail(light_index);
if bit_trail == NO_BIT_TRAIL {
return 1.0 / (n_infinite + empty_nodes);
}
let p_inf = n_infinite / (n_infinite + empty_nodes); let p_inf = n_infinite / (n_infinite + empty_nodes);
let mut pmf = 1.0 - p_inf; let mut pmf = 1.0 - p_inf;
let mut node_ind = 0; let mut node_ind = 0;

View file

@ -65,6 +65,21 @@ impl LightTrait for SpotLight {
0. 0.
} }
fn l(
&self,
_p: Point3f,
_n: Normal3f,
_uv: Point2f,
_w: Vector3f,
_lambda: &SampledWavelengths,
) -> SampledSpectrum {
todo!()
}
fn le(&self, _ray: &Ray, _lambda: &SampledWavelengths) -> SampledSpectrum {
todo!()
}
#[cfg(not(target_os = "cuda"))] #[cfg(not(target_os = "cuda"))]
fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum { fn phi(&self, lambda: SampledWavelengths) -> SampledSpectrum {
self.scale self.scale
@ -74,8 +89,12 @@ impl LightTrait for SpotLight {
* ((1. - self.cos_falloff_start) + (self.cos_falloff_start - self.cos_falloff_end) / 2.) * ((1. - self.cos_falloff_start) + (self.cos_falloff_start - self.cos_falloff_end) / 2.)
} }
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {} #[cfg(not(target_os = "cuda"))]
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
todo!()
}
#[cfg(not(target_os = "cuda"))]
fn bounds(&self) -> Option<LightBounds> { fn bounds(&self) -> Option<LightBounds> {
let p = self let p = self
.base .base

View file

@ -31,6 +31,10 @@ pub struct BilinearPatchMesh {
pub image_distribution: Ptr<PiecewiseConstant2D>, pub image_distribution: Ptr<PiecewiseConstant2D>,
} }
unsafe impl Send for TriangleMesh {}
unsafe impl Sync for TriangleMesh {}
unsafe impl Send for BilinearPatchMesh {}
unsafe impl Sync for BilinearPatchMesh {}
impl TriangleMesh { impl TriangleMesh {
pub fn new( pub fn new(

View file

@ -74,6 +74,8 @@ pub struct RGBColorSpace {
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>, pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
} }
unsafe impl Send for RGBColorSpace {}
unsafe impl Sync for RGBColorSpace {}
impl RGBColorSpace { impl RGBColorSpace {
pub fn to_xyz(&self, rgb: RGB) -> XYZ { pub fn to_xyz(&self, rgb: RGB) -> XYZ {

View file

@ -38,6 +38,8 @@ pub struct DenselySampledSpectrum {
pub values: GVec<Float>, pub values: GVec<Float>,
} }
unsafe impl Send for DenselySampledSpectrum {}
unsafe impl Sync for DenselySampledSpectrum {}
impl DenselySampledSpectrum { impl DenselySampledSpectrum {
pub fn new(lambda_min: i32, lambda_max: i32, values: GVec<Float>) -> Self { pub fn new(lambda_min: i32, lambda_max: i32, values: GVec<Float>) -> Self {
@ -260,6 +262,8 @@ impl PiecewiseLinearSpectrum {
} }
} }
unsafe impl Send for PiecewiseLinearSpectrum {}
unsafe impl Sync for PiecewiseLinearSpectrum {}
impl SpectrumTrait for PiecewiseLinearSpectrum { impl SpectrumTrait for PiecewiseLinearSpectrum {
fn evaluate(&self, lambda: Float) -> Float { fn evaluate(&self, lambda: Float) -> Float {

View file

@ -22,6 +22,8 @@ pub struct MarbleTexture {
pub colorspace: Ptr<RGBColorSpace>, pub colorspace: Ptr<RGBColorSpace>,
} }
unsafe impl Send for MarbleTexture {}
unsafe impl Sync for MarbleTexture {}
impl MarbleTexture { impl MarbleTexture {
pub fn evaluate( pub fn evaluate(

View file

@ -17,8 +17,7 @@ unsafe impl Allocator for SystemAlloc {
} }
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) { unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
// SAFETY: forwarded verbatim; caller upholds Allocator's contract. Global.deallocate(ptr, layout)
unsafe { Global.deallocate(ptr, layout) }
} }
unsafe fn grow( unsafe fn grow(
@ -27,8 +26,7 @@ unsafe impl Allocator for SystemAlloc {
old_layout: Layout, old_layout: Layout,
new_layout: Layout, new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: forwarded verbatim; caller upholds Allocator's contract. Global.grow(ptr, old_layout, new_layout)
unsafe { Global.grow(ptr, old_layout, new_layout) }
} }
unsafe fn shrink( unsafe fn shrink(
@ -37,8 +35,7 @@ unsafe impl Allocator for SystemAlloc {
old_layout: Layout, old_layout: Layout,
new_layout: Layout, new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ) -> Result<NonNull<[u8]>, AllocError> {
// SAFETY: forwarded verbatim; caller upholds Allocator's contract. Global.shrink(ptr, old_layout, new_layout)
unsafe { Global.shrink(ptr, old_layout, new_layout) }
} }
} }

View file

@ -712,6 +712,8 @@ pub struct PiecewiseConstant1D {
pub func_integral: Float, pub func_integral: Float,
} }
unsafe impl Send for PiecewiseConstant1D {}
unsafe impl Sync for PiecewiseConstant1D {}
impl PiecewiseConstant1D { impl PiecewiseConstant1D {
pub fn new(f: &[Float]) -> Self { pub fn new(f: &[Float]) -> Self {
@ -1099,6 +1101,8 @@ pub struct AliasTable {
pub bins: GVec<Bin>, pub bins: GVec<Bin>,
} }
unsafe impl Send for AliasTable {}
unsafe impl Sync for AliasTable {}
impl AliasTable { impl AliasTable {
pub fn new(weights: &[Float]) -> Self { pub fn new(weights: &[Float]) -> Self {

View file

@ -115,7 +115,6 @@ impl<S: SoA> WorkQueue<S> {
i, i,
self.size() self.size()
); );
// SAFETY: bounds checked above; caller guarantees the slot is initialised. self.storage.get(i)
unsafe { self.storage.get(i) }
} }
} }

View file

@ -375,7 +375,12 @@ impl CreateBVH for BVHAggregate {
} }
let mut buckets = [Bucket::default(); N_BUCKETS]; let mut buckets = [Bucket::default(); N_BUCKETS];
let get_bucket_idx = |node: &BVHBuildNode| -> usize { let get_bucket_idx = |node: &BVHBuildNode| -> usize {
centroid_bounds.sah_bucket(&node.bounds().centroid(), dim, N_BUCKETS) let offset = centroid_bounds.offset(&node.bounds().centroid())[dim];
let mut b = (N_BUCKETS as Float * offset) as usize;
if b == N_BUCKETS {
b = N_BUCKETS - 1;
}
b
}; };
// Initialize _Bucket_ for HLBVH SAH partition buckets // Initialize _Bucket_ for HLBVH SAH partition buckets
@ -530,7 +535,11 @@ fn build_recursive(
const N_BUCKETS: usize = 12; const N_BUCKETS: usize = 12;
let mut buckets = [BVHSplitBucket::default(); N_BUCKETS]; let mut buckets = [BVHSplitBucket::default(); N_BUCKETS];
for prim in bvh_primitives.iter() { for prim in bvh_primitives.iter() {
let b = centroid_bounds.sah_bucket(&prim.centroid, dim, N_BUCKETS); let mut b =
(N_BUCKETS as Float * centroid_bounds.offset(&prim.centroid)[dim]) as usize;
if b == N_BUCKETS {
b = N_BUCKETS - 1;
}
buckets[b].count += 1; buckets[b].count += 1;
buckets[b].bounds = buckets[b].bounds.union(prim.bounds); buckets[b].bounds = buckets[b].bounds.union(prim.bounds);
} }

View file

@ -44,7 +44,7 @@ impl InteractionGetter for SurfaceInteraction {
if self.material.is_none() { if self.material.is_none() {
return None; return None;
} }
let mut active_mat: &Material = self.material.get(materials); let mut active_mat: &Material = &materials[self.material.0 as usize];
let material = { let material = {
let tex_eval = UniversalTextureEvaluator; let tex_eval = UniversalTextureEvaluator;
while let Material::Mix(mix) = active_mat { while let Material::Mix(mix) = active_mat {
@ -82,7 +82,7 @@ impl InteractionGetter for SurfaceInteraction {
if self.material.is_none() { if self.material.is_none() {
return None; return None;
} }
let mut active_mat: &Material = self.material.get(materials); let mut active_mat: &Material = &materials[self.material.0 as usize];
let tex_eval = UniversalTextureEvaluator; let tex_eval = UniversalTextureEvaluator;
while let Material::Mix(mix) = active_mat { while let Material::Mix(mix) = active_mat {
let ctx = MaterialEvalContext::from(self); let ctx = MaterialEvalContext::from(self);

View file

@ -291,8 +291,6 @@ pub trait CreateMedium {
} }
} }
impl CreateMedium for Medium {}
fn create_homogeneous( fn create_homogeneous(
parameters: &ParameterDictionary, parameters: &ParameterDictionary,
loc: &FileLoc, loc: &FileLoc,

View file

@ -14,7 +14,7 @@ use crate::wavefront::integrator::CpuWavefrontRenderer;
use shared::Float; use shared::Float;
pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> { pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
let media = scene.create_media(arena); let media = scene.create_media();
let textures = scene.create_textures(arena); let textures = scene.create_textures(arena);
let (named_materials, materials, _default_mtl) = scene.create_materials(&textures, arena)?; let (named_materials, materials, _default_mtl) = scene.create_materials(&textures, arena)?;
let (lights, al_map) = scene.create_lights(&textures, &media, arena); let (lights, al_map) = scene.create_lights(&textures, &media, arena);

View file

@ -152,20 +152,11 @@ impl BasicSceneBuilder {
} }
pub fn new(scene: Arc<BasicScene>) -> Self { pub fn new(scene: Arc<BasicScene>) -> Self {
// pbrt seeds material index 0 with a default "diffuse" so that a shape
// declared before any Material statement still has one (scene.cpp:102).
let default_material_index = scene.add_material(SceneEntity {
name: "diffuse".into(),
loc: FileLoc::default(),
parameters: ParameterDictionary::new(Vec::new(), None).unwrap(),
});
Self { Self {
scene, scene,
current_block: BlockState::OptionsBlock, current_block: BlockState::OptionsBlock,
graphics_state: GraphicsState { graphics_state: GraphicsState {
active_transform_bits: Self::ALL_TRANSFORM_BITS, active_transform_bits: Self::ALL_TRANSFORM_BITS,
current_material_index: Some(default_material_index),
..Default::default() ..Default::default()
}, },
pushed_graphics_states: Vec::new(), pushed_graphics_states: Vec::new(),
@ -521,31 +512,23 @@ impl ParserTarget for BasicSceneBuilder {
params: ParsedParameterVector, params: ParsedParameterVector,
loc: FileLoc, loc: FileLoc,
) -> Result<(), ParserError> { ) -> Result<(), ParserError> {
self.verify_world("MakeNamedMaterial", &loc)?;
let curr_name = normalize_utf8(name); let curr_name = normalize_utf8(name);
if !self.medium_names.insert(curr_name.clone()) { if !self.named_material_names.insert(curr_name.to_string()) {
return Err(ParserError::Generic( return Err(ParserError::Generic(
format!("Named medium '{}' redefined.", name), format!("Named material '{}' redefined.", name),
loc, loc,
)); ));
} }
let parameters = self.make_params(params, &loc)?;
let parameters = ParameterDictionary::from_array( let entity = SceneEntity {
params, name: name.to_string(),
&self.graphics_state.medium_attributes, loc,
self.graphics_state.color_space.clone(), parameters,
)?;
let render_from_object = self.render_from_object();
let entity = MediumSceneEntity {
base: SceneEntity {
name: name.to_string(),
loc,
parameters,
},
render_from_object,
}; };
self.scene.add_medium(&curr_name, entity); self.scene.add_named_material(&curr_name, entity);
Ok(()) Ok(())
} }
@ -758,15 +741,10 @@ impl ParserTarget for BasicSceneBuilder {
loc: FileLoc, loc: FileLoc,
) -> Result<(), ParserError> { ) -> Result<(), ParserError> {
self.verify_world("material", &loc)?; self.verify_world("material", &loc)?;
let dict = ParameterDictionary::from_array(
params,
&self.graphics_state.material_attributes,
self.graphics_state.color_space.clone(),
)?;
let entity = SceneEntity { let entity = SceneEntity {
name: name.to_string(), name: name.to_string(),
loc, loc,
parameters: dict, parameters: ParameterDictionary::new(params.clone(), None).unwrap(),
}; };
let idx = self.scene.add_material(entity); let idx = self.scene.add_material(entity);
self.graphics_state.current_material_index = Some(idx); self.graphics_state.current_material_index = Some(idx);
@ -776,44 +754,15 @@ impl ParserTarget for BasicSceneBuilder {
fn make_named_material( fn make_named_material(
&mut self, &mut self,
name: &str, _name: &str,
params: ParsedParameterVector, _params: ParsedParameterVector,
loc: FileLoc, _loc: FileLoc,
) -> Result<(), ParserError> { ) -> Result<(), ParserError> {
self.verify_world("MakeNamedMaterial", &loc)?; todo!()
let curr_name = normalize_utf8(name);
if !self.named_material_names.insert(curr_name.clone()) {
return Err(ParserError::Generic(
format!("Named material '{}' redefined.", name),
loc,
));
}
let parameters = ParameterDictionary::from_array(
params,
&self.graphics_state.material_attributes,
self.graphics_state.color_space.clone(),
)?;
// pbrt stores an empty entity name here: the material type comes from the
// "type" parameter (scene.cpp:719).
self.scene.add_named_material(
&curr_name,
SceneEntity {
name: String::new(),
loc,
parameters,
},
);
Ok(())
} }
fn named_material(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError> { fn named_material(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
self.verify_world("NamedMaterial", &loc)?; todo!()
self.graphics_state.current_material_name = normalize_utf8(name);
self.graphics_state.current_material_index = None;
Ok(())
} }
fn light_source( fn light_source(

View file

@ -6,7 +6,6 @@ use crate::core::film::FilmFactory;
use crate::core::filter::FilterFactory; use crate::core::filter::FilterFactory;
use crate::core::image::{HostImage, ImageIO}; use crate::core::image::{HostImage, ImageIO};
use crate::core::material::MaterialFactory; use crate::core::material::MaterialFactory;
use crate::core::medium::CreateMedium;
use crate::core::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive}; use crate::core::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive};
use crate::core::sampler::SamplerFactory; use crate::core::sampler::SamplerFactory;
use crate::core::shape::{ShapeFactory, ShapeWithContext}; use crate::core::shape::{ShapeFactory, ShapeWithContext};
@ -88,21 +87,21 @@ fn resolve_material(
MaterialRef::Name(name) => match named_materials.get(name) { MaterialRef::Name(name) => match named_materials.get(name) {
Some(m) => *m, Some(m) => *m,
None => { None => {
log::error!("{}: named material '{}' not found", loc, name); MaterialIdx::default()
MaterialIdx::NONE // log::error!("{}: named material '{}' not found", loc, name);
// crate::core::material::default_diffuse_material(arena)
} }
}, },
// Anonymous materials are placed at the front of `materials`, so the
// index handed out by `add_material` is the handle directly.
MaterialRef::Index(idx) => { MaterialRef::Index(idx) => {
if *idx < materials.len() { if *idx < materials.len() {
MaterialIdx(*idx as u32) MaterialIdx(*idx as u32)
} else { } else {
log::error!("{}: material index {} out of bounds", loc, idx); MaterialIdx::default()
MaterialIdx::NONE // log::error!("{}: material index {} out of bounds", loc, idx);
// crate::core::material::default_diffuse_material(arena)
} }
} }
MaterialRef::None => MaterialIdx::NONE, MaterialRef::None => MaterialIdx::default(),
} }
} }
@ -259,11 +258,6 @@ impl BasicScene {
state.named_materials.push((name.to_string(), material)); state.named_materials.push((name.to_string(), material));
} }
pub fn add_medium(&self, name: &str, medium: MediumSceneEntity) {
let mut state = self.media_state.lock();
state.entities.push((name.to_string(), medium));
}
pub fn add_material(&self, material: SceneEntity) -> usize { pub fn add_material(&self, material: SceneEntity) -> usize {
let mut state = self.material_state.lock(); let mut state = self.material_state.lock();
self.start_loading_normal_maps(&mut state, &material.parameters); self.start_loading_normal_maps(&mut state, &material.parameters);
@ -504,16 +498,13 @@ impl BasicScene {
} }
} }
// Named materials must be *created* first: an anonymous material may be a let mut materials: Vec<Material> = Vec::new();
// mix that resolves its sub-materials by name. They are *placed* after the let mut named_materials: HashMap<String, MaterialIdx> = HashMap::new();
// anonymous ones though, so that `MaterialRef::Index(i)` — an index into
// `state.materials` handed out by `add_material` — is `MaterialIdx(i)`.
let mut named_created: Vec<(String, Material)> = Vec::new();
// Value map for resolving named sub-materials (e.g. mix) during creation. // Value map for resolving named sub-materials (e.g. mix) during creation.
let mut named_values: HashMap<String, Material> = HashMap::new(); let mut named_values: HashMap<String, Material> = HashMap::new();
for (name, entity) in &state.named_materials { for (name, entity) in &state.named_materials {
if named_values.contains_key(name) { if named_materials.contains_key(name) {
log::error!( log::error!(
"{}: trying to redefine named material '{}'.", "{}: trying to redefine named material '{}'.",
entity.loc, entity.loc,
@ -540,8 +531,10 @@ impl BasicScene {
arena, arena,
) { ) {
Ok(mat) => { Ok(mat) => {
let idx = MaterialIdx(materials.len() as u32);
materials.push(mat);
named_values.insert(name.clone(), mat); named_values.insert(name.clone(), mat);
named_created.push((name.clone(), mat)); named_materials.insert(name.clone(), idx);
} }
Err(e) => { Err(e) => {
log::error!( log::error!(
@ -554,9 +547,7 @@ impl BasicScene {
} }
} }
// Indexed (anonymous) materials occupy [0, state.materials.len()) so that // Indexed (anonymous) materials, appended after named ones.
// a `MaterialRef::Index` needs no offset.
let mut materials: Vec<Material> = Vec::with_capacity(state.materials.len());
for entity in &state.materials { for entity in &state.materials {
let result: Result<Material> = (|| { let result: Result<Material> = (|| {
let normal_map = self.get_normal_map(&state, &entity.parameters)?; let normal_map = self.get_normal_map(&state, &entity.parameters)?;
@ -583,19 +574,13 @@ impl BasicScene {
materials.push(mat); materials.push(mat);
} }
let mut named_materials: HashMap<String, MaterialIdx> = HashMap::new();
for (name, mat) in named_created {
named_materials.insert(name, MaterialIdx(materials.len() as u32));
materials.push(mat);
}
let default_mtl = MaterialIdx(materials.len() as u32); let default_mtl = MaterialIdx(materials.len() as u32);
materials.push(crate::core::material::default_diffuse_material(arena)); materials.push(crate::core::material::default_diffuse_material(arena));
Ok((named_materials, materials, default_mtl)) Ok((named_materials, materials, default_mtl))
} }
pub fn create_media(&self, arena: &Arena) -> HashMap<String, Arc<Medium>> { pub fn create_media(&self) -> HashMap<String, Arc<Medium>> {
let mut state = self.media_state.lock(); let mut state = self.media_state.lock();
if !state.jobs.is_empty() { if !state.jobs.is_empty() {
let jobs: Vec<(String, AsyncJob<Medium>)> = state.jobs.drain().collect(); let jobs: Vec<(String, AsyncJob<Medium>)> = state.jobs.drain().collect();
@ -603,29 +588,6 @@ impl BasicScene {
state.map.insert(name, Arc::new(job.wait())); state.map.insert(name, Arc::new(job.wait()));
} }
} }
let entities: Vec<(String, MediumSceneEntity)> = state.entities.drain(..).collect();
for (name, entity) in entities {
if entity.render_from_object.is_animated() {
log::warn!(
"{}: animated media aren't supported, using start transform.",
entity.base.loc
);
}
match Medium::create(
&entity.base.name,
&entity.base.parameters,
entity.render_from_object.start_transform,
&entity.base.loc,
arena,
) {
Ok(m) => {
state.map.insert(name, Arc::new(*m));
}
Err(e) => log::error!("{}: failed to create medium: {}", entity.base.loc, e),
}
}
state.map.clone() state.map.clone()
} }
@ -749,7 +711,7 @@ impl BasicScene {
textures, textures,
named_materials, named_materials,
materials, materials,
Some(area_map), area_map,
media, media,
arena, arena,
); );
@ -761,6 +723,7 @@ impl BasicScene {
textures, textures,
named_materials, named_materials,
materials, materials,
area_map,
media, media,
arena, arena,
); );
@ -777,7 +740,7 @@ impl BasicScene {
textures, textures,
named_materials, named_materials,
materials, materials,
None, area_map,
media, media,
arena, arena,
); );
@ -786,6 +749,7 @@ impl BasicScene {
textures, textures,
named_materials, named_materials,
materials, materials,
area_map,
media, media,
arena, arena,
); );
@ -932,23 +896,13 @@ impl BasicScene {
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, MaterialIdx>, named_materials: &HashMap<String, MaterialIdx>,
materials: &[Material], materials: &[Material],
// `None` for object-instance definitions: the map is keyed by the area_map: &AreaLightMap,
// top-level shape index, so it must not be consulted for the
// independently-numbered shapes inside an instance definition.
area_map: Option<&AreaLightMap>,
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
arena: &Arena, arena: &Arena,
) -> Vec<Primitive> { ) -> Vec<Primitive> {
let mut primitives = Vec::new(); let mut primitives = Vec::new();
for (entity_idx, entity) in shapes.iter().enumerate() { for (entity_idx, entity) in shapes.iter().enumerate() {
if area_map.is_none() && entity.light_index.is_some() {
log::error!(
"{}: area lights are not supported with object instancing.",
entity.base.loc
);
}
let created_shapes = match Shape::create( let created_shapes = match Shape::create(
&entity.base.name, &entity.base.name,
*entity.render_from_object, *entity.render_from_object,
@ -992,7 +946,8 @@ impl BasicScene {
for (sub_idx, shape) in created_shapes.into_iter().enumerate() { for (sub_idx, shape) in created_shapes.into_iter().enumerate() {
// look up the pre-created light index instead of creating one // look up the pre-created light index instead of creating one
let light_idx = area_map let light_idx = area_map
.and_then(|m| m.get(&(entity_idx, sub_idx)).copied()) .get(&(entity_idx, sub_idx))
.copied()
.unwrap_or(LightIdx::NONE); .unwrap_or(LightIdx::NONE);
let prim = let prim =
@ -1018,6 +973,7 @@ impl BasicScene {
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, MaterialIdx>, named_materials: &HashMap<String, MaterialIdx>,
materials: &[Material], materials: &[Material],
area_map: &AreaLightMap,
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
arena: &Arena, arena: &Arena,
) -> Vec<Primitive> { ) -> Vec<Primitive> {
@ -1259,10 +1215,10 @@ impl BasicScene {
entities: &[ShapeSceneEntity], entities: &[ShapeSceneEntity],
loaded: Vec<ShapeWithContext>, loaded: Vec<ShapeWithContext>,
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, MaterialIdx>, named_materials: &HashMap<String, Material>,
materials: &[Material], materials: &[Material],
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
shape_lights: &AreaLightMap, shape_lights: &HashMap<usize, Vec<Light>>,
) -> Vec<Primitive> { ) -> Vec<Primitive> {
// TODO: GPU wavefront path — upload shapes into device-visible arena, // TODO: GPU wavefront path — upload shapes into device-visible arena,
// build SOA primitive arrays for kernel dispatch // build SOA primitive arrays for kernel dispatch
@ -1286,7 +1242,7 @@ impl BasicScene {
entities: &[AnimatedShapeSceneEntity], entities: &[AnimatedShapeSceneEntity],
loaded: Vec<Ptr<Shape>>, loaded: Vec<Ptr<Shape>>,
textures: &NamedTextures, textures: &NamedTextures,
named_materials: &HashMap<String, MaterialIdx>, named_materials: &HashMap<String, Material>,
materials: &[Material], materials: &[Material],
media: &HashMap<String, Arc<Medium>>, media: &HashMap<String, Arc<Medium>>,
) -> Vec<Primitive> { ) -> Vec<Primitive> {

View file

@ -1,4 +1,4 @@
use super::{LightSceneEntity, MediumSceneEntity, SceneEntity, TextureSceneEntity}; use super::{LightSceneEntity, SceneEntity, TextureSceneEntity};
use crate::core::image::HostImage; use crate::core::image::HostImage;
use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::utils::parallel::AsyncJob; use crate::utils::parallel::AsyncJob;
@ -34,8 +34,6 @@ pub struct LightState {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct MediaState { pub struct MediaState {
/// Media declared by `MakeNamedMedium`, not yet instantiated.
pub entities: Vec<(String, MediumSceneEntity)>,
pub jobs: HashMap<String, AsyncJob<Medium>>, pub jobs: HashMap<String, AsyncJob<Medium>>,
pub map: HashMap<String, Arc<Medium>>, pub map: HashMap<String, Arc<Medium>>,
} }

View file

@ -73,7 +73,7 @@ impl IntegratorBase {
use_mis: bool, use_mis: bool,
) { ) {
for &idx in &self.infinite_lights { for &idx in &self.infinite_lights {
let light = idx.get(&self.lights); let light = &self.lights[idx.0 as usize];
let le = light.le(ray, lambda); let le = light.le(ray, lambda);
if le.is_black() { if le.is_black() {
continue; continue;

View file

@ -72,6 +72,8 @@ pub struct PathIntegrator {
materials: Vec<Material>, materials: Vec<Material>,
} }
unsafe impl Send for PathIntegrator {}
unsafe impl Sync for PathIntegrator {}
impl PathIntegrator { impl PathIntegrator {
pub fn new( pub fn new(
@ -106,7 +108,7 @@ impl PathIntegrator {
return SampledSpectrum::zero(); return SampledSpectrum::zero();
}; };
let light = sampled.light.get(&self.base.lights); let light = &self.base.lights[sampled.light.0 as usize];
let Some(ls) = light.sample_li(&ctx, sampler.get2d(), lambda, true) else { let Some(ls) = light.sample_li(&ctx, sampler.get2d(), lambda, true) else {
return SampledSpectrum::zero(); return SampledSpectrum::zero();
@ -238,7 +240,7 @@ impl RayIntegratorTrait for PathIntegrator {
state.l += state.beta * le; state.l += state.beta * le;
} else if self.config.use_mis && !isect.area_light.is_none() { } else if self.config.use_mis && !isect.area_light.is_none() {
let idx = isect.area_light; let idx = isect.area_light;
let light = idx.get(&self.base.lights); let light = &self.base.lights[idx.0 as usize];
let p_l = self.sampler.pmf_with_context(&state.prev_ctx, idx) let p_l = self.sampler.pmf_with_context(&state.prev_ctx, idx)
* light.pdf_li(&state.prev_ctx, ray.d, true); * light.pdf_li(&state.prev_ctx, ray.d, true);
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l); let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);

View file

@ -1,23 +1,19 @@
use crate::Arena; use crate::Arena;
use shared::Float;
use shared::core::LightIdx;
use shared::core::geometry::Bounds3f;
use shared::core::light::LightBounds;
use shared::core::light::{Light, LightTrait}; use shared::core::light::{Light, LightTrait};
use shared::lights::sampler::{ use shared::lights::sampler::{LightSampler, PowerLightSampler, UniformLightSampler};
BVHLightSampler, CompactLightBounds, LightBVHNode, LightSampler, NO_BIT_TRAIL,
PowerLightSampler, UniformLightSampler,
};
use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::utils::Ptr;
use shared::utils::partition_slice;
use shared::utils::sampling::AliasTable; use shared::utils::sampling::AliasTable;
use shared::utils::Ptr;
use shared::Float;
pub fn create_light_sampler(name: &str, lights: &[Light], arena: &Arena) -> LightSampler { pub fn create_light_sampler(name: &str, lights: &[Light], arena: &Arena) -> LightSampler {
match name { match name {
"uniform" => LightSampler::Uniform(create_uniform(lights.len() as u32)), "uniform" => LightSampler::Uniform(create_uniform(lights.len() as u32)),
"power" => LightSampler::Power(create_power(lights, arena)), "power" => LightSampler::Power(create_power(lights, arena)),
"bvh" => LightSampler::BVH(create_bvh(lights, arena)), "bvh" => {
log::warn!("BVH light sampler not yet implemented, falling back to power");
LightSampler::Power(create_power(lights, arena))
}
_ => { _ => {
log::error!("Unknown light sampler \"{}\", using power", name); log::error!("Unknown light sampler \"{}\", using power", name);
LightSampler::Power(create_power(lights, arena)) LightSampler::Power(create_power(lights, arena))
@ -32,6 +28,7 @@ fn create_uniform(lights_len: u32) -> UniformLightSampler {
fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler { fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler {
if lights.is_empty() { if lights.is_empty() {
return PowerLightSampler { return PowerLightSampler {
lights_len: 0,
alias_table: Ptr::null(), alias_table: Ptr::null(),
}; };
} }
@ -54,142 +51,7 @@ fn create_power(lights: &[Light], arena: &Arena) -> PowerLightSampler {
let alias_ptr = arena.alloc(alias_table); let alias_ptr = arena.alloc(alias_table);
PowerLightSampler { PowerLightSampler {
lights_len: lights.len() as u32,
alias_table: alias_ptr, alias_table: alias_ptr,
} }
} }
// Straight up port of original +C+ BVHLightSampler::BVHLightSampler and ::buildBVH (lightsamplers.cpp).
const N_BUCKETS: usize = 12;
/// Accumulates the flat node array and per-light bit trails during the build.
struct BVHBuilder {
nodes: Vec<LightBVHNode>,
bit_trails: Vec<u64>,
all_light_bounds: Bounds3f,
}
impl BVHBuilder {
/// Builds the subtree over `lights`, pairs of `(global light index, bounds)`,
/// reordered in place, returning its node index and combined bounds.
fn build(
&mut self,
lights: &mut [(usize, LightBounds)],
bit_trail: u64,
depth: u32,
) -> (usize, LightBounds) {
if lights.len() == 1 {
let (light_index, lb) = lights[0];
let cb = CompactLightBounds::new(&lb, &self.all_light_bounds);
self.bit_trails[light_index] = bit_trail;
self.nodes
.push(LightBVHNode::make_leaf(light_index as u32, cb));
return (self.nodes.len() - 1, lb);
}
let mid = self.split(lights);
let node_index = self.nodes.len();
self.nodes.push(LightBVHNode::default());
debug_assert!(depth < 64);
// Traversal assumes child0 sits at node_index + 1, so only child1 is stored.
let (child0, b0) = self.build(&mut lights[..mid], bit_trail, depth + 1);
debug_assert_eq!(child0, node_index + 1);
let (child1, b1) = self.build(&mut lights[mid..], bit_trail | (1 << depth), depth + 1);
let lb = LightBounds::union(&b0, &b1);
let cb = CompactLightBounds::new(&lb, &self.all_light_bounds);
self.nodes[node_index] = LightBVHNode::make_interior(child1 as u32, cb);
(node_index, lb)
}
/// Reorders `lights` at the SAH split point and returns where to cut.
fn split(&self, lights: &mut [(usize, LightBounds)]) -> usize {
let mut bounds = Bounds3f::default();
let mut centroid_bounds = Bounds3f::default();
for (_, lb) in lights.iter() {
bounds = bounds.union(lb.bounds);
centroid_bounds = centroid_bounds.union_point(lb.centroid());
}
let mut best: Option<(Float, usize, usize)> = None; // (cost, dim, bucket)
for dim in 0..3 {
if centroid_bounds.p_max[dim] == centroid_bounds.p_min[dim] {
continue;
}
let mut buckets = [LightBounds::default(); N_BUCKETS];
for (_, lb) in lights.iter() {
let b = centroid_bounds.sah_bucket(&lb.centroid(), dim, N_BUCKETS);
buckets[b] = LightBounds::union(&buckets[b], lb);
}
let union = |bs: &[LightBounds]| {
bs.iter()
.fold(LightBounds::default(), |a, b| LightBounds::union(&a, b))
};
// pbrt only considers splits after buckets 1..N_BUCKETS-1.
for b in 1..N_BUCKETS - 1 {
let cost = BVHLightSampler::evaluate_cost(&union(&buckets[..=b]), &bounds, dim)
+ BVHLightSampler::evaluate_cost(&union(&buckets[b + 1..]), &bounds, dim);
if cost > 0. && best.map_or(true, |(c, ..)| cost < c) {
best = Some((cost, dim, b));
}
}
}
let Some((_, dim, bucket)) = best else {
return lights.len() / 2;
};
let mid = partition_slice(lights, |(_, lb)| {
centroid_bounds.sah_bucket(&lb.centroid(), dim, N_BUCKETS) <= bucket
});
if mid == 0 || mid == lights.len() {
lights.len() / 2
} else {
mid
}
}
}
fn create_bvh(lights: &[Light], arena: &Arena) -> BVHLightSampler {
// Infinite lights are sampled uniformly; bounded lights that emit go in the BVH.
let mut infinite_lights = Vec::new();
let mut bvh_lights = Vec::new();
let mut all_light_bounds = Bounds3f::default();
for (i, light) in lights.iter().enumerate() {
match light.bounds() {
None => infinite_lights.push(LightIdx(i as u32)),
Some(lb) if lb.phi > 0. => {
all_light_bounds = all_light_bounds.union(lb.bounds);
bvh_lights.push((i, lb));
}
Some(_) => {}
}
}
let mut builder = BVHBuilder {
nodes: Vec::new(),
// Lights that never become a leaf keep the sentinel.
bit_trails: vec![NO_BIT_TRAIL; lights.len()],
all_light_bounds,
};
if !bvh_lights.is_empty() {
builder.build(&mut bvh_lights, 0, 0);
}
let (nodes, nodes_len) = arena.alloc_slice(&builder.nodes);
let (bit_trails, _) = arena.alloc_slice(&builder.bit_trails);
let (infinite, infinite_len) = arena.alloc_slice(&infinite_lights);
BVHLightSampler {
nodes,
infinite_lights: infinite,
bit_trails,
nodes_len: nodes_len as u32,
lights_len: lights.len() as u32,
infinite_lights_len: infinite_len as u32,
all_light_bounds,
}
}

View file

@ -281,10 +281,7 @@ impl ParameterDictionary {
params: &[ParsedParameter], params: &[ParsedParameter],
color_space: Option<Arc<RGBColorSpace>>, color_space: Option<Arc<RGBColorSpace>>,
) -> Result<Self> { ) -> Result<Self> {
// paramdict.cpp:150 — the owned params are `p0`; the inherited attribute let n_owned_params = params.len();
// vector is appended (reversed) after `p0` has itself been reversed.
let n_owned_params = p0.len();
p0.reverse();
p0.extend(params.iter().rev().cloned()); p0.extend(params.iter().rev().cloned());
let dict = Self { let dict = Self {

View file

@ -101,7 +101,7 @@ impl WavefrontAggregate for CpuAggregate {
} }
// Material eval queue dispatch // Material eval queue dispatch
let material = intr.material.get(&self.materials); let material = &self.materials[intr.material.0 as usize];
let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) { let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) {
basic_eval_mtl_q basic_eval_mtl_q
} else { } else {

View file

@ -364,7 +364,7 @@ impl CpuWavefrontRenderer {
let mut l_contrib = SampledSpectrum::new(0.0); let mut l_contrib = SampledSpectrum::new(0.0);
for idx in infinite_lights { for idx in infinite_lights {
let light = idx.get(&self.lights); let light = &self.lights[idx.0 as usize];
let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null()); let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null());
let le = light.le(&ray, &w.lambda); let le = light.le(&ray, &w.lambda);
if le.is_black() { if le.is_black() {
@ -402,7 +402,7 @@ impl CpuWavefrontRenderer {
if w.area_light.is_none() { if w.area_light.is_none() {
return; return;
} }
let light = w.area_light.get(&self.lights); let light = &self.lights[w.area_light.0 as usize];
let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda); let le = light.l(w.p, w.n, w.uv, w.wo, &w.lambda);
if le.is_black() { if le.is_black() {
@ -475,7 +475,7 @@ impl CpuWavefrontRenderer {
if w.material.is_none() { if w.material.is_none() {
return; return;
} }
let material = w.material.get(&self.materials); let material = &self.materials[w.material.0 as usize];
let pi = w.pixel_index as usize; let pi = w.pixel_index as usize;
let rs = pixel_sample_state.samples.get(pi); let rs = pixel_sample_state.samples.get(pi);
@ -604,7 +604,7 @@ impl CpuWavefrontRenderer {
DIAG_SAMPLE_LIGHT_NONE.fetch_add(1, Ordering::Relaxed); DIAG_SAMPLE_LIGHT_NONE.fetch_add(1, Ordering::Relaxed);
return; return;
}; };
let light = sampled_light.light.get(&self.lights); let light = &self.lights[sampled_light.light.0 as usize];
let Some(ls) = light.sample_li(&light_ctx, rs.direct.u, &lambda, true) else { let Some(ls) = light.sample_li(&light_ctx, rs.direct.u, &lambda, true) else {
DIAG_SAMPLE_LI_NONE.fetch_add(1, Ordering::Relaxed); DIAG_SAMPLE_LI_NONE.fetch_add(1, Ordering::Relaxed);