Continuing work on handling Options. Lesson learned, actually handle them

This commit is contained in:
Wito Wiala 2026-05-13 23:32:58 +01:00
parent c8d083df62
commit f21cb7cf08
27 changed files with 291 additions and 186 deletions

1
.gitignore vendored
View file

@ -13,3 +13,4 @@ tests/
*.json *.json
*.txt *.txt
scenes/ scenes/
compile.sh

View file

@ -67,3 +67,6 @@ excessive_precision = "allow"
approx_constant = "allow" approx_constant = "allow"
upper_case_acronyms = "allow" upper_case_acronyms = "allow"
wrong_self_convention = "allow" wrong_self_convention = "allow"
[profile.release]
debug = true

View file

@ -193,3 +193,4 @@ pub enum Material {
ThinDielectric(ThinDielectricMaterial), ThinDielectric(ThinDielectricMaterial),
Mix(MixMaterial), Mix(MixMaterial),
} }

View file

@ -40,7 +40,7 @@ pub enum Spectrum {
impl Spectrum { impl Spectrum {
pub fn std_illuminant_d65() -> Self { pub fn std_illuminant_d65() -> Self {
todo!() unimplemented!("Use crate::spectra::default_illuminant() on host")
} }
pub fn to_xyz(&self, std: &StandardSpectra) -> XYZ { pub fn to_xyz(&self, std: &StandardSpectra) -> XYZ {

View file

@ -1,13 +1,14 @@
use rayon::prelude::*; use rayon::prelude::*;
use shared::Float;
use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f}; use shared::core::geometry::{Bounds3f, Point3f, Ray, Vector3f};
use shared::core::primitive::PrimitiveTrait; use shared::core::primitive::PrimitiveTrait;
use shared::core::shape::ShapeIntersection; use shared::core::shape::ShapeIntersection;
use shared::utils::math::encode_morton_3; use shared::utils::math::encode_morton_3;
use shared::utils::{find_interval, partition_slice}; use shared::utils::{find_interval, partition_slice};
use shared::Float;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::sync::Arc; use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
use std::sync::Arc;
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -120,17 +121,17 @@ impl BVHBuildNode {
} }
} }
pub struct SharedPrimitiveBuffer<'a> { pub struct SharedPrimitiveBuffer<'a, P> {
ptr: *mut Arc<dyn PrimitiveTrait>, ptr: *mut P,
pub offset: &'a AtomicUsize, pub offset: &'a AtomicUsize,
_marker: std::marker::PhantomData<&'a mut [Arc<dyn PrimitiveTrait>]>, _marker: std::marker::PhantomData<&'a mut [P]>,
} }
unsafe impl<'a> Sync for SharedPrimitiveBuffer<'a> {} unsafe impl<'a, P> Sync for SharedPrimitiveBuffer<'a, P> {}
unsafe impl<'a> Send for SharedPrimitiveBuffer<'a> {} unsafe impl<'a, P> Send for SharedPrimitiveBuffer<'a, P> {}
impl<'a> SharedPrimitiveBuffer<'a> { impl<'a, P> SharedPrimitiveBuffer<'a, P> {
pub fn new(slice: &'a mut [Arc<dyn PrimitiveTrait>], offset: &'a AtomicUsize) -> Self { pub fn new(slice: &'a mut [P], offset: &'a AtomicUsize) -> Self {
Self { Self {
ptr: slice.as_mut_ptr(), ptr: slice.as_mut_ptr(),
offset, offset,
@ -138,11 +139,10 @@ impl<'a> SharedPrimitiveBuffer<'a> {
} }
} }
pub fn append( pub fn append(&self, primitives: &[P], indices: &[BVHPrimitiveInfo]) -> usize
&self, where
primitives: &[Arc<dyn PrimitiveTrait>], P: Clone,
indices: &[BVHPrimitiveInfo], {
) -> usize {
let count = indices.len(); let count = indices.len();
let start_index = self.offset.fetch_add(count, AtomicOrdering::Relaxed); let start_index = self.offset.fetch_add(count, AtomicOrdering::Relaxed);
@ -156,16 +156,16 @@ impl<'a> SharedPrimitiveBuffer<'a> {
} }
} }
pub struct BVHAggregate { pub struct BVHAggregate<P: PrimitiveTrait + Clone + Send + Sync> {
max_prims_in_node: usize, pub max_prims_in_node: usize,
primitives: Vec<Arc<dyn PrimitiveTrait>>, pub primitives: Vec<P>,
split_method: SplitMethod, pub split_method: SplitMethod,
nodes: Vec<LinearBVHNode>, pub nodes: Vec<LinearBVHNode>,
} }
impl BVHAggregate { impl<P: PrimitiveTrait + Clone + Send + Sync> BVHAggregate<P> {
pub fn new( pub fn new(
mut primitives: Vec<Arc<dyn PrimitiveTrait>>, mut primitives: Vec<P>,
max_prims_in_node: usize, max_prims_in_node: usize,
split_method: SplitMethod, split_method: SplitMethod,
) -> Self { ) -> Self {
@ -186,7 +186,7 @@ impl BVHAggregate {
.map(|(i, p)| BVHPrimitiveInfo::new(i, p.bounds())) .map(|(i, p)| BVHPrimitiveInfo::new(i, p.bounds()))
.collect(); .collect();
let ordered_prims: Vec<Arc<dyn PrimitiveTrait>>; let ordered_prims: Vec<P>;
let total_nodes_count: usize; let total_nodes_count: usize;
let root: Box<BVHBuildNode>; let root: Box<BVHBuildNode>;
@ -310,7 +310,11 @@ impl BVHAggregate {
let m1 = w[0].morton_code & TREELET_MASK; let m1 = w[0].morton_code & TREELET_MASK;
let m2 = w[1].morton_code & TREELET_MASK; let m2 = w[1].morton_code & TREELET_MASK;
// If mask changes, the split is at index i + 1 // If mask changes, the split is at index i + 1
if m1 != m2 { Some(i + 1) } else { None } if m1 != m2 {
Some(i + 1)
} else {
None
}
}) })
.collect(); .collect();

View file

@ -4,8 +4,7 @@ use crate::core::image::{Image, ImageIO};
use crate::globals::get_options; use crate::globals::get_options;
use crate::utils::read_float_file; use crate::utils::read_float_file;
use crate::utils::{Arena, FileLoc, ParameterDictionary}; use crate::utils::{Arena, FileLoc, ParameterDictionary};
use anyhow::{Result, anyhow}; use anyhow::{anyhow, Result};
use shared::Ptr;
use shared::cameras::*; use shared::cameras::*;
use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform}; use shared::core::camera::{Camera, CameraBase, CameraTrait, CameraTransform};
use shared::core::color::SRGB; use shared::core::color::SRGB;
@ -14,6 +13,7 @@ use shared::core::geometry::{Bounds2f, Point2f, Point2i, Vector2f, Vector3f};
use shared::core::image::PixelFormat; use shared::core::image::PixelFormat;
use shared::core::medium::Medium; use shared::core::medium::Medium;
use shared::utils::math::square; use shared::utils::math::square;
use shared::Ptr;
use shared::{Float, PI}; use shared::{Float, PI};
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
@ -25,14 +25,14 @@ pub struct CameraBaseParameters {
pub shutter_open: Float, pub shutter_open: Float,
pub shutter_close: Float, pub shutter_close: Float,
pub film: Arc<Film>, pub film: Arc<Film>,
pub medium: Arc<Medium>, pub medium: Option<Arc<Medium>>,
} }
impl CameraBaseParameters { impl CameraBaseParameters {
pub fn new( pub fn new(
camera_transform: &CameraTransform, camera_transform: &CameraTransform,
film: Arc<Film>, film: Arc<Film>,
medium: Arc<Medium>, medium: Option<Arc<Medium>>,
params: &ParameterDictionary, params: &ParameterDictionary,
loc: &FileLoc, loc: &FileLoc,
) -> Result<Self> { ) -> Result<Self> {
@ -63,7 +63,10 @@ pub trait CameraBaseFactory {
shutter_open: p.shutter_open, shutter_open: p.shutter_open,
shutter_close: p.shutter_close, shutter_close: p.shutter_close,
film: Ptr::from(p.film.clone().as_ref()), film: Ptr::from(p.film.clone().as_ref()),
medium: Ptr::from(p.medium.clone().as_ref()), medium: match p.medium {
Some(ref m) => Ptr::from(m.as_ref()),
None => Ptr::null(),
},
min_pos_differential_x: Vector3f::default(), min_pos_differential_x: Vector3f::default(),
min_pos_differential_y: Vector3f::default(), min_pos_differential_y: Vector3f::default(),
min_dir_differential_x: Vector3f::default(), min_dir_differential_x: Vector3f::default(),
@ -96,7 +99,7 @@ pub trait CameraFactory {
name: &str, name: &str,
params: &ParameterDictionary, params: &ParameterDictionary,
camera_transform: &CameraTransform, camera_transform: &CameraTransform,
medium: Medium, medium: Option<Arc<Medium>>,
film: Arc<Film>, film: Arc<Film>,
loc: &FileLoc, loc: &FileLoc,
arena: &Arena, arena: &Arena,
@ -110,7 +113,7 @@ impl CameraFactory for Camera {
name: &str, name: &str,
params: &ParameterDictionary, params: &ParameterDictionary,
camera_transform: &CameraTransform, camera_transform: &CameraTransform,
medium: Medium, medium: Option<Arc<Medium>>,
film: Arc<Film>, film: Arc<Film>,
loc: &FileLoc, loc: &FileLoc,
arena: &Arena, arena: &Arena,
@ -122,7 +125,7 @@ impl CameraFactory for Camera {
"perspective" => { "perspective" => {
let full_res = film.full_resolution(); let full_res = film.full_resolution();
let camera_params = let camera_params =
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?; CameraBaseParameters::new(camera_transform, film, medium, params, loc)?;
let base = CameraBase::create(camera_params); let base = CameraBase::create(camera_params);
let lens_radius = params.get_one_float("lensradius", 0.)?; let lens_radius = params.get_one_float("lensradius", 0.)?;
let focal_distance = params.get_one_float("focaldistance", 1e6)?; let focal_distance = params.get_one_float("focaldistance", 1e6)?;
@ -169,7 +172,7 @@ impl CameraFactory for Camera {
"orthographic" => { "orthographic" => {
let full_res = film.full_resolution(); let full_res = film.full_resolution();
let camera_params = let camera_params =
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?; CameraBaseParameters::new(camera_transform, film, medium, params, loc)?;
let base = CameraBase::create(camera_params); let base = CameraBase::create(camera_params);
let lens_radius = params.get_one_float("lensradius", 0.)?; let lens_radius = params.get_one_float("lensradius", 0.)?;
let focal_distance = params.get_one_float("focaldistance", 1e6)?; let focal_distance = params.get_one_float("focaldistance", 1e6)?;
@ -211,7 +214,7 @@ impl CameraFactory for Camera {
} }
"realistic" => { "realistic" => {
let camera_params = let camera_params =
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?; CameraBaseParameters::new(camera_transform, film, medium, params, loc)?;
let base = CameraBase::create(camera_params); let base = CameraBase::create(camera_params);
let aperture_diameter = params.get_one_float("aperturediameter", 1.)?; let aperture_diameter = params.get_one_float("aperturediameter", 1.)?;
let focal_distance = params.get_one_float("focaldistance", 10.)?; let focal_distance = params.get_one_float("focaldistance", 10.)?;
@ -391,7 +394,7 @@ impl CameraFactory for Camera {
"spherical" => { "spherical" => {
let full_res = film.full_resolution(); let full_res = film.full_resolution();
let camera_params = let camera_params =
CameraBaseParameters::new(camera_transform, film, medium.into(), params, loc)?; CameraBaseParameters::new(camera_transform, film, medium, params, loc)?;
let base = CameraBase::create(camera_params); let base = CameraBase::create(camera_params);
let m = params.get_one_string("mapping", "equalarea")?; let m = params.get_one_string("mapping", "equalarea")?;
let mapping = match m.as_str() { let mapping = match m.as_str() {

View file

@ -21,13 +21,13 @@ impl Deref for RGBToSpectrumTableData {
impl RGBToSpectrumTableData { impl RGBToSpectrumTableData {
pub fn new(z_nodes: Vec<Float>, coeffs: Vec<Float>) -> Self { pub fn new(z_nodes: Vec<Float>, coeffs: Vec<Float>) -> Self {
eprintln!("z_nodes.len() = {}, coeffs.len() = {}", z_nodes.len(), coeffs.len());
assert_eq!(z_nodes.len(), RES as usize); assert_eq!(z_nodes.len(), RES as usize);
assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3); // bucket*z*y*x*3(coeffs) assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3);
let coeffs_struct = Coeffs::from(&[coeffs[0], coeffs[1], coeffs[2]]);
let view = RGBToSpectrumTable { let view = RGBToSpectrumTable {
z_nodes: Ptr::from(z_nodes.as_ptr()), z_nodes: Ptr::from(z_nodes.as_ptr()),
coeffs: Ptr::from(&coeffs_struct), coeffs: Ptr::from(coeffs.as_ptr() as *const Coeffs),
n_nodes: z_nodes.len() as u32, n_nodes: z_nodes.len() as u32,
}; };

View file

@ -86,3 +86,21 @@ impl MaterialFactory for Material {
} }
} }
} }
pub fn default_diffuse_material(arena: &Arena) -> Material {
use shared::core::texture::GPUSpectrumTexture;
use shared::core::texture::SpectrumConstantTexture;
use shared::core::spectrum::{ConstantSpectrum, Spectrum};
use shared::materials::DiffuseMaterial;
use shared::utils::Ptr;
let grey = Spectrum::Constant(ConstantSpectrum { c: 0.5 });
let tex = GPUSpectrumTexture::Constant(SpectrumConstantTexture::new(grey));
let tex_ptr = arena.alloc(tex);
Material::Diffuse(DiffuseMaterial {
normal_map: Ptr::null(),
displacement: Ptr::null(),
reflectance: tex_ptr,
})
}

View file

@ -640,6 +640,7 @@ impl ParserTarget for BasicSceneBuilder {
loc: FileLoc, loc: FileLoc,
arena: Arc<Arena>, arena: Arc<Arena>,
) -> Result<(), ParserError> { ) -> Result<(), ParserError> {
eprintln!("TEXTURE: name='{}' type='{}' tex='{}'", orig_name, type_name, tex_name);
let name = normalize_utf8(orig_name); let name = normalize_utf8(orig_name);
self.verify_world("Texture", &loc)?; self.verify_world("Texture", &loc)?;
let dict = ParameterDictionary::from_array( let dict = ParameterDictionary::from_array(

View file

@ -3,17 +3,17 @@ use super::state::*;
use crate::core::camera::CameraFactory; use crate::core::camera::CameraFactory;
use crate::core::film::FilmFactory; use crate::core::film::FilmFactory;
use crate::core::filter::FilterFactory; use crate::core::filter::FilterFactory;
use crate::core::image::{Image, io::ImageIO}; use crate::core::image::{io::ImageIO, Image};
use crate::core::material::MaterialFactory; use crate::core::material::MaterialFactory;
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; use crate::core::shape::ShapeFactory;
use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::utils::parallel::{AsyncJob, run_async}; use crate::utils::parallel::{run_async, AsyncJob};
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary}; use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
use crate::utils::{Upload, resolve_filename}; use crate::utils::{resolve_filename, Upload};
use crate::{Arena, FileLoc}; use crate::{Arena, FileLoc};
use anyhow::{Result, anyhow}; use anyhow::{anyhow, Result};
use parking_lot::Mutex; use parking_lot::Mutex;
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::camera::{Camera, CameraTransform}; use shared::core::camera::{Camera, CameraTransform};
@ -46,16 +46,28 @@ impl<'a> SceneLookup<'a> {
return None; return None;
} }
self.media.get(name).cloned().or_else(|| { self.media.get(name).cloned().or_else(|| {
panic!("{}: medium '{}' not defined", loc, name); log::error!("{}: medium '{}' not defined", loc, name);
None
}) })
} }
pub fn resolve_material(&self, mat_ref: &MaterialRef, _loc: &FileLoc) -> Option<Material> { pub fn resolve_material(&self, mat_ref: &MaterialRef, loc: &FileLoc) -> Option<Material> {
match mat_ref { match mat_ref {
MaterialRef::Name(name) => { MaterialRef::Name(name) => match self.named_materials.get(name) {
Some(*self.named_materials.get(name).expect("Material not found")) Some(m) => Some(*m),
None => {
log::error!("{}: named material '{}' not found", loc, name);
None
}
},
MaterialRef::Index(idx) => {
if *idx < self.materials.len() {
Some(self.materials[*idx])
} else {
log::error!("{}: material index {} out of bounds", loc, idx);
None
}
} }
MaterialRef::Index(idx) => Some(self.materials[*idx]),
MaterialRef::None => None, MaterialRef::None => None,
} }
} }
@ -167,7 +179,7 @@ impl BasicScene {
&camera.base.name, &camera.base.name,
&camera.base.parameters, &camera.base.parameters,
&camera.camera_transform, &camera.camera_transform,
*medium.unwrap(), medium,
camera_film, camera_film,
&camera.base.loc, &camera.base.loc,
&arena_camera, &arena_camera,
@ -235,7 +247,7 @@ impl BasicScene {
fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool { fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool {
if filename.is_empty() { if filename.is_empty() {
log::error!( eprintln!(
"[{:?}] \"string filename\" not provided for image texture.", "[{:?}] \"string filename\" not provided for image texture.",
loc loc
); );
@ -243,7 +255,7 @@ impl BasicScene {
return false; return false;
} }
if !std::path::Path::new(filename).exists() { if !std::path::Path::new(filename).exists() {
log::error!("[{:?}] {}: file not found.", loc, filename); eprintln!("[{:?}] {}: file not found.", loc, filename);
*n_missing += 1; *n_missing += 1;
return false; return false;
} }
@ -538,7 +550,7 @@ impl BasicScene {
} }
/// Create area lights for shapes that reference one. Produces a map from /// Create area lights for shapes that reference one. Produces a map from
/// shape index to a vec of lights (one per sub-shape, e.g. per triangle). /// shape index to a vec of lights.
/// Must be called after shapes are loaded but before upload_shapes. /// Must be called after shapes are loaded but before upload_shapes.
pub fn create_area_lights( pub fn create_area_lights(
&self, &self,
@ -569,6 +581,9 @@ impl BasicScene {
&textures.float_textures, &textures.float_textures,
); );
let default_alpha = Arc::new(FloatTexture::default());
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
// Use the film colorspace as fallback for area light emission // Use the film colorspace as fallback for area light emission
let film_cs = self.film_colorspace.lock(); let film_cs = self.film_colorspace.lock();
let colorspace_ref = al_entity let colorspace_ref = al_entity
@ -588,9 +603,7 @@ impl BasicScene {
&al_entity.parameters, &al_entity.parameters,
&al_entity.loc, &al_entity.loc,
shape, shape,
alpha_tex alpha_ref,
.as_ref()
.expect("Alpha texture required for area light"),
colorspace_ref.map(|cs| cs.as_ref()), colorspace_ref.map(|cs| cs.as_ref()),
arena, arena,
) { ) {
@ -647,7 +660,7 @@ impl BasicScene {
primitives primitives
} }
fn load_shapes_parallel( pub fn load_shapes_parallel(
&self, &self,
entities: &[ShapeSceneEntity], entities: &[ShapeSceneEntity],
lookup: &SceneLookup, lookup: &SceneLookup,
@ -655,7 +668,7 @@ impl BasicScene {
) -> Vec<Vec<Shape>> { ) -> Vec<Vec<Shape>> {
entities entities
.par_iter() .par_iter()
.filter_map(|sh| { .map(|sh| {
Shape::create( Shape::create(
&sh.base.name, &sh.base.name,
*sh.render_from_object.as_ref(), *sh.render_from_object.as_ref(),
@ -666,7 +679,10 @@ impl BasicScene {
sh.base.loc.clone(), sh.base.loc.clone(),
arena, arena,
) )
.ok() .unwrap_or_else(|e| {
eprintln!("Shape '{}' failed: {}", sh.base.name, e);
Vec::new()
})
}) })
.collect() .collect()
} }
@ -717,18 +733,18 @@ impl BasicScene {
let mtl = lookup let mtl = lookup
.resolve_material(&entity.material, &entity.base.loc) .resolve_material(&entity.material, &entity.base.loc)
.unwrap(); .unwrap_or_else(|| crate::core::material::default_diffuse_material(arena));
let mi = MediumInterface::new( let mi = MediumInterface {
lookup inside: lookup
.find_medium(&entity.inside_medium, &entity.base.loc) .find_medium(&entity.inside_medium, &entity.base.loc)
.unwrap() .map(|m| Ptr::from(m.as_ref()))
.as_ref(), .unwrap_or(Ptr::null()),
lookup outside: lookup
.find_medium(&entity.outside_medium, &entity.base.loc) .find_medium(&entity.outside_medium, &entity.base.loc)
.unwrap() .map(|m| Ptr::from(m.as_ref()))
.as_ref(), .unwrap_or(Ptr::null()),
); };
let shape_lights_opt = lookup.shape_lights.get(&i); let shape_lights_opt = lookup.shape_lights.get(&i);
@ -872,20 +888,13 @@ impl BasicScene {
textures: &HashMap<String, Arc<FloatTexture>>, textures: &HashMap<String, Arc<FloatTexture>>,
) -> Option<Arc<FloatTexture>> { ) -> Option<Arc<FloatTexture>> {
let name = params.get_texture("alpha"); let name = params.get_texture("alpha");
if name.is_empty() {
return None;
}
match textures.get(&name) { match textures.get(&name) {
Some(tex) => Some(tex.clone()), Some(tex) => Some(tex.clone()),
None => panic!("{:?}: Alpha texture '{}' not found", loc, name), None => panic!("{:?}: Alpha texture '{}' not found", loc, name),
} }
// } else {
// let alpha_val = params.get_one_float("alpha", 1.0);
// if alpha_val < 1.0 {
// Some(Arc::new(FloatTexture::Constant(FloatConstantTexture::new(
// alpha_val,
// ))))
// } else {
// None
// }
// }
} }
pub fn get_medium(&self, name: &str, loc: &FileLoc) -> Option<Arc<Medium>> { pub fn get_medium(&self, name: &str, loc: &FileLoc) -> Option<Arc<Medium>> {

View file

@ -1,9 +1,10 @@
use crate::core::texture::FloatTexture; use crate::core::texture::FloatTexture;
use crate::shapes::{BilinearPatchMesh, TriangleMesh}; use crate::shapes::{BilinearPatchMesh, TriangleMesh};
use crate::utils::{Arena, FileLoc, ParameterDictionary}; use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
use anyhow::{Result, anyhow}; use anyhow::{anyhow, bail, Result};
use parking_lot::Mutex; use parking_lot::Mutex;
use shared::core::shape::*; use shared::core::shape::*;
use shared::Ptr;
use shared::shapes::*; use shared::shapes::*;
use shared::utils::Transform; use shared::utils::Transform;
use std::collections::HashMap; use std::collections::HashMap;
@ -95,20 +96,31 @@ impl ShapeFactory for Shape {
arena, arena,
), ),
"plymesh" => { "plymesh" => {
// let filename = resolve_filename(&parameters.get_one_string("filename", "")); let filename = resolve_filename(&parameters.get_one_string("filename", "")?);
// let ply_mesh = TriQuadMesh::read_ply(filename); if filename.is_empty() {
// let mut edge_length = parameters.get_one_float("edgelength", 1.); bail!("{}: plymesh requires \"filename\" parameter", loc);
// edge_length *= get_options().displacement_edge_scale; }
// let displacement_tex_name = parameters.get_texture("displacement");
TriangleShape::create( let tri_mesh =
render_from_object, TriangleMesh::from_ply(&filename, &render_from_object, reverse_orientation)?;
object_from_render,
reverse_orientation, let host_arc = Arc::new(tri_mesh);
parameters, let mut global_store = ALL_TRIANGLE_MESHES.lock();
float_textures, global_store.push(host_arc.clone());
loc, drop(global_store);
arena,
) let n_tris = host_arc.device.n_triangles;
let mesh_ptr = Ptr::from(&host_arc.device);
let shapes: Vec<Shape> = (0..n_tris)
.map(|i| {
Shape::Triangle(TriangleShape {
mesh: mesh_ptr,
tri_index: i as i32,
})
})
.collect();
Ok(shapes)
} }
_ => Err(anyhow!("Unknown shape name")), _ => Err(anyhow!("Unknown shape name")),
} }

View file

@ -70,10 +70,7 @@ impl CreateFilm for RGBFilm {
loc: &FileLoc, loc: &FileLoc,
_arena: &Arena, _arena: &Arena,
) -> Result<Film> { ) -> Result<Film> {
let colorspace = params.color_space.as_ref().cloned().unwrap_or_else(|| { let colorspace = params.color_space.as_ref().cloned().unwrap_or_else(crate::spectra::default_colorspace_arc);
let stdcs = crate::spectra::get_colorspace_device();
Arc::new(*stdcs.srgb)
});
let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?; let max_component_value = params.get_one_float("maxcomponentvalue", Float::INFINITY)?;
let write_fp16 = params.get_one_bool("savefp16", true)?; let write_fp16 = params.get_one_bool("savefp16", true)?;
let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?; let sensor = PixelSensor::create(params, colorspace.clone(), exposure_time, loc)?;

View file

@ -1,4 +1,5 @@
use crate::core::color::RGBToSpectrumTableData; use crate::core::color::RGBToSpectrumTableData;
use shared::core::color::RES;
use bytemuck::cast_slice; use bytemuck::cast_slice;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use shared::Float; use shared::Float;
@ -20,72 +21,53 @@ pub fn get_options() -> &'static PBRTOptions {
}) })
} }
fn aligned_cast(bytes: &[u8]) -> &[Float] {
match bytemuck::try_cast_slice(bytes) {
Ok(s) => s,
Err(_) => {
let v: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
Box::leak(v.into_boxed_slice())
}
}
}
static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat"); static SRGB_SCALE_BYTES: &[u8] = include_bytes!("../data/srgb_scale.dat");
static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat"); static SRGB_COEFFS_BYTES: &[u8] = include_bytes!("../data/srgb_coeffs.dat");
pub static SRGB_SCALE: Lazy<&[Float]> = Lazy::new(|| cast_slice(SRGB_SCALE_BYTES));
pub static SRGB_COEFFS: Lazy<&[Float]> =
Lazy::new(|| match bytemuck::try_cast_slice(SRGB_COEFFS_BYTES) {
Ok(s) => s,
Err(_) => {
let v: Vec<Float> = bytemuck::pod_collect_to_vec(SRGB_COEFFS_BYTES);
Box::leak(v.into_boxed_slice())
}
});
static DCI_P3_SCALE_BYTES: &[u8] = include_bytes!("../data/dcip3_scale.dat"); static DCI_P3_SCALE_BYTES: &[u8] = include_bytes!("../data/dcip3_scale.dat");
static DCI_P3_COEFFS_BYTES: &[u8] = include_bytes!("../data/dcip3_coeffs.dat"); static DCI_P3_COEFFS_BYTES: &[u8] = include_bytes!("../data/dcip3_coeffs.dat");
pub static DCI_P3_SCALE: Lazy<&[Float]> = Lazy::new(|| cast_slice(DCI_P3_SCALE_BYTES));
pub static DCI_P3_COEFFS: Lazy<&[Float]> =
Lazy::new(|| match bytemuck::try_cast_slice(DCI_P3_COEFFS_BYTES) {
Ok(s) => s,
Err(_) => {
let v: Vec<Float> = bytemuck::pod_collect_to_vec(DCI_P3_COEFFS_BYTES);
Box::leak(v.into_boxed_slice())
}
});
static ACES_SCALE_BYTES: &[u8] = include_bytes!("../data/aces_scale.dat"); static ACES_SCALE_BYTES: &[u8] = include_bytes!("../data/aces_scale.dat");
static ACES_COEFFS_BYTES: &[u8] = include_bytes!("../data/aces_coeffs.dat"); static ACES_COEFFS_BYTES: &[u8] = include_bytes!("../data/aces_coeffs.dat");
pub static ACES_SCALE: Lazy<&[Float]> = Lazy::new(|| cast_slice(ACES_SCALE_BYTES));
pub static ACES_COEFFS: Lazy<&[Float]> =
Lazy::new(|| match bytemuck::try_cast_slice(ACES_COEFFS_BYTES) {
Ok(s) => s,
Err(_) => {
let v: Vec<Float> = bytemuck::pod_collect_to_vec(ACES_COEFFS_BYTES);
Box::leak(v.into_boxed_slice())
}
});
static REC2020_SCALE_BYTES: &[u8] = include_bytes!("../data/rec2020_scale.dat"); static REC2020_SCALE_BYTES: &[u8] = include_bytes!("../data/rec2020_scale.dat");
static REC2020_COEFFS_BYTES: &[u8] = include_bytes!("../data/rec2020_coeffs.dat"); static REC2020_COEFFS_BYTES: &[u8] = include_bytes!("../data/rec2020_coeffs.dat");
pub static REC2020_SCALE: Lazy<&[Float]> = Lazy::new(|| cast_slice(REC2020_SCALE_BYTES));
pub static REC2020_COEFFS: Lazy<&[Float]> = fn strip_to_len(bytes: &[u8], expected_len: usize) -> &'static [Float] {
Lazy::new(|| match bytemuck::try_cast_slice(REC2020_COEFFS_BYTES) { let all: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
Ok(s) => s, let skip = all.len() - expected_len;
Err(_) => { let data = all[skip..].to_vec();
let v: Vec<Float> = bytemuck::pod_collect_to_vec(REC2020_COEFFS_BYTES); Box::leak(data.into_boxed_slice())
Box::leak(v.into_boxed_slice()) }
}
}); const COEFFS_LEN: usize = (RES * RES * RES) as usize * 3 * 3;
pub static SRGB_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_SCALE_BYTES, RES as usize));
pub static SRGB_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(SRGB_COEFFS_BYTES, COEFFS_LEN));
pub static DCI_P3_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(DCI_P3_SCALE_BYTES, RES as usize));
pub static DCI_P3_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(DCI_P3_COEFFS_BYTES, COEFFS_LEN));
pub static ACES_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_SCALE_BYTES, RES as usize));
pub static ACES_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(ACES_COEFFS_BYTES, COEFFS_LEN));
pub static REC2020_SCALE: Lazy<&[Float]> = Lazy::new(|| strip_to_len(REC2020_SCALE_BYTES, RES as usize));
pub static REC2020_COEFFS: Lazy<&[Float]> = Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
pub static SRGB_TABLE: Lazy<RGBToSpectrumTableData> = pub static SRGB_TABLE: Lazy<RGBToSpectrumTableData> =
Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE.to_vec(), SRGB_COEFFS.to_vec())); Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE.to_vec(), SRGB_COEFFS.to_vec()));
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTableData> = pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTableData> =
Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec())); Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
pub static REC2020_TABLE: Lazy<RGBToSpectrumTableData> = pub static REC2020_TABLE: Lazy<RGBToSpectrumTableData> =
Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec())); Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
pub static ACES_TABLE: Lazy<RGBToSpectrumTableData> = pub static ACES_TABLE: Lazy<RGBToSpectrumTableData> =
Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec())); Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec()));
// pub static ACES_TABLE: Lazy<RGBToSpectrumTableData> = Lazy::new(|| {
// RGBToSpectrumTableData::load(Path::new("data/"), "aces2065_1")
// .expect("Failed to load ACES table")
// });

