From 82255e50462563bb13c9019e9969c58d5810f08b Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Wed, 20 May 2026 20:52:34 +0100 Subject: [PATCH] ALmost done with changes --- shared/src/core/primitive.rs | 14 +- src/core/aggregates.rs | 10 +- src/core/camera.rs | 2 +- src/core/film.rs | 4 +- src/core/filter.rs | 4 +- src/core/image/mod.rs | 4 +- src/core/image/ops.rs | 319 +++++++++-------------------------- src/core/scene/scene.rs | 20 +-- src/films/rgb.rs | 10 +- src/lights/diffuse.rs | 2 +- src/lights/goniometric.rs | 60 ++++--- src/lights/infinite.rs | 1 - src/lights/projection.rs | 12 +- src/lights/spot.rs | 8 +- src/shapes/bilinear.rs | 6 +- src/shapes/mesh.rs | 2 +- src/spectra/data.rs | 8 +- src/spectra/mod.rs | 3 +- 18 files changed, 168 insertions(+), 321 deletions(-) diff --git a/shared/src/core/primitive.rs b/shared/src/core/primitive.rs index e758957..d61bdc9 100644 --- a/shared/src/core/primitive.rs +++ b/shared/src/core/primitive.rs @@ -1,5 +1,5 @@ -use crate::core::geometry::{Bounds3f, Ray}; use crate::core::aggregates::BVHAggregate; +use crate::core::geometry::{Bounds3f, Ray}; use crate::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction}; use crate::core::light::Light; use crate::core::material::Material; @@ -104,7 +104,12 @@ impl PrimitiveTrait for SimplePrimitive { fn intersect(&self, r: &Ray, t_max: Option) -> Option { let mut si = self.shape.intersect(r, t_max)?; - si.set_intersection_properties(self.material, Ptr::null(), MediumInterface::default(), r.medium); + si.set_intersection_properties( + self.material, + Ptr::null(), + MediumInterface::default(), + r.medium, + ); Some(si) } @@ -183,7 +188,7 @@ impl PrimitiveTrait for AnimatedPrimitive { } #[repr(C)] -#[derive(Debug, Clone, Copy)] +#[derive(Default, Debug, Clone, Copy)] pub struct LinearBVHNode { bounds: Bounds3f, } @@ -217,7 +222,6 @@ pub enum Primitive { KdTree(KdTreeAggregate), } - impl PrimitiveTrait for Ptr { fn bounds(&self) -> Bounds3f { unsafe { self.as_ref().bounds() } @@ -231,5 +235,3 @@ impl PrimitiveTrait for Ptr { unsafe { self.as_ref().intersect_p(r, t_max) } } } - - diff --git a/src/core/aggregates.rs b/src/core/aggregates.rs index 6ddf4ac..c9dc661 100644 --- a/src/core/aggregates.rs +++ b/src/core/aggregates.rs @@ -1,3 +1,4 @@ +use crate::Arena; use rayon::prelude::*; use shared::core::aggregates::{BVHAggregate as DeviceBVHAggregate, LinearBVHNode}; use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f}; @@ -5,8 +6,7 @@ use shared::core::primitive::{Primitive, PrimitiveTrait}; use shared::core::shape::ShapeIntersection; use shared::utils::math::encode_morton_3; use shared::utils::{find_interval, partition_slice}; -use crate::Arena; -use shared::Float; +use shared::{gvec_from_slice, Float}; use std::cmp::Ordering; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; @@ -26,7 +26,6 @@ struct BVHSplitBucket { pub bounds: Bounds3f, } - #[derive(Debug, Clone, Copy, Default)] struct MortonPrimitive { primitive_index: usize, @@ -890,7 +889,8 @@ impl BVHAggregate

{ impl BVHAggregate { pub fn to_device(&self, arena: &mut Arena) -> DeviceBVHAggregate { - let shared_nodes: Vec = self.nodes + let shared_nodes: Vec = self + .nodes .iter() .map(|n| shared::core::aggregates::LinearBVHNode { bounds: n.bounds, @@ -905,7 +905,7 @@ impl BVHAggregate { max_prims_in_node: self.max_prims_in_node as u32, primitives: gvec_from_slice(&self.primitives), primitive_count: self.primitives.len() as u32, - nodes: gvec_from_slice(&self.shared_nodes), + nodes: gvec_from_slice(&shared_nodes), node_count: self.nodes.len() as u32, } } diff --git a/src/core/camera.rs b/src/core/camera.rs index 8d33e9f..5de799e 100644 --- a/src/core/camera.rs +++ b/src/core/camera.rs @@ -386,7 +386,7 @@ impl CameraFactory for Camera { ); - arena.alloc(camera); + arena.alloc(camera.clone()); Ok(Camera::Realistic(camera)) } "spherical" => { diff --git a/src/core/film.rs b/src/core/film.rs index e468541..bc57298 100644 --- a/src/core/film.rs +++ b/src/core/film.rs @@ -15,7 +15,7 @@ use shared::spectra::{ cie::SWATCHES_RAW, DenselySampledSpectrum, PiecewiseLinearSpectrum, RGBColorSpace, }; use shared::utils::math::{linear_least_squares, SquareMatrix}; -use shared::{Float, Ptr}; +use shared::{Float, Ptr, leak}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, LazyLock}; @@ -27,7 +27,7 @@ const SWATCH_REFLECTANCES: LazyLock<[Spectrum; N_SWATCH_REFLECTANCES]> = LazyLoc std::array::from_fn(|i| { let raw_data = SWATCHES_RAW[i]; let pls = PiecewiseLinearSpectrum::from_interleaved(raw_data, false); - Spectrum::Piecewise(pls) + Spectrum::Piecewise(leak(pls)) }) }); diff --git a/src/core/filter.rs b/src/core/filter.rs index c5a471c..a0f3f93 100644 --- a/src/core/filter.rs +++ b/src/core/filter.rs @@ -1,10 +1,10 @@ -use crate::utils::sampling::PiecewiseConstant2D; -use crate::utils::{FileLoc, ParameterDictionary}; use crate::Arena; +use crate::{FileLoc, ParameterDictionary}; use anyhow::{bail, Result}; use shared::core::filter::Filter; use shared::core::geometry::{Bounds2f, Point2f, Vector2f}; use shared::filters::*; +use shared::utils::sampling::PiecewiseConstant2D; use shared::{Array2D, Float}; pub trait FilterFactory { diff --git a/src/core/image/mod.rs b/src/core/image/mod.rs index 457c986..21626de 100644 --- a/src/core/image/mod.rs +++ b/src/core/image/mod.rs @@ -256,8 +256,8 @@ impl HostImage { } } - pub fn set_channel(&mut self, p: Point2i, values: &ImageChannelValues) { - self.inner.set_channel(p, values[i]); + pub fn set_channel(&mut self, p: Point2i, c: i32, val: Float) { + self.inner.set_channel(p, c, val); } pub fn select_channels(&self, desc: &ImageChannelDesc) -> Self { diff --git a/src/core/image/ops.rs b/src/core/image/ops.rs index d24ea3a..07c7a73 100644 --- a/src/core/image/ops.rs +++ b/src/core/image/ops.rs @@ -1,6 +1,4 @@ use super::HostImage; -use crate::core::image::pixel::PixelStorageTrait; -use crate::core::image::PixelStorage; use rayon::prelude::*; use shared::core::color::ColorEncoding; use shared::core::geometry::{Bounds2i, Point2i}; @@ -18,98 +16,84 @@ pub struct ResampleWeight { impl HostImage { pub fn flip_y(&mut self) { let res = self.inner.resolution; - let nc = self.inner.n_channels as usize; + let nc = self.inner.n_channels; - match self.inner.format { - PixelFormat::U8 => flip_y_kernel(self.inner.pixels.as_u8_mut(), res, nc), - PixelFormat::F16 => flip_y_kernel(self.inner.pixels.as_f16_mut(), res, nc), - PixelFormat::F32 => flip_y_kernel(self.inner.pixels.as_f32_slice_mut(), res, nc), + for y in 0..res.y() / 2 { + let y2 = res.y() - 1 - y; + for x in 0..res.x() { + for c in 0..nc { + let a = self.inner.get_channel(Point2i::new(x, y), c); + let b = self.inner.get_channel(Point2i::new(x, y2), c); + self.inner.set_channel(Point2i::new(x, y), c, b); + self.inner.set_channel(Point2i::new(x, y2), c, a); + } + } } } pub fn crop(&self, bounds: Bounds2i) -> HostImage { - let n_channels = self.inner.n_channels as usize; + let nc = self.inner.n_channels; let new_res = Point2i::new( bounds.p_max.x() - bounds.p_min.x(), bounds.p_max.y() - bounds.p_min.y(), ); - let new_names = self.channel_names.clone(); let mut new_image = - HostImage::new(self.inner.format, new_res, &new_names, self.inner.encoding); + HostImage::new(self.inner.format, new_res, &self.channel_names, self.inner.encoding); - match self.inner.format { - PixelFormat::U8 => crop_kernel( - self.inner.pixels.as_u8(), - new_image.inner.pixels.as_u8_mut(), - self.inner.resolution, - bounds, - n_channels, - ), - PixelFormat::F16 => crop_kernel( - self.inner.pixels.as_f16(), - new_image.inner.pixels.as_f16_mut(), - self.inner.resolution, - bounds, - n_channels, - ), - PixelFormat::F32 => crop_kernel( - self.inner.pixels.as_f32_slice(), - new_image.inner.pixels.as_f32_slice_mut(), - self.inner.resolution, - bounds, - n_channels, - ), + for y in 0..new_res.y() { + for x in 0..new_res.x() { + let src = Point2i::new(bounds.p_min.x() + x, bounds.p_min.y() + y); + let dst = Point2i::new(x, y); + for c in 0..nc { + let val = self.inner.get_channel(src, c); + new_image.inner.set_channel(dst, c, val); + } + } } new_image } pub fn copy_rect_out(&self, extent: Bounds2i, buf: &mut [Float], wrap: WrapMode2D) { - match self.inner.format { - PixelFormat::U8 => { - copy_rect_out_kernel(self.inner.pixels.as_u8(), self, extent, buf, wrap) - } - PixelFormat::F16 => { - copy_rect_out_kernel(self.inner.pixels.as_f16(), self, extent, buf, wrap) - } - PixelFormat::F32 => { - copy_rect_out_kernel(self.inner.pixels.as_f32_slice(), self, extent, buf, wrap) - } - } + let w = (extent.p_max.x() - extent.p_min.x()) as usize; + let channels = self.inner.n_channels as usize; + + buf.par_chunks_mut(w * channels) + .enumerate() + .for_each(|(y_rel, row_buf)| { + let y = extent.p_min.y() + y_rel as i32; + for x_rel in 0..w { + let x = extent.p_min.x() + x_rel as i32; + let p = Point2i::new(x, y); + for c in 0..channels { + row_buf[x_rel * channels + c] = + self.inner.get_channel_with_wrap(p, c as i32, wrap); + } + } + }); } pub fn copy_rect_in(&mut self, extent: Bounds2i, buf: &[Float]) { - let res = self.inner.resolution; - let n_channels = self.inner.n_channels as usize; - let encoding = self.inner.encoding; - let format = self.inner.format; + let w = (extent.p_max.x() - extent.p_min.x()) as usize; + let channels = self.inner.n_channels as usize; - match format { - PixelFormat::U8 => copy_rect_in_kernel( - self.inner.pixels.as_u8_mut(), - res, - n_channels, - encoding, - extent, - buf, - ), - PixelFormat::F16 => copy_rect_in_kernel( - self.inner.pixels.as_f16_mut(), - res, - n_channels, - encoding, - extent, - buf, - ), - PixelFormat::F32 => copy_rect_in_kernel( - self.inner.pixels.as_f32_slice_mut(), - res, - n_channels, - encoding, - extent, - buf, - ), + for (y_rel, row) in buf.chunks(w * channels).enumerate() { + let y = extent.p_min.y() + y_rel as i32; + if y < 0 || y >= self.inner.resolution.y() { + continue; + } + + for x_rel in 0..w { + let x = extent.p_min.x() + x_rel as i32; + if x < 0 || x >= self.inner.resolution.x() { + continue; + } + for c in 0..channels { + let val = row[x_rel * channels + c]; + self.inner.set_channel(Point2i::new(x, y), c as i32, val); + } + } } } @@ -180,6 +164,7 @@ impl HostImage { } let new_res = Point2i::new((old.x() / 2).max(1), (old.y() / 2).max(1)); + let nc = prev.n_channels(); let mut next = HostImage::new( prev.inner.format, new_res, @@ -187,177 +172,37 @@ impl HostImage { prev.inner.encoding, ); - match next.inner.format { - PixelFormat::U8 => downsample_kernel( - next.inner.pixels.as_u8_mut(), - new_res, - &prev, - internal_wrap, - ), - PixelFormat::F16 => downsample_kernel( - next.inner.pixels.as_f16_mut(), - new_res, - &prev, - internal_wrap, - ), - PixelFormat::F32 => downsample_kernel( - next.inner.pixels.as_f32_slice_mut(), - new_res, - &prev, - internal_wrap, - ), + for y in 0..new_res.y() { + for x in 0..new_res.x() { + let src_x = x * 2; + let src_y = y * 2; + for c in 0..nc { + let mut sum = 0.0; + let mut count = 0.0; + for dy in 0..2i32 { + for dx in 0..2i32 { + let sx = src_x + dx; + let sy = src_y + dy; + if sx < old.x() && sy < old.y() { + sum += prev.inner.get_channel_with_wrap( + Point2i::new(sx, sy), c, internal_wrap, + ); + count += 1.0; + } + } + } + let avg = if count > 0.0 { sum / count } else { 0.0 }; + next.inner.set_channel(Point2i::new(x, y), c, avg); + } + } } + levels.push(next); } levels } } -fn flip_y_kernel(pixels: &mut [T], res: Point2i, channels: usize) { - let w = res.x() as usize; - let h = res.y() as usize; - let stride = w * channels; - - for y in 0..(h / 2) { - let bot = h - 1 - y; - for i in 0..stride { - pixels.swap(y * stride + i, bot * stride + i); - } - } -} - -fn crop_kernel( - src: &[T], - dst: &mut [T], - src_res: Point2i, - bounds: Bounds2i, - channels: usize, -) { - let dst_w = (bounds.p_max.x() - bounds.p_min.x()) as usize; - // let dst_h = (bounds.p_max.y() - bounds.p_min.y()) as usize; - - dst.par_chunks_mut(dst_w * channels) - .enumerate() - .for_each(|(dy, dst_row)| { - let sy = bounds.p_min.y() as usize + dy; - let sx_start = bounds.p_min.x() as usize; - - let src_offset = (sy * src_res.x() as usize + sx_start) * channels; - let count = dst_w * channels; - - dst_row.copy_from_slice(&src[src_offset..src_offset + count]); - }); -} - -fn copy_rect_out_kernel( - src: &[T], - image: &HostImage, - extent: Bounds2i, - buf: &mut [Float], - wrap: WrapMode2D, -) { - let w = (extent.p_max.x() - extent.p_min.x()) as usize; - let channels = image.n_channels() as usize; - let enc = image.encoding(); - let res = image.resolution(); - - buf.par_chunks_mut(w * channels) - .enumerate() - .for_each(|(y_rel, row_buf)| { - let y = extent.p_min.y() + y_rel as i32; - for x_rel in 0..w { - let x = extent.p_min.x() + x_rel as i32; - - if x >= 0 && x < res.x() && y >= 0 && y < res.y() { - let offset = (y as usize * res.x() as usize + x as usize) * channels; - - for c in 0..channels { - row_buf[x_rel * channels + c] = T::to_linear(src[offset + c], enc); - } - } else { - // We fall back to get_channel which handles the wrapping math. - let p = Point2i::new(x, y); - for c in 0..channels { - row_buf[x_rel * channels + c] = - image.get_channel_with_wrap(p, c.try_into().unwrap(), wrap); - } - } - } - }); -} - -fn copy_rect_in_kernel( - dst: &mut [T], - res: Point2i, - channels: usize, - enc: ColorEncoding, - extent: Bounds2i, - buf: &[Float], -) { - let w = (extent.p_max.x() - extent.p_min.x()) as usize; - let res_x = res.x() as usize; - - let rows = buf.chunks(w * channels); - for (y_rel, row) in rows.enumerate() { - let y = extent.p_min.y() + y_rel as i32; - if y < 0 || y >= res.y() { - continue; - } - - let dst_row_start = (y as usize * res_x) * channels; - - for (x_rel, &val) in row.iter().enumerate() { - let c = x_rel % channels; - let x_pixel = x_rel / channels; - let x = extent.p_min.x() + x_pixel as i32; - - if x >= 0 && x < res.x() { - let idx = dst_row_start + (x as usize * channels) + c; - dst[idx] = T::from_linear(val, enc); - } - } - } -} - -fn downsample_kernel( - dst: &mut [T], - dst_res: Point2i, - prev: &HostImage, - wrap: WrapMode2D, -) { - let w = dst_res.x() as usize; - let channels = prev.n_channels(); - let enc = prev.encoding(); - let old_res = prev.resolution(); - - dst.par_chunks_mut(w * channels as usize) - .enumerate() - .for_each(|(y, row)| { - let src_y = y * 2; - for x in 0..w { - let src_x = x * 2; - for c in 0..channels { - let mut sum = 0.0; - let mut count = 0.0; - - for dy in 0..2 { - for dx in 0..2 { - let sx = src_x as i32 + dx; - let sy = src_y as i32 + dy; - if sx < old_res.x() && sy < old_res.y() { - sum += prev.get_channel_with_wrap(Point2i::new(sx, sy), c, wrap); - count += 1.0; - } - } - } - - let avg = if count > 0.0 { sum / count } else { 0.0 }; - row[x * channels as usize + c as usize] = T::from_linear(avg, enc); - } - } - }); -} - fn resample_weights(old_res: usize, new_res: usize) -> Vec { let filter_radius = 2.0; let tau = 2.0; @@ -416,7 +261,6 @@ fn compute_resize_tile( let mut x_buf = vec![0.0; n_channels * ny_in * nx_out]; - // Resize X for y in 0..ny_in { for x in 0..nx_out { let x_global = out_extent.p_min.x() + x as i32; @@ -438,7 +282,6 @@ fn compute_resize_tile( let mut out_buf = vec![0.0; n_channels * nx_out * ny_out]; - // Resize Y for x in 0..nx_out { for y in 0..ny_out { let y_global = out_extent.p_min.y() + y as i32; diff --git a/src/core/scene/scene.rs b/src/core/scene/scene.rs index ae7cdad..9d81778 100644 --- a/src/core/scene/scene.rs +++ b/src/core/scene/scene.rs @@ -36,7 +36,7 @@ pub struct SceneLookup<'a> { pub media: &'a HashMap>, pub named_materials: &'a HashMap, pub materials: &'a Vec, - pub shape_lights: &'a HashMap>, + pub shape_lights: &'a HashMap>>, } impl<'a> SceneLookup<'a> { @@ -154,7 +154,6 @@ impl BasicScene { job: None, }; - let arena_sampler = Arc::clone(&arena); let sampler_film = Arc::clone(&film_instance); let sampler_job = run_async(move || { let res = sampler_film.as_ref().base().full_resolution; @@ -163,7 +162,7 @@ impl BasicScene { &sampler.parameters, res, &sampler.loc, - &arena_sampler, + &arena, ) .map_err(|e| anyhow!("Failed to create sampler: {}", e)) }); @@ -556,9 +555,9 @@ impl BasicScene { shape_entities: &[ShapeSceneEntity], textures: &NamedTextures, arena: &Arena, - ) -> HashMap> { + ) -> HashMap>> { let light_state = self.light_state.lock(); - let mut shape_lights: HashMap> = HashMap::new(); + let mut shape_lights: HashMap>> = HashMap::new(); for (i, entity) in shape_entities.iter().enumerate() { let light_idx = match entity.light_index { @@ -592,7 +591,7 @@ impl BasicScene { let render_from_light = *entity.render_from_object; - let lights: Vec = shapes + let lights: Vec> = shapes .iter() .filter_map(|shape| { match crate::core::light::create_area_light( @@ -676,7 +675,7 @@ impl BasicScene { mtl: Material, alpha_tex: &Option>, mi: MediumInterface, - al_params: Option<&AreaLightEntity>, + al_params: Option<&SceneEntity>, render_from_light: Transform, film_cs: Option<&RGBColorSpace>, arena: &mut Arena, @@ -938,16 +937,15 @@ impl BasicScene { .get(&shape_ctx.entity_index) .and_then(|lights| lights.get(shape_ctx.shape_index)); - let light_ptr = arena.alloc_opt(shape_lights_opt); + let light_ptr = shape_lights_opt.copied().unwrap_or(Ptr::null()); let shape_ptr = shape_ctx.shape; - let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() - { + let prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() { Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl))) } else { Primitive::Geometric(GeometricPrimitive::new( shape_ptr, arena.alloc(mtl), - area_light, + light_ptr, mi.clone(), arena.upload(alpha_tex), )) diff --git a/src/films/rgb.rs b/src/films/rgb.rs index cb41b95..6409d48 100644 --- a/src/films/rgb.rs +++ b/src/films/rgb.rs @@ -1,9 +1,9 @@ use super::*; -use crate::core::film::{CreateFilmBase, PixelSensor}; +use crate::core::film::{CreateFilmBase, CreatePixelSensor}; use crate::Arena; use anyhow::Result; use shared::core::camera::CameraTransform; -use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel}; +use shared::core::film::{Film, FilmBase, RGBFilm, RGBPixel, PixelSensor}; use shared::spectra::RGBColorSpace; impl CreateFilm for RGBFilm { @@ -13,13 +13,13 @@ impl CreateFilm for RGBFilm { filter: Filter, _camera_transform: Option, loc: &FileLoc, - _arena: &Arena, + arena: &Arena, ) -> Result { let colorspace = params.color_space.as_ref().cloned().unwrap_or_else(crate::spectra::default_colorspace_arc); 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 sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc, arena)?; + let film_base = FilmBase::create(params, filter, Some(&sensor), loc)?; let film = RGBFilm::new(film_base, &colorspace, max_component_value, write_fp16); Ok(Film::RGB(film)) } diff --git a/src/lights/diffuse.rs b/src/lights/diffuse.rs index ad863c4..30dc47b 100644 --- a/src/lights/diffuse.rs +++ b/src/lights/diffuse.rs @@ -11,7 +11,7 @@ use shared::core::light::{Light, LightBase, LightType}; use shared::core::medium::{Medium, MediumInterface}; use shared::core::shape::{Shape, ShapeTrait}; use shared::core::spectrum::Spectrum; -use shared::core::texture::{SpectrumType, TextureEvalContext}; +use shared::core::texture::{SpectrumType, TextureEvalContext, GPUFloatTexture}; use shared::lights::DiffuseAreaLight; use shared::spectra::RGBColorSpace; use shared::utils::Transform; diff --git a/src/lights/goniometric.rs b/src/lights/goniometric.rs index 18b144b..f15aadf 100644 --- a/src/lights/goniometric.rs +++ b/src/lights/goniometric.rs @@ -1,13 +1,10 @@ -use std::path::Path; - use crate::core::image::{HostImage, ImageIO}; use crate::core::light::lookup_spectrum; use crate::core::spectrum::spectrum_to_photometric; use crate::core::texture::FloatTexture; -use crate::utils::sampling::PiecewiseConstant2D; use crate::utils::resolve_filename; use crate::{Arena, FileLoc, ParameterDictionary}; -use anyhow::{Result, anyhow}; +use anyhow::{anyhow, Result}; use shared::core::geometry::Point2i; use shared::core::light::{Light, LightBase, LightType}; use shared::core::medium::{Medium, MediumInterface}; @@ -16,8 +13,10 @@ use shared::core::spectrum::Spectrum; use shared::core::texture::SpectrumType; use shared::lights::GoniometricLight; use shared::spectra::RGBColorSpace; +use shared::utils::sampling::PiecewiseConstant2D; use shared::utils::{Ptr, Transform}; use shared::{Float, PI}; +use std::path::Path; pub fn create( render_from_light: Transform, @@ -25,11 +24,10 @@ pub fn create( params: &ParameterDictionary, loc: &FileLoc, _shape: &Shape, - _alpha_text: &FloatTexture, + _alpha_tex: &FloatTexture, colorspace: Option<&RGBColorSpace>, arena: &Arena, ) -> Result { - let default_cs = crate::spectra::default_colorspace(); let cs = colorspace.unwrap_or(&default_cs); let i = params @@ -39,14 +37,13 @@ pub fn create( 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 image: Ptr = if filename.is_empty() { - Ptr::null() - } else { + + let host_image = if !filename.is_empty() { let im = HostImage::read(Path::new(&filename), None) .map_err(|e| anyhow!("could not load image '{}': {}", filename, e))?; - let loaded = im.image; let res = loaded.resolution(); @@ -56,7 +53,6 @@ pub fn create( filename )); } - if res.x() != res.y() { return Err(anyhow!( "image resolution ({}, {}) is non-square; unlikely to be an equal-area map", @@ -65,15 +61,19 @@ pub fn create( )); } - Ptr::from(&convert_to_luminance_image(&loaded, &filename, loc)?) + Some(convert_to_luminance_image(&loaded, &filename, loc)?) + } else { + None }; 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); - scale *= phi_v / k_e; + if let Some(ref img) = host_image { + let k_e = compute_emissive_power(img); + scale *= phi_v / k_e; + } } let swap_yz: [Float; 16] = [ @@ -81,7 +81,7 @@ pub fn create( ]; let t = Transform::from_flat(&swap_yz).expect("Could not create transform for GoniometricLight"); - let _final_render_from_light = render_from_light * t; + let final_render_from_light = render_from_light * t; let mi = match medium { Some(m) => { @@ -93,31 +93,35 @@ pub fn create( } None => MediumInterface::default(), }; - let base = LightBase::new(LightType::DeltaPosition, render_from_light, mi); + let base = LightBase::new(LightType::DeltaPosition, final_render_from_light, mi); let iemit = lookup_spectrum(&i); - let image_ptr = if !image.is_null() { - let distrib = PiecewiseConstant2D::from_image(&image); - let distrib_ptr = arena.alloc(distrib); - let img_ptr = arena.alloc(image); - (img_ptr, distrib_ptr) - } else { - (Ptr::null(), Ptr::null()) + // Build distribution from host image, then upload both to arena + let (image_ptr, distrib_ptr) = match host_image { + Some(img) => { + let distrib = PiecewiseConstant2D::from_image(&img.inner); + (arena.alloc(img.inner), arena.alloc(distrib)) + } + None => (Ptr::null(), Ptr::null()), }; let specific = GoniometricLight { base, - iemit: arena.alloc(*iemit), + iemit: arena.alloc((*iemit).clone()), scale, - image: image_ptr.0, - distrib: image_ptr.1, + image: image_ptr, + distrib: distrib_ptr, }; Ok(Light::Goniometric(specific)) } -fn convert_to_luminance_image(image: &HostImage, filename: &str, loc: &FileLoc) -> Result { +fn convert_to_luminance_image( + image: &HostImage, + filename: &str, + loc: &FileLoc, +) -> Result { let res = image.resolution(); let rgb_desc = image.get_channel_desc(&["R", "G", "B"]); let y_desc = image.get_channel_desc(&["Y"]); diff --git a/src/lights/infinite.rs b/src/lights/infinite.rs index f9dd0a7..1096947 100644 --- a/src/lights/infinite.rs +++ b/src/lights/infinite.rs @@ -3,7 +3,6 @@ use crate::core::light::lookup_spectrum; use crate::core::spectrum::spectrum_to_photometric; use crate::spectra::get_spectra_context; use crate::utils::resolve_filename; -use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D}; use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload, Upload}; use anyhow::{anyhow, Result}; use rayon::prelude::*; diff --git a/src/lights/projection.rs b/src/lights/projection.rs index 8e2f220..d64c73f 100644 --- a/src/lights/projection.rs +++ b/src/lights/projection.rs @@ -1,13 +1,11 @@ use crate::core::image::{HostImage, ImageIO}; use crate::core::spectrum::spectrum_to_photometric; use crate::core::texture::FloatTexture; -use crate::utils::sampling::PiecewiseConstant2D; use crate::utils::resolve_filename; -use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload}; -use anyhow::{Result, anyhow}; -use shared::Float; +use crate::{Arena, ArenaUpload, FileLoc, ParameterDictionary}; +use anyhow::{anyhow, Result}; use shared::core::geometry::{ - Bounds2f, Point2f, Point2i, Point3f, Vector3f, VectorLike, cos_theta, + cos_theta, Bounds2f, Point2f, Point2i, Point3f, Vector3f, VectorLike, }; use shared::core::light::{Light, LightBase, LightType}; use shared::core::medium::{Medium, MediumInterface}; @@ -15,8 +13,10 @@ use shared::core::shape::Shape; use shared::core::spectrum::Spectrum; use shared::lights::ProjectionLight; use shared::spectra::RGBColorSpace; -use shared::utils::Transform; use shared::utils::math::{radians, square}; +use shared::utils::sampling::PiecewiseConstant2D; +use shared::utils::Transform; +use shared::Float; use std::path::Path; pub fn create( diff --git a/src/lights/spot.rs b/src/lights/spot.rs index 1d6ed8f..d13942b 100644 --- a/src/lights/spot.rs +++ b/src/lights/spot.rs @@ -1,7 +1,7 @@ use crate::core::light::lookup_spectrum; use crate::core::spectrum::spectrum_to_photometric; use crate::core::texture::FloatTexture; -use crate::utils::{Arena, FileLoc, ParameterDictionary}; +use crate::{Arena, FileLoc, ParameterDictionary}; use anyhow::Result; use shared::core::geometry::{Frame, Point3f, VectorLike}; use shared::core::light::{Light, LightBase, LightType}; @@ -22,6 +22,7 @@ trait CreateSpotLight { scale: Float, cos_falloff_start: Float, total_width: Float, + arena: &Arena ) -> Self; } @@ -33,6 +34,7 @@ impl CreateSpotLight for SpotLight { scale: Float, cos_falloff_start: Float, total_width: Float, + arena: &Arena ) -> Self { let base = LightBase::new( LightType::DeltaPosition, @@ -41,7 +43,7 @@ impl CreateSpotLight for SpotLight { ); let i = lookup_spectrum(&le); - let iemit = arena.alloc(i); + let iemit = arena.alloc_arc(i); Self { base, iemit, @@ -100,7 +102,7 @@ pub fn create( None => MediumInterface::default(), }; - let specific = SpotLight::new(final_render, mi, i, scale, coneangle, coneangle - conedelta); + let specific = SpotLight::new(final_render, mi, i, scale, coneangle, coneangle - conedelta, arena); arena.alloc(specific); Ok(Light::Spot(specific)) } diff --git a/src/shapes/bilinear.rs b/src/shapes/bilinear.rs index f1a5e12..88f65c6 100644 --- a/src/shapes/bilinear.rs +++ b/src/shapes/bilinear.rs @@ -1,12 +1,12 @@ use crate::core::image::{HostImage, ImageIO}; use crate::core::shape::{CreateShape, ALL_BILINEAR_MESHES}; use crate::core::texture::FloatTexture; -use crate::utils::sampling::PiecewiseConstant2D; use crate::{Arena, FileLoc, ParameterDictionary}; use anyhow::{anyhow, Result}; use log::warn; use shared::core::shape::Shape; use shared::shapes::{BilinearPatchMesh, BilinearPatchShape}; +use shared::utils::sampling::PiecewiseConstant2D; use shared::{Ptr, Transform}; use std::collections::HashMap; use std::path::Path; @@ -95,7 +95,7 @@ impl CreateShape for BilinearPatchShape { let im = HostImage::read(Path::new(&filename), None)?; let mut img = im.image; img.flip_y(); - Some(PiecewiseConstant2D::from_image(&img)) + Some(PiecewiseConstant2D::from_image(&img.inner)) } } else { None @@ -108,7 +108,7 @@ impl CreateShape for BilinearPatchShape { &p, &n, &uv, - image_dist, + image_dist.as_ref(), ); let host_arc = Arc::new(host); diff --git a/src/shapes/mesh.rs b/src/shapes/mesh.rs index 952f6b5..b1c2166 100644 --- a/src/shapes/mesh.rs +++ b/src/shapes/mesh.rs @@ -1,9 +1,9 @@ -use crate::utils::sampling::PiecewiseConstant2D; use anyhow::{bail, Context, Result as AnyResult}; use ply_rs::parser::Parser; use ply_rs::ply::{DefaultElement, Property}; use shared::core::geometry::{Normal3f, Point2f, Point3f, VectorLike}; use shared::shapes::mesh::TriangleMesh; +use shared::utils::sampling::PiecewiseConstant2D; use shared::Transform; use std::fs::File; use std::path::Path; diff --git a/src/spectra/data.rs b/src/spectra/data.rs index 3641095..6ac8454 100644 --- a/src/spectra/data.rs +++ b/src/spectra/data.rs @@ -1,7 +1,7 @@ use shared::core::spectrum::Spectrum; use shared::spectra::cie::*; use shared::spectra::{DenselySampledSpectrum, PiecewiseLinearSpectrum}; -use shared::{gbox, leak, Float, Ptr}; +use shared::{gbox, gvec_from_slice, leak, Float, Ptr}; use std::collections::HashMap; use std::sync::LazyLock; @@ -11,13 +11,11 @@ pub fn create_cie(data: &[Float]) -> DenselySampledSpectrum { 95 => (300.0, 5.0), n => panic!("Unexpected CIE data length: {}", n), }; - let lambdas: Vec = (0..data.len()) .map(|i| start_lambda + i as Float * step) .collect(); - - let buffer = PiecewiseLinearSpectrum::new(lambdas, data.to_vec()); - let spec = Spectrum::Piecewise(Ptr::from(&*buffer)); + let buffer = PiecewiseLinearSpectrum::new(gvec_from_slice(&lambdas), gvec_from_slice(data)); + let spec = Spectrum::Piecewise(leak(buffer)); DenselySampledSpectrum::from_spectrum(&spec) } diff --git a/src/spectra/mod.rs b/src/spectra/mod.rs index 24c030e..e00104d 100644 --- a/src/spectra/mod.rs +++ b/src/spectra/mod.rs @@ -134,7 +134,8 @@ pub fn default_colorspace_arc() -> Arc { pub fn default_colorspace_ref() -> &'static RGBColorSpace { static CS: OnceLock = OnceLock::new(); - CS.get_or_init(|| stdcs.srgb) + let stdcs = get_colorspace_device(); + CS.get_or_init(|| *stdcs.srgb) } pub fn default_illuminant() -> Spectrum {