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
*.txt
scenes/
compile.sh

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,13 +21,13 @@ impl Deref for RGBToSpectrumTableData {
impl RGBToSpectrumTableData {
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!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3); // bucket*z*y*x*3(coeffs)
let coeffs_struct = Coeffs::from(&[coeffs[0], coeffs[1], coeffs[2]]);
assert_eq!(coeffs.len(), (RES * RES * RES) as usize * 3 * 3);
let view = RGBToSpectrumTable {
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,
};

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,
arena: Arc<Arena>,
) -> Result<(), ParserError> {
eprintln!("TEXTURE: name='{}' type='{}' tex='{}'", orig_name, type_name, tex_name);
let name = normalize_utf8(orig_name);
self.verify_world("Texture", &loc)?;
let dict = ParameterDictionary::from_array(

View file

@ -3,17 +3,17 @@ use super::state::*;
use crate::core::camera::CameraFactory;
use crate::core::film::FilmFactory;
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::primitive::{CreateGeometricPrimitive, CreateSimplePrimitive};
use crate::core::sampler::SamplerFactory;
use crate::core::shape::ShapeFactory;
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::{Upload, resolve_filename};
use crate::utils::{resolve_filename, Upload};
use crate::{Arena, FileLoc};
use anyhow::{Result, anyhow};
use anyhow::{anyhow, Result};
use parking_lot::Mutex;
use rayon::prelude::*;
use shared::core::camera::{Camera, CameraTransform};
@ -46,16 +46,28 @@ impl<'a> SceneLookup<'a> {
return None;
}
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 {
MaterialRef::Name(name) => {
Some(*self.named_materials.get(name).expect("Material not found"))
MaterialRef::Name(name) => match self.named_materials.get(name) {
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,
}
}
@ -167,7 +179,7 @@ impl BasicScene {
&camera.base.name,
&camera.base.parameters,
&camera.camera_transform,
*medium.unwrap(),
medium,
camera_film,
&camera.base.loc,
&arena_camera,
@ -235,7 +247,7 @@ impl BasicScene {
fn validate_texture_file(&self, filename: &str, loc: &FileLoc, n_missing: &mut usize) -> bool {
if filename.is_empty() {
log::error!(
eprintln!(
"[{:?}] \"string filename\" not provided for image texture.",
loc
);
@ -243,7 +255,7 @@ impl BasicScene {
return false;
}
if !std::path::Path::new(filename).exists() {
log::error!("[{:?}] {}: file not found.", loc, filename);
eprintln!("[{:?}] {}: file not found.", loc, filename);
*n_missing += 1;
return false;
}
@ -538,7 +550,7 @@ impl BasicScene {
}
/// 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.
pub fn create_area_lights(
&self,
@ -569,6 +581,9 @@ impl BasicScene {
&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
let film_cs = self.film_colorspace.lock();
let colorspace_ref = al_entity
@ -588,9 +603,7 @@ impl BasicScene {
&al_entity.parameters,
&al_entity.loc,
shape,
alpha_tex
.as_ref()
.expect("Alpha texture required for area light"),
alpha_ref,
colorspace_ref.map(|cs| cs.as_ref()),
arena,
) {
@ -647,7 +660,7 @@ impl BasicScene {
primitives
}
fn load_shapes_parallel(
pub fn load_shapes_parallel(
&self,
entities: &[ShapeSceneEntity],
lookup: &SceneLookup,
@ -655,7 +668,7 @@ impl BasicScene {
) -> Vec<Vec<Shape>> {
entities
.par_iter()
.filter_map(|sh| {
.map(|sh| {
Shape::create(
&sh.base.name,
*sh.render_from_object.as_ref(),
@ -666,7 +679,10 @@ impl BasicScene {
sh.base.loc.clone(),
arena,
)
.ok()
.unwrap_or_else(|e| {
eprintln!("Shape '{}' failed: {}", sh.base.name, e);
Vec::new()
})
})
.collect()
}
@ -717,18 +733,18 @@ impl BasicScene {
let mtl = lookup
.resolve_material(&entity.material, &entity.base.loc)
.unwrap();
.unwrap_or_else(|| crate::core::material::default_diffuse_material(arena));
let mi = MediumInterface::new(
lookup
let mi = MediumInterface {
inside: lookup
.find_medium(&entity.inside_medium, &entity.base.loc)
.unwrap()
.as_ref(),
lookup
.map(|m| Ptr::from(m.as_ref()))
.unwrap_or(Ptr::null()),
outside: lookup
.find_medium(&entity.outside_medium, &entity.base.loc)
.unwrap()
.as_ref(),
);
.map(|m| Ptr::from(m.as_ref()))
.unwrap_or(Ptr::null()),
};
let shape_lights_opt = lookup.shape_lights.get(&i);
@ -872,20 +888,13 @@ impl BasicScene {
textures: &HashMap<String, Arc<FloatTexture>>,
) -> Option<Arc<FloatTexture>> {
let name = params.get_texture("alpha");
if name.is_empty() {
return None;
}
match textures.get(&name) {
Some(tex) => Some(tex.clone()),
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>> {

View file

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

View file

@ -70,10 +70,7 @@ impl CreateFilm for RGBFilm {
loc: &FileLoc,
_arena: &Arena,
) -> Result<Film> {
let colorspace = params.color_space.as_ref().cloned().unwrap_or_else(|| {
let stdcs = crate::spectra::get_colorspace_device();
Arc::new(*stdcs.srgb)
});
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)?;

View file

@ -1,4 +1,5 @@
use crate::core::color::RGBToSpectrumTableData;
use shared::core::color::RES;
use bytemuck::cast_slice;
use once_cell::sync::Lazy;
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_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_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_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_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]> =
Lazy::new(|| match bytemuck::try_cast_slice(REC2020_COEFFS_BYTES) {
Ok(s) => s,
Err(_) => {
let v: Vec<Float> = bytemuck::pod_collect_to_vec(REC2020_COEFFS_BYTES);
Box::leak(v.into_boxed_slice())
}
});
fn strip_to_len(bytes: &[u8], expected_len: usize) -> &'static [Float] {
let all: Vec<Float> = bytemuck::pod_collect_to_vec(bytes);
let skip = all.len() - expected_len;
let data = all[skip..].to_vec();
Box::leak(data.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> =
Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE.to_vec(), SRGB_COEFFS.to_vec()));
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTableData> =
Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
pub static REC2020_TABLE: Lazy<RGBToSpectrumTableData> =
Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
pub static ACES_TABLE: Lazy<RGBToSpectrumTableData> =
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,
) -> Result<Light> {
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 two_sided = params.get_one_bool("twosided", false)?;

View file

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

View file

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

View file

@ -1,14 +1,14 @@
use crate::Arena;
use crate::core::image::{Image, ImageIO};
use crate::core::light::lookup_spectrum;
use crate::core::spectrum::spectrum_to_photometric;
use crate::spectra::get_spectra_context;
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
use crate::utils::{FileLoc, ParameterDictionary, Upload, resolve_filename};
use anyhow::{Result, anyhow};
use crate::utils::{resolve_filename, FileLoc, ParameterDictionary, Upload};
use crate::Arena;
use anyhow::{anyhow, Result};
use rayon::prelude::*;
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::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface};
@ -131,6 +131,8 @@ pub fn create(
loc: &FileLoc,
arena: &Arena,
) -> 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 mut scale = parameters.get_one_float("scale", 1.0)?;
let portal = parameters.get_point3f_array("portal")?;
@ -154,7 +156,7 @@ pub fn create(
scale /= spectrum_to_photometric(l[0]);
l[0]
} else {
Spectrum::Dense(colorspace.unwrap().illuminant)
Spectrum::Dense(cs.illuminant)
};
if e_v > 0.0 {
@ -167,7 +169,7 @@ pub fn create(
}
// 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));

View file

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

View file

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

View file

@ -1,6 +1,6 @@
// use crate::Arena;
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::ply::{DefaultElement, Property};
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))?;
let p = Parser::<DefaultElement>::new();
let ply = p
.read_ply(&mut f)
.with_context(|| format!("Unable to read/parse PLY file \"{}\"", filename_display))?;
let ply = if path.extension().and_then(|e| e.to_str()) == Some("gz") {
let decoder = flate2::read::GzDecoder::new(f);
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();

View file

@ -6,7 +6,17 @@ use std::collections::HashMap;
use std::sync::LazyLock;
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);
DenselySampledSpectrumBuffer::from_spectrum(&spec)
}

View file

@ -4,6 +4,7 @@ use anyhow::{Result, anyhow};
use shared::core::geometry::Point2f;
use shared::core::spectrum::Spectrum;
use shared::core::spectrum::StandardSpectra;
use shared::spectra::RGBColorSpace;
use shared::spectra::DeviceStandardColorSpaces;
use shared::spectra::cie::{CIE_D65, CIE_X, CIE_Y, CIE_Z};
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 g = Point2f::new(0.3, 0.6);
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))
});
@ -71,7 +72,7 @@ pub static DCI_P3: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.265, 0.690);
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))
});
@ -82,7 +83,7 @@ pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.170, 0.797);
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))
});
@ -92,7 +93,7 @@ pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
let g = Point2f::new(0.0000, 1.0000);
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))
});
@ -133,3 +134,16 @@ pub fn get_colorspace_device() -> DeviceStandardColorSpaces {
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::{
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture,
SpectrumTextureTrait,
};
use crate::core::texture::{TexInfo, get_texture_cache};
use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions};
use crate::utils::{FileLoc, TextureParameterDictionary};
use crate::Arena;
use anyhow::Result;
use shared::Float;
use shared::core::color::ColorEncoding;
use shared::core::color::RGB;
use shared::core::color::{ColorEncoding, SRGBEncoding};
use shared::core::geometry::Vector2f;
use shared::core::image::WrapMode;
use shared::core::spectrum::SpectrumTrait;
@ -19,6 +18,7 @@ use shared::spectra::{
SampledWavelengths,
};
use shared::utils::Transform;
use shared::Float;
use std::path::Path;
use std::sync::Arc;
// use crate::utils::{FileLoc, TextureParameterDictionary};
@ -156,12 +156,40 @@ impl SpectrumTextureTrait for SpectrumImageTexture {
impl CreateSpectrumTexture for SpectrumImageTexture {
fn create(
_render_from_texture: Transform,
_parameters: TextureParameterDictionary,
_spectrum_type: SpectrumType,
_loc: FileLoc,
render_from_texture: Transform,
parameters: TextureParameterDictionary,
spectrum_type: SpectrumType,
loc: FileLoc,
) -> 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::spectra::{DenselySampledSpectrum, DeviceStandardColorSpaces, RGBColorSpace};
use shared::textures::*;
use shared::utils::Ptr;
use shared::utils::mesh::{DeviceBilinearPatchMesh, DeviceTriangleMesh};
use shared::utils::sampling::{
DevicePiecewiseConstant1D, DevicePiecewiseConstant2D, DeviceWindowedPiecewiseConstant2D,
};
use shared::utils::Ptr;
use std::alloc::Layout;
use std::collections::HashMap;
use std::slice::from_raw_parts;
@ -174,7 +174,7 @@ impl Upload for SpectrumTexture {
scale: tex.base.scale,
invert: tex.base.invert,
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,
};
GPUSpectrumTexture::Image(gpu_img)
@ -288,17 +288,17 @@ impl Upload for RGBToSpectrumTable {
fn upload<A: GpuAllocator>(&self, arena: &Arena<A>) -> Ptr<Self::Target> {
let n_nodes = self.n_nodes as usize;
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 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 shared_table = RGBToSpectrumTable {
arena.alloc(RGBToSpectrumTable {
z_nodes: z_ptr,
coeffs: c_ptr,
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();
fn set_search_directory(filename: &str) {
pub fn set_search_directory(filename: &str) {
let path = Path::new(filename);
let dir = if path.is_dir() {
path.to_path_buf()

View file

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

View file

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