View file

@ -29,7 +29,9 @@ pub fn create(
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant); let mut l = params.get_one_spectrum("l", None, SpectrumType::Illuminant);
let illum_spec = Spectrum::Dense(colorspace.unwrap().illuminant); let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let illum_spec = Spectrum::Dense(cs.illuminant);
let mut scale = params.get_one_float("scale", 1.)?; let mut scale = params.get_one_float("scale", 1.)?;
let two_sided = params.get_one_bool("twosided", false)?; let two_sided = params.get_one_bool("twosided", false)?;

View file

@ -46,10 +46,12 @@ pub fn create(
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
_arena: &Arena, _arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let l = parameters let l = parameters
.get_one_spectrum( .get_one_spectrum(
"L", "L",
Some(Spectrum::Dense(colorspace.unwrap().illuminant)), Some(Spectrum::Dense(cs.illuminant)),
SpectrumType::Illuminant, SpectrumType::Illuminant,
) )
.unwrap(); .unwrap();

View file

@ -28,10 +28,13 @@ pub fn create(
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let i = params let i = params
.get_one_spectrum( .get_one_spectrum(
"I", "I",
Some(Spectrum::Dense(colorspace.unwrap().illuminant)), Some(Spectrum::Dense(cs.illuminant)),
SpectrumType::Illuminant, SpectrumType::Illuminant,
) )
.expect("Could not retrieve spectrum"); .expect("Could not retrieve spectrum");

View file

@ -1,14 +1,14 @@
use crate::Arena;
use crate::core::image::{Image, ImageIO}; use crate::core::image::{Image, ImageIO};
use crate::core::light::lookup_spectrum; use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric; use crate::core::spectrum::spectrum_to_photometric;
use crate::spectra::get_spectra_context; use crate::spectra::get_spectra_context;
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D}; use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
use crate::utils::{FileLoc, ParameterDictionary, Upload, resolve_filename}; use crate::utils::{resolve_filename, FileLoc, ParameterDictionary, Upload};
use anyhow::{Result, anyhow}; use crate::Arena;
use anyhow::{anyhow, Result};
use rayon::prelude::*; use rayon::prelude::*;
use shared::core::camera::CameraTransform; use shared::core::camera::CameraTransform;
use shared::core::geometry::{Bounds2f, Frame, Point2f, Point2i, Point3f, VectorLike, cos_theta}; use shared::core::geometry::{cos_theta, Bounds2f, Frame, Point2f, Point2i, Point3f, VectorLike};
use shared::core::image::{DeviceImage, WrapMode}; use shared::core::image::{DeviceImage, WrapMode};
use shared::core::light::{Light, LightBase, LightType}; use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface}; use shared::core::medium::{Medium, MediumInterface};
@ -131,6 +131,8 @@ pub fn create(
loc: &FileLoc, loc: &FileLoc,
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant); let l = parameters.get_spectrum_array("L", SpectrumType::Illuminant);
let mut scale = parameters.get_one_float("scale", 1.0)?; let mut scale = parameters.get_one_float("scale", 1.0)?;
let portal = parameters.get_point3f_array("portal")?; let portal = parameters.get_point3f_array("portal")?;
@ -154,7 +156,7 @@ pub fn create(
scale /= spectrum_to_photometric(l[0]); scale /= spectrum_to_photometric(l[0]);
l[0] l[0]
} else { } else {
Spectrum::Dense(colorspace.unwrap().illuminant) Spectrum::Dense(cs.illuminant)
}; };
if e_v > 0.0 { if e_v > 0.0 {
@ -167,7 +169,7 @@ pub fn create(
} }
// Image-based lights // Image-based lights
let (image, image_cs) = load_image(&filename, &l, colorspace.unwrap(), loc)?; let (image, image_cs) = load_image(&filename, &l, cs, loc)?;
scale /= spectrum_to_photometric(Spectrum::Dense(image_cs.illuminant)); scale /= spectrum_to_photometric(Spectrum::Dense(image_cs.illuminant));

View file

@ -52,10 +52,13 @@ pub fn create(
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let l = parameters let l = parameters
.get_one_spectrum( .get_one_spectrum(
"L", "L",
Some(Spectrum::Dense(colorspace.unwrap().illuminant)), Some(Spectrum::Dense(cs.illuminant)),
SpectrumType::Illuminant, SpectrumType::Illuminant,
) )
.unwrap(); .unwrap();

View file

@ -63,10 +63,12 @@ pub fn create(
colorspace: Option<&RGBColorSpace>, colorspace: Option<&RGBColorSpace>,
arena: &Arena, arena: &Arena,
) -> Result<Light> { ) -> Result<Light> {
let default_cs = crate::spectra::default_colorspace();
let cs = colorspace.unwrap_or(&default_cs);
let i = parameters let i = parameters
.get_one_spectrum( .get_one_spectrum(
"I", "I",
Some(Spectrum::Dense(colorspace.unwrap().illuminant)), Some(Spectrum::Dense(cs.illuminant)),
SpectrumType::Illuminant, SpectrumType::Illuminant,
) )
.expect("No spectrum"); .expect("No spectrum");

View file

@ -1,6 +1,6 @@
// use crate::Arena; // use crate::Arena;
use crate::utils::sampling::PiecewiseConstant2D; use crate::utils::sampling::PiecewiseConstant2D;
use anyhow::{Context, Result as AnyResult, bail}; use anyhow::{bail, Context, Result as AnyResult};
use ply_rs::parser::Parser; use ply_rs::parser::Parser;
use ply_rs::ply::{DefaultElement, Property}; use ply_rs::ply::{DefaultElement, Property};
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector3f, VectorLike}; use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector3f, VectorLike};
@ -299,9 +299,15 @@ impl TriQuadMesh {
.with_context(|| format!("Couldn't open PLY file \"{}\"", filename_display))?; .with_context(|| format!("Couldn't open PLY file \"{}\"", filename_display))?;
let p = Parser::<DefaultElement>::new(); let p = Parser::<DefaultElement>::new();
let ply = p let ply = if path.extension().and_then(|e| e.to_str()) == Some("gz") {
.read_ply(&mut f) let decoder = flate2::read::GzDecoder::new(f);
.with_context(|| format!("Unable to read/parse PLY file \"{}\"", filename_display))?; let mut buf = std::io::BufReader::new(decoder);
p.read_ply(&mut buf)
} else {
let mut buf = std::io::BufReader::new(f);
p.read_ply(&mut buf)
}
.with_context(|| format!("Unable to read/parse PLY file \"{}\"", filename_display))?;
let mut mesh = TriQuadMesh::default(); let mut mesh = TriQuadMesh::default();

View file

@ -6,7 +6,17 @@ use std::collections::HashMap;
use std::sync::LazyLock; use std::sync::LazyLock;
pub fn create_cie_buffer(data: &[Float]) -> DenselySampledSpectrumBuffer { pub fn create_cie_buffer(data: &[Float]) -> DenselySampledSpectrumBuffer {
let buffer = PiecewiseLinearSpectrumBuffer::from_interleaved(data, false); let (start_lambda, step) = match data.len() {
471 => (360.0, 1.0),
95 => (300.0, 5.0),
n => panic!("Unexpected CIE data length: {}", n),
};
let lambdas: Vec<Float> = (0..data.len())
.map(|i| start_lambda + i as Float * step)
.collect();
let buffer = PiecewiseLinearSpectrumBuffer::new(lambdas, data.to_vec());
let spec = Spectrum::Piecewise(buffer.device); let spec = Spectrum::Piecewise(buffer.device);
DenselySampledSpectrumBuffer::from_spectrum(&spec) DenselySampledSpectrumBuffer::from_spectrum(&spec)
} }

View file

@ -4,6 +4,7 @@ use anyhow::{Result, anyhow};
use shared::core::geometry::Point2f; use shared::core::geometry::Point2f;
use shared::core::spectrum::Spectrum; use shared::core::spectrum::Spectrum;
use shared::core::spectrum::StandardSpectra; use shared::core::spectrum::StandardSpectra;
use shared::spectra::RGBColorSpace;
use shared::spectra::DeviceStandardColorSpaces; use shared::spectra::DeviceStandardColorSpaces;
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z}; use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
use shared::utils::Ptr; use shared::utils::Ptr;
@ -60,7 +61,7 @@ pub static SRGB: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let r = Point2f::new(0.64, 0.33); let r = Point2f::new(0.64, 0.33);
let g = Point2f::new(0.3, 0.6); let g = Point2f::new(0.3, 0.6);
let b = Point2f::new(0.15, 0.06); let b = Point2f::new(0.15, 0.06);
let table_ptr = Ptr::from(&SRGB_TABLE.clone()); let table_ptr = Ptr::from(&SRGB_TABLE.view);
Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr)) Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr))
}); });
@ -71,7 +72,7 @@ pub static DCI_P3: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.265, 0.690); let g = Point2f::new(0.265, 0.690);
let b = Point2f::new(0.150, 0.060); let b = Point2f::new(0.150, 0.060);
let table_ptr = Ptr::from(&DCI_P3_TABLE.clone()); let table_ptr = Ptr::from(&DCI_P3_TABLE.view);
Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr)) Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr))
}); });
@ -82,7 +83,7 @@ pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.170, 0.797); let g = Point2f::new(0.170, 0.797);
let b = Point2f::new(0.131, 0.046); let b = Point2f::new(0.131, 0.046);
let table_ptr = Ptr::from(&REC2020_TABLE.clone()); let table_ptr = Ptr::from(&REC2020_TABLE.view);
Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr)) Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr))
}); });
@ -92,7 +93,7 @@ pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.0000, 1.0000); let g = Point2f::new(0.0000, 1.0000);
let b = Point2f::new(0.0001, -0.0770); let b = Point2f::new(0.0001, -0.0770);
let table_ptr = Ptr::from(&ACES_TABLE.clone()); let table_ptr = Ptr::from(&ACES_TABLE.view);
Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr)) Arc::new(RGBColorSpaceData::new(r, g, b, illum, table_ptr))
}); });
@ -133,3 +134,16 @@ pub fn get_colorspace_device() -> DeviceStandardColorSpaces {
aces2065_1: Ptr::from(&ACES.view), aces2065_1: Ptr::from(&ACES.view),
} }
} }
pub fn default_colorspace() -> RGBColorSpace {
let stdcs = get_colorspace_device();
*stdcs.srgb
}
pub fn default_colorspace_arc() -> Arc<RGBColorSpace> {
Arc::new(default_colorspace())
}
pub fn default_illuminant() -> Spectrum {
Spectrum::Dense(default_colorspace().illuminant)
}

View file

@ -1,15 +1,14 @@
use crate::Arena; use crate::core::texture::{get_texture_cache, CreateTextureMapping, TexInfo};
use crate::core::texture::{ use crate::core::texture::{
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture, CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture,
SpectrumTextureTrait, SpectrumTextureTrait,
}; };
use crate::core::texture::{TexInfo, get_texture_cache};
use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions}; use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions};
use crate::utils::{FileLoc, TextureParameterDictionary}; use crate::utils::{FileLoc, TextureParameterDictionary};
use crate::Arena;
use anyhow::Result; use anyhow::Result;
use shared::Float;
use shared::core::color::ColorEncoding;
use shared::core::color::RGB; use shared::core::color::RGB;
use shared::core::color::{ColorEncoding, SRGBEncoding};
use shared::core::geometry::Vector2f; use shared::core::geometry::Vector2f;
use shared::core::image::WrapMode; use shared::core::image::WrapMode;
use shared::core::spectrum::SpectrumTrait; use shared::core::spectrum::SpectrumTrait;
@ -19,6 +18,7 @@ use shared::spectra::{
SampledWavelengths, SampledWavelengths,
}; };
use shared::utils::Transform; use shared::utils::Transform;
use shared::Float;
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
// use crate::utils::{FileLoc, TextureParameterDictionary}; // use crate::utils::{FileLoc, TextureParameterDictionary};
@ -156,12 +156,40 @@ impl SpectrumTextureTrait for SpectrumImageTexture {
impl CreateSpectrumTexture for SpectrumImageTexture { impl CreateSpectrumTexture for SpectrumImageTexture {
fn create( fn create(
_render_from_texture: Transform, render_from_texture: Transform,
_parameters: TextureParameterDictionary, parameters: TextureParameterDictionary,
_spectrum_type: SpectrumType, spectrum_type: SpectrumType,
_loc: FileLoc, loc: FileLoc,
) -> Result<SpectrumTexture> { ) -> Result<SpectrumTexture> {
todo!() let mapping = TextureMapping2D::create(&parameters, &render_from_texture, &loc)?;
let filename = crate::utils::resolve_filename(&parameters.get_one_string("filename", "")?);
let scale = parameters.get_one_float("scale", 1.0)?;
let invert = parameters.get_one_bool("invert", false)?;
let filter_options = MIPMapFilterOptions::default();
let wrap_str = parameters.get_one_string("wrap", "repeat")?;
let wrap_mode = match wrap_str.as_str() {
"repeat" => WrapMode::Repeat,
"clamp" => WrapMode::Clamp,
"black" => WrapMode::Black,
_ => WrapMode::Repeat,
};
let encoding = ColorEncoding::SRGB(SRGBEncoding);
let tex = SpectrumImageTexture::new(
mapping,
filename,
filter_options,
wrap_mode,
scale,
invert,
encoding,
spectrum_type,
);
Ok(SpectrumTexture::Image(tex))
} }
} }

View file

@ -15,11 +15,11 @@ use shared::core::spectrum::Spectrum;
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture}; use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace}; use shared::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
use shared::textures::*; use shared::textures::*;
use shared::utils::Ptr;
use shared::utils::mesh::{DeviceBilinearPatchMesh, DeviceTriangleMesh}; use shared::utils::mesh::{DeviceBilinearPatchMesh, DeviceTriangleMesh};
use shared::utils::sampling::{ use shared::utils::sampling::{
DevicePiecewiseConstant1D, DevicePiecewiseConstant2D, DeviceWindowedPiecewiseConstant2D, DevicePiecewiseConstant1D, DevicePiecewiseConstant2D, DeviceWindowedPiecewiseConstant2D,
}; };
use shared::utils::Ptr;
use std::alloc::Layout; use std::alloc::Layout;
use std::collections::HashMap; use std::collections::HashMap;
use std::slice::from_raw_parts; use std::slice::from_raw_parts;
@ -174,7 +174,7 @@ impl Upload for SpectrumTexture {
scale: tex.base.scale, scale: tex.base.scale,
invert: tex.base.invert, invert: tex.base.invert,
is_single_channel: tex.base.mipmap.is_single_channel(), is_single_channel: tex.base.mipmap.is_single_channel(),
color_space: tex.base.mipmap.color_space.clone().unwrap(), color_space: tex.base.mipmap.color_space.clone().unwrap_or_else(crate::spectra::default_colorspace),
spectrum_type: tex.spectrum_type, spectrum_type: tex.spectrum_type,
}; };
GPUSpectrumTexture::Image(gpu_img) GPUSpectrumTexture::Image(gpu_img)
@ -288,17 +288,17 @@ impl Upload for RGBToSpectrumTable {
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> { fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let n_nodes = self.n_nodes as usize; let n_nodes = self.n_nodes as usize;
let z_slice = unsafe { from_raw_parts(self.z_nodes.as_raw(), n_nodes) }; let z_slice = unsafe { from_raw_parts(self.z_nodes.as_raw(), n_nodes) };
let coeffs_slice = unsafe { from_raw_parts(self.coeffs.as_raw(), n_nodes) };
let (z_ptr, _) = arena.alloc_slice(z_slice); let (z_ptr, _) = arena.alloc_slice(z_slice);
let n_coeffs = 3 * (n_nodes as usize).pow(3);
let coeffs_slice = unsafe { from_raw_parts(self.coeffs.as_raw(), n_coeffs) };
let (c_ptr, _) = arena.alloc_slice(coeffs_slice); let (c_ptr, _) = arena.alloc_slice(coeffs_slice);
let shared_table = RGBToSpectrumTable { arena.alloc(RGBToSpectrumTable {
z_nodes: z_ptr, z_nodes: z_ptr,
coeffs: c_ptr, coeffs: c_ptr,
n_nodes: self.n_nodes, n_nodes: self.n_nodes,
}; })
arena.alloc(shared_table)
} }
} }

View file

@ -6,7 +6,7 @@ use std::sync::OnceLock;
static SEARCH_DIRECTORY: OnceLock<PathBuf> = OnceLock::new(); static SEARCH_DIRECTORY: OnceLock<PathBuf> = OnceLock::new();
fn set_search_directory(filename: &str) { pub fn set_search_directory(filename: &str) {
let path = Path::new(filename); let path = Path::new(filename);
let dir = if path.is_dir() { let dir = if path.is_dir() {
path.to_path_buf() path.to_path_buf()

View file

@ -513,7 +513,7 @@ impl ParameterDictionary {
pub fn get_texture(&self, name: &str) -> String { pub fn get_texture(&self, name: &str) -> String {
for p in &self.params { for p in &self.params {
if p.name == name || p.type_name != "texture" { if p.name == name && p.type_name == "texture" {
if p.strings.len() != 1 { if p.strings.len() != 1 {
panic!( panic!(
"[{:?}] Expected 1 texture name for {}, found {}", "[{:?}] Expected 1 texture name for {}, found {}",
@ -526,7 +526,7 @@ impl ParameterDictionary {
return p.strings[0].clone(); return p.strings[0].clone();
} }
} }
return "".to_string(); String::new()
} }
pub fn report_unused(&self) { pub fn report_unused(&self) {

View file

@ -788,6 +788,8 @@ impl<'a> SceneParser<'a> {
.unwrap_or(Path::new(".")) .unwrap_or(Path::new("."))
.to_path_buf(); .to_path_buf();
crate::utils::file::set_search_directory(current_dir.to_str().unwrap_or("."));
Self { Self {
target, target,
file_stack: vec![root], file_stack: vec![root],