From 3cb2086f6d0a1212690652be833b2ed15ab9479a Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Mon, 25 May 2026 22:14:56 +0100 Subject: [PATCH] Added missing geometry. Implementing missing material methods --- shared/src/core/filter.rs | 8 +- shared/src/core/interaction.rs | 3 +- shared/src/core/primitive.rs | 6 +- shared/src/core/spectrum.rs | 4 +- shared/src/materials/conductor.rs | 75 ++- shared/src/utils/ptr.rs | 15 +- src/core/interaction.rs | 8 +- src/core/render.rs | 13 +- src/core/scene/builder.rs | 8 +- src/core/scene/scene.rs | 9 +- src/core/shape.rs | 76 ++- src/integrators/path.rs | 6 - src/lights/diffuse.rs | 2 +- src/materials/conductor.rs | 64 ++- src/textures/scaled.rs | 30 +- src/utils/loopsubdiv.rs | 745 ++++++++++++++---------------- src/utils/parameters.rs | 15 +- src/utils/parser.rs | 8 +- 18 files changed, 629 insertions(+), 466 deletions(-) diff --git a/shared/src/core/filter.rs b/shared/src/core/filter.rs index 10beb20..6e449cf 100644 --- a/shared/src/core/filter.rs +++ b/shared/src/core/filter.rs @@ -75,17 +75,17 @@ pub enum Filter { impl FilterTrait for Ptr { fn radius(&self) -> Vector2f { - self.radius() + self.get().unwrap().radius() } fn integral(&self) -> Float { - self.as_ref().integral() + self.get().unwrap().integral() } fn evaluate(&self, p: Point2f) -> Float { - self.evaluate(p) + self.get().unwrap().evaluate(p) } fn sample(&self, p: Point2f) -> FilterSample { - self.sample(p) + self.get().unwrap().sample(p) } } diff --git a/shared/src/core/interaction.rs b/shared/src/core/interaction.rs index fdc9683..5de3a73 100644 --- a/shared/src/core/interaction.rs +++ b/shared/src/core/interaction.rs @@ -354,8 +354,7 @@ impl SurfaceInteraction { let (dpdu, dpdv) = if !displacement.is_null() { bump_map(tex_eval, &displacement, &ctx) } else if !normal_image.is_null() { - let map = unsafe { normal_image.as_ref() }; - normal_map(map, &ctx) + normal_map(&normal_image, &ctx) } else { (self.shading.dpdu, self.shading.dpdv) }; diff --git a/shared/src/core/primitive.rs b/shared/src/core/primitive.rs index 3db89f3..b81607b 100644 --- a/shared/src/core/primitive.rs +++ b/shared/src/core/primitive.rs @@ -224,14 +224,14 @@ pub enum Primitive { impl PrimitiveTrait for Ptr { fn bounds(&self) -> Bounds3f { - unsafe { self.as_ref().bounds() } + self.get().unwrap().bounds() } fn intersect(&self, r: &Ray, t_max: Option) -> Option { - unsafe { self.as_ref().intersect(r, t_max) } + self.get().unwrap().intersect(r, t_max) } fn intersect_p(&self, r: &Ray, t_max: Option) -> bool { - unsafe { self.as_ref().intersect_p(r, t_max) } + self.get().unwrap().intersect_p(r, t_max) } } diff --git a/shared/src/core/spectrum.rs b/shared/src/core/spectrum.rs index ad458c6..c63b4b3 100644 --- a/shared/src/core/spectrum.rs +++ b/shared/src/core/spectrum.rs @@ -40,10 +40,10 @@ pub enum Spectrum { impl SpectrumTrait for Ptr { fn evaluate(&self, lambda: Float) -> Float { - self.evaluate(lambda) + self.get().unwrap().evaluate(lambda) } fn max_value(&self) -> Float { - self.max_value() + self.get().unwrap().max_value() } } diff --git a/shared/src/materials/conductor.rs b/shared/src/materials/conductor.rs index 26792e3..a621c87 100644 --- a/shared/src/materials/conductor.rs +++ b/shared/src/materials/conductor.rs @@ -10,34 +10,85 @@ use crate::core::scattering::TrowbridgeReitzDistribution; use crate::core::spectrum::{Spectrum, SpectrumTrait}; use crate::core::texture::{GPUFloatTexture, GPUSpectrumTexture, TextureEvaluator}; use crate::spectra::{SampledSpectrum, SampledWavelengths}; -use crate::utils::Ptr; use crate::utils::math::clamp; +use crate::utils::Ptr; #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct ConductorMaterial { - pub displacement: Ptr, + pub normal_map: Ptr, + pub reflectance: Ptr, pub eta: Ptr, pub k: Ptr, - pub reflectance: Ptr, pub u_roughness: Ptr, pub v_roughness: Ptr, + pub displacement: Ptr, pub remap_roughness: bool, - pub normal_map: Ptr, +} + +impl ConductorMaterial { + pub fn new( + normal_map: Ptr, + reflectance: Ptr, + eta: Ptr, + k: Ptr, + u_roughness: Ptr, + v_roughness: Ptr, + displacement: Ptr, + remap_roughness: bool, + ) -> Self { + Self { + normal_map, + reflectance, + eta, + k, + u_roughness, + v_roughness, + displacement, + remap_roughness, + } + } } impl MaterialTrait for ConductorMaterial { fn get_bsdf( &self, - _tex_eval: &T, - _ctx: &MaterialEvalContext, - _lambda: &SampledWavelengths, + tex_eval: &T, + ctx: &MaterialEvalContext, + lambda: &SampledWavelengths, ) -> BSDF { - todo!() + let mut u_rough = tex_eval.evaluate_float(&self.u_roughness, ctx); + let mut v_rough = tex_eval.evaluate_float(&self.v_roughness, ctx); + if self.remap_roughness { + u_rough = TrowbridgeReitzDistribution::roughness_to_alpha(u_rough); + v_rough = TrowbridgeReitzDistribution::roughness_to_alpha(v_rough); + } + let (etas, ks) = if !self.eta.is_null() { + ( + tex_eval.evaluate_spectrum(&self.eta, ctx, lambda), + tex_eval.evaluate_spectrum(&self.k, ctx, lambda), + ) + } else { + let r = SampledSpectrum::clamp( + &tex_eval.evaluate_spectrum(&self.reflectance, ctx, lambda), + 0., + 0.9999, + ); + let one_minus_r = SampledSpectrum::new(1.) - r; + ( + SampledSpectrum::new(1.), + 2. * r.sqrt() / SampledSpectrum::clamp_zero(&one_minus_r).sqrt(), + ) + }; + + let distrib = TrowbridgeReitzDistribution::new(u_rough, v_rough); + let bxdf = BxDF::Conductor(ConductorBxDF::new(&distrib, etas, ks)); + BSDF::new(ctx.ns, ctx.dpdus, bxdf) } + fn get_bssrdf( &self, - _tex_eval: &T, + tex_eval: &T, _ctx: &MaterialEvalContext, _lambda: &SampledWavelengths, ) -> Option { @@ -51,14 +102,14 @@ impl MaterialTrait for ConductorMaterial { } fn get_normal_map(&self) -> Option<&Image> { - todo!() + self.normal_map.get() } fn get_displacement(&self) -> Ptr { - todo!() + self.displacement } fn has_subsurface_scattering(&self) -> bool { - todo!() + false } } diff --git a/shared/src/utils/ptr.rs b/shared/src/utils/ptr.rs index 447fd97..8de2017 100644 --- a/shared/src/utils/ptr.rs +++ b/shared/src/utils/ptr.rs @@ -1,6 +1,7 @@ use core::hash::{Hash, Hasher}; use core::marker::PhantomData; use core::ops::Index; +use core::cmp; #[repr(transparent)] #[derive(Debug)] @@ -21,6 +22,18 @@ impl PartialEq for Ptr { } } +impl PartialOrd for Ptr { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.ptr.cmp(&other.ptr)) + } +} + +impl Ord for Ptr { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.ptr.cmp(&other.ptr) + } +} + impl Eq for Ptr {} unsafe impl Send for Ptr {} @@ -61,7 +74,7 @@ impl Ptr { } #[inline(always)] - pub unsafe fn get_mut<'a>(self) -> Option<&'a mut T> { + pub fn get_mut<'a>(self) -> Option<&'a mut T> { if self.is_null() { None } else { diff --git a/src/core/interaction.rs b/src/core/interaction.rs index 2108319..3e56bb2 100644 --- a/src/core/interaction.rs +++ b/src/core/interaction.rs @@ -39,7 +39,9 @@ impl InteractionGetter for SurfaceInteraction { ) -> Option { self.compute_differentials(r, camera, sampler.samples_per_pixel() as i32); let material = { - let mut active_mat = unsafe { self.material.as_ref() }; + let Some(mut active_mat) = self.material.get() else { + return None; + }; let tex_eval = UniversalTextureEvaluator; while let Material::Mix(mix) = active_mat { let ctx = MaterialEvalContext::from(&*self); @@ -72,7 +74,9 @@ impl InteractionGetter for SurfaceInteraction { lambda: &SampledWavelengths, _camera: &Camera, ) -> Option { - let mut active_mat = unsafe { self.material.as_ref() }; + let Some(mut active_mat) = self.material.get() else { + return None; + }; let tex_eval = UniversalTextureEvaluator; while let Material::Mix(mix) = active_mat { let ctx = MaterialEvalContext::from(self); diff --git a/src/core/render.rs b/src/core/render.rs index 5e3abc3..d63b5c6 100644 --- a/src/core/render.rs +++ b/src/core/render.rs @@ -17,13 +17,20 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> { let media = scene.create_media(); let textures = scene.create_textures(arena); let (named_materials, materials) = scene.create_materials(&textures, arena)?; + for (i, m) in materials.iter().enumerate() { + eprintln!("materials[{}]: {:?}", i, std::mem::discriminant(m)); + } let lights = scene.create_lights(&textures, arena); let have_scattering = { let shapes = scene.shapes.lock(); let animated = scene.animated_shapes.lock(); - shapes.iter().any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty()) - || animated.iter().any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty()) + shapes + .iter() + .any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty()) + || animated + .iter() + .any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty()) }; let (aggregate, area_lights) = @@ -83,7 +90,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> { log::debug!("Distance from camera: {}\n", intr.p().distance(cr.ray.o)); for (name, mtl) in &named_materials { - if *mtl == unsafe { *intr.material.as_ref() } { + if *mtl == *intr.material.get().unwrap() { log::debug!("Named material: {}\n\n", name); break; } diff --git a/src/core/scene/builder.rs b/src/core/scene/builder.rs index 8e9fc92..3804c75 100644 --- a/src/core/scene/builder.rs +++ b/src/core/scene/builder.rs @@ -104,7 +104,6 @@ impl Default for GraphicsState { } } - #[derive(PartialEq, Eq)] enum BlockState { OptionsBlock, @@ -263,7 +262,7 @@ impl ParserTarget for BasicSceneBuilder { let stdcs = get_colorspace_device(); let _ = match stdcs.get_named(name) { Some(cs) => { - self.graphics_state.color_space = unsafe { Some(Arc::new(*cs.as_ref())) }; + self.graphics_state.color_space = Some(Arc::new(*cs.get().unwrap())); } None => { eprintln!("Error: Color space '{}' unknown at {}", name, loc); @@ -747,9 +746,12 @@ impl ParserTarget for BasicSceneBuilder { loc, parameters: ParameterDictionary::new(params.clone(), None).unwrap(), }; - self.graphics_state.current_material_name = self.scene.add_material(entity).to_string(); + let idx = self.scene.add_material(entity); + self.graphics_state.current_material_index = Some(idx); + self.graphics_state.current_material_name = String::new(); Ok(()) } + fn make_named_material( &mut self, _name: &str, diff --git a/src/core/scene/scene.rs b/src/core/scene/scene.rs index d369301..9270be6 100644 --- a/src/core/scene/scene.rs +++ b/src/core/scene/scene.rs @@ -80,7 +80,7 @@ fn resolve_material( arena: &Arena, ) -> Material { match mat_ref { - MaterialRef::Name(name) => match named_materials.get(name) { + MaterialRef::Name(name) => match named_materials.get(name) { Some(m) => *m, None => { log::error!("{}: named material '{}' not found", loc, name); @@ -468,7 +468,7 @@ impl BasicScene { let materials: Vec = state .materials .iter() - .filter_map(|entity| { + .map(|entity| { let result: Result = (|| { let normal_map = self.get_normal_map(&state, &entity.parameters)?; let tex_dict = TextureParameterDictionary::new( @@ -484,12 +484,11 @@ impl BasicScene { arena, ) })(); - match result { - Ok(mat) => Some(mat), + Ok(mat) => mat, Err(e) => { log::error!("{}: failed to create material: {}", entity.loc, e); - None + crate::core::material::default_diffuse_material(arena) } } }) diff --git a/src/core/shape.rs b/src/core/shape.rs index bebe9ce..a8f6a6c 100644 --- a/src/core/shape.rs +++ b/src/core/shape.rs @@ -1,12 +1,13 @@ use crate::core::texture::FloatTexture; use crate::shapes::*; +use crate::utils::loop_subdivide; use crate::utils::resolve_filename; use crate::{Arena, FileLoc, ParameterDictionary}; use anyhow::{anyhow, bail, Result}; use parking_lot::Mutex; use shared::core::shape::*; use shared::shapes::*; -use shared::{Transform, Ptr}; +use shared::{Ptr, Transform}; use std::collections::HashMap; use std::sync::Arc; @@ -130,6 +131,79 @@ impl ShapeFactory for Shape { Ok(shapes) } + "loopsubdiv" => { + let n_levels = parameters.get_one_int("levels", 3)? as usize; + + let Ok(vertex_indices_i32) = parameters.get_int_array("indices") else { + bail!("Vertex indices \"indices\" not provided for \"LoopSubdiv\" shape."); + }; + let Ok(p) = parameters.get_point3f_array("P") else { + bail!("Vertex positions \"P\" not provided for LoopSubdiv shape."); + }; + + if vertex_indices_i32.is_empty() { + bail!("Vertex indices \"indices\" must be provided with loopsubdiv."); + } + if vertex_indices_i32.len() % 3 != 0 { + bail!( + "Number of vertex indices {} not a multiple of 3.", + vertex_indices_i32.len() + ); + } + if p.is_empty() { + bail!("Vertex positions \"P\" must be provided with loopsubdiv."); + } + + let vertex_indices: Vec = vertex_indices_i32 + .iter() + .map(|&i| { + if i < 0 { + bail!("Negative vertex index {} in loopsubdiv.", i); + } + let ui = i as usize; + if ui >= p.len() { + bail!( + "Vertex index {} out of bounds ({} \"P\" values given).", + i, + p.len() + ); + } + Ok(ui) + }) + .collect::>>()?; + + let scheme = parameters.get_one_string("scheme", "loop")?; + if scheme != "loop" { + bail!("Unsupported subdivision scheme \"{}\".", scheme); + } + + let mesh = loop_subdivide( + arena, + &render_from_object, + reverse_orientation, + n_levels, + &vertex_indices, + &p, + ); + + let host_arc = Arc::new(mesh); + let mut global_store = ALL_TRIANGLE_MESHES.lock(); + global_store.push(host_arc.clone()); + drop(global_store); + + let n_tris = host_arc.n_triangles; + let mesh_ptr = arena.alloc_arc(host_arc); + let shapes: Vec> = (0..n_tris) + .map(|i| { + arena.alloc(Shape::Triangle(TriangleShape { + mesh: mesh_ptr, + tri_index: i as i32, + })) + }) + .collect(); + + Ok(shapes) + } _ => Err(anyhow!("Unknown shape name")), } } diff --git a/src/integrators/path.rs b/src/integrators/path.rs index 0e7ffde..3a13f85 100644 --- a/src/integrators/path.rs +++ b/src/integrators/path.rs @@ -227,12 +227,6 @@ impl RayIntegratorTrait for PathIntegrator { let t_hit = si.t_hit(); let isect = &mut si.intr; - if state.depth == 0 { - let n = isect.n(); - let v = (n.x() + 1.0) * 0.5; - return (SampledSpectrum::from_array(&[v, v, v, v]), None); - } - // Emission from hit surface let le = isect.le(-ray.d, lambda); if !le.is_black() { diff --git a/src/lights/diffuse.rs b/src/lights/diffuse.rs index 9b9552c..e3bee4d 100644 --- a/src/lights/diffuse.rs +++ b/src/lights/diffuse.rs @@ -101,7 +101,7 @@ pub fn create( // Upload alpha texture to GPU and check for null texture let alpha_ptr = arena.upload(alpha); - let light_type = match unsafe { alpha_ptr.as_ref() } { + let light_type = match alpha_ptr.get().unwrap() { GPUFloatTexture::Constant(t) if t.evaluate(&TextureEvalContext::default()) == 0.0 => { LightType::DeltaPosition } diff --git a/src/materials/conductor.rs b/src/materials/conductor.rs index 60f16f7..e2231bc 100644 --- a/src/materials/conductor.rs +++ b/src/materials/conductor.rs @@ -1,20 +1,68 @@ use crate::core::image::HostImage; use crate::core::material::CreateMaterial; -// use crate::core::scattering::TrowbridgeReitzDistribution; +use crate::core::texture::SpectrumTexture; +use crate::spectra::data::get_named_spectrum; use crate::utils::TextureParameterDictionary; -use anyhow::Result; +use crate::{Arena, ArenaUpload, FileLoc}; +use anyhow::{bail, Result}; +use shared::core::texture::SpectrumType; use shared::core::material::Material; use shared::materials::ConductorMaterial; +use shared::textures::SpectrumConstantTexture; +use std::collections::HashMap; use std::sync::Arc; impl CreateMaterial for ConductorMaterial { fn create( - _parameters: &TextureParameterDictionary, - _normal_map: Option>, - _named_materials: &std::collections::HashMap, - _loc: &crate::utils::FileLoc, - _arena: &crate::Arena, + parameters: &TextureParameterDictionary, + normal_map: Option>, + _named_materials: &HashMap, + loc: &FileLoc, + arena: &Arena, ) -> Result { - todo!() + let eta = parameters.get_spectrum_texture_or_null("eta", SpectrumType::Unbounded); + let k = parameters.get_spectrum_texture_or_null("k", SpectrumType::Unbounded); + let reflectance = + parameters.get_spectrum_texture_or_null("reflectance", SpectrumType::Albedo); + let (eta, k) = if reflectance.is_some() { + if eta.is_some() || k.is_some() { + bail!( + "{}: For the coated conductor material, both \"reflectance\" \ + and \"eta\"/\"k\" cannot be provided simultaneously.", + loc + ); + } + (None, None) + } else { + let eta = eta.unwrap_or_else(|| { + let s = get_named_spectrum("metal-Cu-eta").expect("Missing copper spectrum"); + Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) + }); + let k = k.unwrap_or_else(|| { + let s = get_named_spectrum("metal-Cu-k").expect("Missing copper spectrum"); + Arc::new(SpectrumTexture::Constant(SpectrumConstantTexture::new(s))) + }); + (Some(eta), Some(k)) + }; + + let u_roughness = parameters.get_float_texture("roughness", 0.)?; + let v_roughness = parameters.get_float_texture("roughness", 0.)?; + let displacement = parameters.get_float_texture_or_null("displacement")?; + let remap_roughness = parameters.get_one_bool("remaproughness", true)?; + eprintln!("ConductorMaterial::create eta={:?} k={:?} reflectance={:?}", eta.is_some(), k.is_some(), reflectance.is_some()); + + let material = Self::new( + arena.upload(normal_map), + arena.upload(reflectance), + arena.upload(eta), + arena.upload(k), + arena.upload(u_roughness), + arena.upload(v_roughness), + arena.upload(displacement), + remap_roughness, + ); + + arena.alloc(material); + Ok(Material::Conductor(material)) } } diff --git a/src/textures/scaled.rs b/src/textures/scaled.rs index 9adc5a7..97c25de 100644 --- a/src/textures/scaled.rs +++ b/src/textures/scaled.rs @@ -3,8 +3,9 @@ use crate::core::texture::{FloatTextureTrait, SpectrumTextureTrait}; use crate::utils::{FileLoc, TextureParameterDictionary}; use crate::Arena; use anyhow::Result; +use shared::core::spectrum::Spectrum; use shared::core::texture::{SpectrumType, TextureEvalContext}; -use shared::spectra::{SampledSpectrum, SampledWavelengths}; +use shared::spectra::{SampledSpectrum, SampledWavelengths, ConstantSpectrum}; use shared::utils::Transform; use shared::Float; use std::sync::Arc; @@ -70,11 +71,32 @@ pub struct SpectrumScaledTexture { impl CreateSpectrumTexture for SpectrumScaledTexture { fn create( _render_from_texture: Transform, - _parameters: TextureParameterDictionary, - _spectrum_type: SpectrumType, + parameters: TextureParameterDictionary, + spectrum_type: SpectrumType, _loc: FileLoc, ) -> Result { - todo!() + let one = Spectrum::Constant(ConstantSpectrum::new(1.0)); + let tex = parameters + .get_spectrum_texture("tex", Some(one), spectrum_type) + .ok_or_else(|| anyhow::anyhow!("{}: missing \"tex\" parameter", _loc))?; + let scale = parameters.get_float_texture("scale", 1.0)?; + + if let FloatTexture::Constant(ref cscale) = *scale { + let cs = cscale.value; + if cs == 1.0 { + return Ok((*tex).clone()); + } + if let SpectrumTexture::Image(ref image) = *tex { + let mut image_copy = image.clone(); + image_copy.base.multiply_scale(cs); + return Ok(SpectrumTexture::Image(image_copy)); + } + } + + Ok(SpectrumTexture::Scaled(SpectrumScaledTexture { + tex, + scale, + })) } } diff --git a/src/utils/loopsubdiv.rs b/src/utils/loopsubdiv.rs index 5c240be..39af554 100644 --- a/src/utils/loopsubdiv.rs +++ b/src/utils/loopsubdiv.rs @@ -1,533 +1,472 @@ -use crate::utils::backend::GpuAllocator; +use crate::Arena; use shared::core::geometry::{Normal3f, Point3f, Vector3f}; -use shared::{Float, Ptr}; +use shared::shapes::TriangleMesh; +use shared::{Float, Transform}; use std::collections::HashMap; -pub struct SDVertex { - pub p: Point3f, - pub start_face: Ptr, - pub child: Ptr, - pub regular: bool, - pub boundary: bool, +const NULL: u32 = u32::MAX; + +#[repr(C)] +struct SDVertex { + p: Point3f, + start_face: u32, + child: u32, + regular: bool, + boundary: bool, } -impl SDVertex { - pub fn new(p: Point3f) -> Self { - Self { - p, - start_face: Ptr::null(), - child: Ptr::null(), - regular: false, - boundary: false, - } - } -} - -pub struct SDFace { - pub v: [Ptr; 3], - pub f: [Ptr; 3], - pub children: [Ptr; 4], +#[repr(C)] +struct SDFace { + v: [u32; 3], + f: [u32; 3], + children: [u32; 4], } impl SDFace { - pub fn new(v0: Ptr, v1: Ptr, v2: Ptr) -> Self { - Self { - v: [v0, v1, v2], - f: [Ptr::null(); 3], - children: [Ptr::null(); 4], - } - } - - pub fn vnum(&self, vert: Ptr) -> usize { + fn vnum(&self, vert: u32) -> usize { for i in 0..3 { if self.v[i] == vert { return i; } } - panic!("Basic logic error in SDFace::vnum()"); + panic!("SDFace::vnum: vertex {} not in face", vert); } - pub fn next_face(&self, vert: Ptr) -> Ptr { - self.f[self.vnum(vert)] - } + fn next_face(&self, vert: u32) -> u32 { self.f[self.vnum(vert)] } + fn prev_face(&self, vert: u32) -> u32 { self.f[prev(self.vnum(vert))] } + fn next_vert(&self, vert: u32) -> u32 { self.v[next(self.vnum(vert))] } + fn prev_vert(&self, vert: u32) -> u32 { self.v[prev(self.vnum(vert))] } - pub fn prev_face(&self, vert: Ptr) -> Ptr { - self.f[prev(self.vnum(vert))] - } - - pub fn next_vert(&self, vert: Ptr) -> Ptr { - self.v[next(self.vnum(vert))] - } - - pub fn prev_vert(&self, vert: Ptr) -> Ptr { - self.v[prev(self.vnum(vert))] - } - - pub fn other_vert(&self, v0: Ptr, v1: Ptr) -> Ptr { + fn other_vert(&self, v0: u32, v1: u32) -> u32 { for i in 0..3 { if self.v[i] != v0 && self.v[i] != v1 { return self.v[i]; } } - panic!("Basic logic error in SDFace::other_vert()"); + panic!("SDFace::other_vert: face doesn't contain both vertices"); } } -const fn next(i: usize) -> usize { - (i + 1) % 3 -} +#[inline(always)] +const fn next(i: usize) -> usize { (i + 1) % 3 } -const fn prev(i: usize) -> usize { - (i + 2) % 3 -} +#[inline(always)] +const fn prev(i: usize) -> usize { (i + 2) % 3 } -fn canonical_key(v0: Ptr, v1: Ptr) -> (usize, usize) { - let a = v0.as_ptr() as usize; - let b = v1.as_ptr() as usize; - if a <= b { - (a, b) - } else { - (b, a) +#[derive(Hash, Eq, PartialEq, Copy, Clone)] +struct EdgeKey(u32, u32); + +impl EdgeKey { + fn new(a: u32, b: u32) -> Self { + if a <= b { Self(a, b) } else { Self(b, a) } } } fn beta(valence: usize) -> Float { - if valence == 3 { - 3.0 / 16.0 - } else { - 3.0 / (8.0 * valence as Float) - } + if valence == 3 { 3.0 / 16.0 } else { 3.0 / (8.0 * valence as Float) } } fn loop_gamma(valence: usize) -> Float { 1.0 / (valence as Float + 3.0 / (8.0 * beta(valence))) } -fn valence(v: Ptr) -> usize { - let vertex = v; - let start = vertex.start_face; - if start.is_null() { - return 0; - } +fn valence(verts: &[SDVertex], faces: &[SDFace], vi: u32) -> usize { + let v = &verts[vi as usize]; + let start = v.start_face; + debug_assert_ne!(start, NULL); - if !vertex.boundary { - let mut nf = 1; - let mut f = start; - loop { - f = f.next_face(v); - if f.is_null() { - panic!("interior vertex with broken face loop"); - } - if f == start { - break; - } + if !v.boundary { + let mut nf = 1usize; + let mut fi = faces[start as usize].next_face(vi); + while fi != start { + assert_ne!(fi, NULL, "interior vertex with broken face loop"); nf += 1; + fi = faces[fi as usize].next_face(vi); } nf } else { - let mut nf = 1; - let mut f = start; - while !f.next_face(v).is_null() { - f = f.next_face(v); + let mut nf = 1usize; + let mut fi = faces[start as usize].next_face(vi); + while fi != NULL { nf += 1; + fi = faces[fi as usize].next_face(vi); } - f = start; - while !f.prev_face(v).is_null() { - f = f.prev_face(v); + fi = faces[start as usize].prev_face(vi); + while fi != NULL { nf += 1; + fi = faces[fi as usize].prev_face(vi); } nf + 1 } } -fn one_ring(v: Ptr, out: &mut [Point3f]) { - let vertex = v; - if !vertex.boundary { - let start = vertex.start_face; - let mut face = start; - let mut i = 0; +fn one_ring(verts: &[SDVertex], faces: &[SDFace], vi: u32) -> Vec { + let v = &verts[vi as usize]; + let mut ring = Vec::new(); + + if !v.boundary { + let start = v.start_face; + let mut fi = start; loop { - out[i] = face.next_vert(v).p; - i += 1; - face = face.next_face(v); - if face == start { - break; - } + ring.push(verts[faces[fi as usize].next_vert(vi) as usize].p); + fi = faces[fi as usize].next_face(vi); + if fi == start { break; } } } else { - let start = vertex.start_face; - let mut face = start; - while !face.next_face(v).is_null() { - face = face.next_face(v); - } - out[0] = face.next_vert(v).p; - let mut i = 1; + // Walk forward to the last face in the fan + let mut fi = v.start_face; loop { - out[i] = face.prev_vert(v).p; - i += 1; - let pf = face.prev_face(v); - if pf.is_null() { - break; - } - face = pf; + let nf = faces[fi as usize].next_face(vi); + if nf == NULL { break; } + fi = nf; + } + ring.push(verts[faces[fi as usize].next_vert(vi) as usize].p); + loop { + ring.push(verts[faces[fi as usize].prev_vert(vi) as usize].p); + let pf = faces[fi as usize].prev_face(vi); + if pf == NULL { break; } + fi = pf; } } + ring } -fn weight_one_ring(v: Ptr, beta: Float) -> Point3f { - let valence = valence(v); - let mut p_ring = vec![Point3f::default(); valence]; - one_ring(v, &mut p_ring); - - let vertex = v; - let mut p = vertex.p * (1.0 - valence as Float * beta); - for i in 0..valence { - p += p_ring[i] * beta; +fn weight_one_ring(verts: &[SDVertex], faces: &[SDFace], vi: u32, beta: Float) -> Point3f { + let ring = one_ring(verts, faces, vi); + let val = ring.len(); + let mut acc = (1.0 - val as Float * beta) * Vector3f::from(verts[vi as usize].p); + for r in &ring { + acc += beta * Vector3f::from(*r); } - p + Point3f::from(acc) } -fn weight_boundary(v: Ptr, beta: Float) -> Point3f { - let valence = valence(v); - let mut p_ring = vec![Point3f::default(); valence]; - one_ring(v, &mut p_ring); - - let vertex = v; - let mut p = vertex.p * (1.0 - 2.0 * beta); - p += p_ring[0] * beta; - p += p_ring[valence - 1] * beta; - p +fn weight_boundary(verts: &[SDVertex], faces: &[SDFace], vi: u32, beta: Float) -> Point3f { + let ring = one_ring(verts, faces, vi); + let val = ring.len(); + let acc = (1.0 - 2.0 * beta) * Vector3f::from(verts[vi as usize].p) + + beta * Vector3f::from(ring[0]) + + beta * Vector3f::from(ring[val - 1]); + Point3f::from(acc) } -pub fn loop_subdivide( - arena: &Arena, +pub fn loop_subdivide( + _arena: &Arena, render_from_object: &Transform, reverse_orientation: bool, n_levels: usize, vertex_indices: &[usize], - p: &[Point3f], + points: &[Point3f], ) -> TriangleMesh { - let mut vertices: Vec> = Vec::with_capacity(p.len()); - for &pos in p { - vertices.push(arena.alloc(SDVertex::new(pos))); - } + let n_faces_init = vertex_indices.len() / 3; - let n_faces = vertex_indices.len() / 3; - let mut faces: Vec> = Vec::with_capacity(n_faces); - for i in 0..n_faces { - let v0 = vertices[vertex_indices[3 * i]]; - let v1 = vertices[vertex_indices[3 * i + 1]]; - let v2 = vertices[vertex_indices[3 * i + 2]]; - faces.push(arena.alloc(SDFace::new(v0, v1, v2))); - } + let mut verts: Vec = points + .iter() + .map(|&p| SDVertex { + p, + start_face: NULL, + child: NULL, + regular: false, + boundary: false, + }) + .collect(); - for &f in &faces { - let face = f; + let mut faces: Vec = (0..n_faces_init) + .map(|i| { + let base = i * 3; + SDFace { + v: [ + vertex_indices[base] as u32, + vertex_indices[base + 1] as u32, + vertex_indices[base + 2] as u32, + ], + f: [NULL; 3], + children: [NULL; 4], + } + }) + .collect(); + + for fi in 0..n_faces_init { for j in 0..3 { - face.v[j].get_mut().unwrap().start_face = f; + verts[faces[fi].v[j] as usize].start_face = fi as u32; } } - let mut edges: HashMap<(usize, usize), (Ptr, usize)> = HashMap::new(); - for &f in &faces { - let face = f; - for edge_num in 0..3 { - let v0 = face.v[edge_num]; - let v1 = face.v[next(edge_num)]; - let key = canonical_key(v0, v1); + { + let mut edge_map: HashMap = + HashMap::with_capacity(n_faces_init * 3 / 2); - if let Some(&(first_face, first_edge_num)) = edges.get(&key) { - first_face.get_mut().unwrap().f[first_edge_num] = f; - f.get_mut().f[edge_num] = first_face; - edges.remove(&key); - } else { - edges.insert(key, (f, edge_num)); - } - } - } + for fi in 0..n_faces_init { + for edge_num in 0..3 { + let v0 = faces[fi].v[edge_num]; + let v1 = faces[fi].v[next(edge_num)]; + let key = EdgeKey::new(v0, v1); - for &v in &vertices { - let vertex = v.get_mut().unwrap(); - let start = vertex.start_face; - let mut f = start; - let mut is_boundary = false; - loop { - let nf = f.next_face(v); - if nf.is_null() { - is_boundary = true; - break; - } - f = nf; - if f == start { - break; - } - } - - let val = valence(v); - vertex.boundary = is_boundary; - vertex.regular = if !is_boundary && val == 6 { - true - } else if is_boundary && val == 4 { - true - } else { - false - }; - } - - // -- Subdivision levels --------------------------------------------------- - let mut f = faces; - let mut v = vertices; - - for _ in 0..n_levels { - let mut new_faces: Vec> = Vec::new(); - let mut new_vertices: Vec> = Vec::new(); - - // Allocate vertex children - for &vertex_ptr in &v { - let vertex = vertex_ptr; - let child = arena.alloc(SDVertex { - p: Point3f::default(), - start_face: Ptr::null(), - child: Ptr::null(), - regular: vertex.regular, - boundary: vertex.boundary, - }); - vertex_ptr.get_mut().unwrap().child = child; - new_vertices.push(child); - } - - for &face_ptr in &f { - for k in 0..4 { - let child = arena.alloc(SDFace::new(Ptr::null(), Ptr::null(), Ptr::null())); - face_ptr.get_mut().unwrap().children[k] = child; - new_faces.push(child); - } - } - - for &vertex_ptr in &v { - let vertex = vertex_ptr; - let child = vertex.child; - let new_p = if !vertex.boundary { - if vertex.regular { - weight_one_ring(vertex_ptr, 1.0 / 16.0) + if let Some(&(other_fi, other_edge)) = edge_map.get(&key) { + faces[other_fi as usize].f[other_edge] = fi as u32; + faces[fi].f[edge_num] = other_fi; + edge_map.remove(&key); } else { - let val = valence(vertex_ptr); - weight_one_ring(vertex_ptr, beta(val)) + edge_map.insert(key, (fi as u32, edge_num)); + } + } + } + } + + // Classify vertices as boundary/interior and regular/extraordinary + for vi in 0..verts.len() { + let start = verts[vi].start_face; + let mut fi = start; + let is_boundary = loop { + let nf = faces[fi as usize].next_face(vi as u32); + if nf == NULL { break true; } + fi = nf; + if fi == start { break false; } + }; + verts[vi].boundary = is_boundary; + let val = valence(&verts, &faces, vi as u32); + verts[vi].regular = if !is_boundary { val == 6 } else { val == 4 }; + } + + let mut live_verts: Vec = (0..verts.len() as u32).collect(); + let mut live_faces: Vec = (0..faces.len() as u32).collect(); + + for _level in 0..n_levels { + let old_verts = live_verts.clone(); + let old_faces = live_faces.clone(); + let mut new_verts: Vec = Vec::with_capacity(old_verts.len() + old_faces.len() * 3); + let mut new_faces: Vec = Vec::with_capacity(old_faces.len() * 4); + + // Allocate child (even) vertices — one per existing vertex + for &vi in &old_verts { + let child_id = verts.len() as u32; + verts.push(SDVertex { + p: Point3f::default(), + start_face: NULL, + child: NULL, + regular: verts[vi as usize].regular, + boundary: verts[vi as usize].boundary, + }); + verts[vi as usize].child = child_id; + new_verts.push(child_id); + } + + // Allocate 4 child faces per existing face + for &fi in &old_faces { + let base = faces.len() as u32; + for _ in 0..4 { + faces.push(SDFace { + v: [NULL; 3], + f: [NULL; 3], + children: [NULL; 4], + }); + } + faces[fi as usize].children = [base, base + 1, base + 2, base + 3]; + for k in 0..4u32 { + new_faces.push(base + k); + } + } + + // Reposition even vertices using the Loop stencil + for &vi in &old_verts { + let child = verts[vi as usize].child; + let new_p = if !verts[vi as usize].boundary { + if verts[vi as usize].regular { + weight_one_ring(&verts, &faces, vi, 1.0 / 16.0) + } else { + let val = valence(&verts, &faces, vi); + weight_one_ring(&verts, &faces, vi, beta(val)) } } else { - weight_boundary(vertex_ptr, 1.0 / 8.0) + weight_boundary(&verts, &faces, vi, 1.0 / 8.0) }; - child.get_mut().unwrap().p = new_p; + verts[child as usize].p = new_p; } - let mut edge_verts: HashMap<(usize, usize), Ptr> = HashMap::new(); - for &face_ptr in &f { - let face = face_ptr; + // Create odd (edge) vertices + let mut edge_verts: HashMap = HashMap::new(); + + for &fi in &old_faces { for k in 0..3 { - let v0 = face.v[k]; - let v1 = face.v[next(k)]; - let key = canonical_key(v0, v1); + let v0 = faces[fi as usize].v[k]; + let v1 = faces[fi as usize].v[next(k)]; + let key = EdgeKey::new(v0, v1); - if !edge_verts.contains_key(&key) { - let is_boundary = face.f[k].is_null(); - let start_face = face.children[3]; + if edge_verts.contains_key(&key) { continue; } - let new_p = if is_boundary { - let p0 = v0.p; - let p1 = v1.p; - p0 * 0.5 + p1 * 0.5 - } else { - let p0 = v0.p; - let p1 = v1.p; - let p2 = face.other_vert(v0, v1).p; - let f2 = face.f[k]; - let p3 = f2.other_vert(v0, v1).p; - p0 * (3.0 / 8.0) + p1 * (3.0 / 8.0) + p2 * (1.0 / 8.0) + p3 * (1.0 / 8.0) - }; + let is_boundary = faces[fi as usize].f[k] == NULL; + let new_vi = verts.len() as u32; - let vert = arena.alloc(SDVertex { - p: new_p, - start_face, - child: Ptr::null(), - regular: true, - boundary: is_boundary, - }); - edge_verts.insert(key, vert); - new_vertices.push(vert); - } + let new_p = if is_boundary { + Point3f::from( + 0.5 * Vector3f::from(verts[v0 as usize].p) + + 0.5 * Vector3f::from(verts[v1 as usize].p), + ) + } else { + let neighbor_fi = faces[fi as usize].f[k]; + let other_a = faces[fi as usize].other_vert(v0, v1); + let other_b = faces[neighbor_fi as usize].other_vert(v0, v1); + Point3f::from( + (3.0 / 8.0) * Vector3f::from(verts[v0 as usize].p) + + (3.0 / 8.0) * Vector3f::from(verts[v1 as usize].p) + + (1.0 / 8.0) * Vector3f::from(verts[other_a as usize].p) + + (1.0 / 8.0) * Vector3f::from(verts[other_b as usize].p), + ) + }; + + verts.push(SDVertex { + p: new_p, + start_face: faces[fi as usize].children[3], + child: NULL, + regular: true, + boundary: is_boundary, + }); + new_verts.push(new_vi); + edge_verts.insert(key, new_vi); } } - for &vertex_ptr in &v { - let vertex = vertex_ptr; - let start_face = vertex.start_face; - let vert_num = start_face.vnum(vertex_ptr); - let child = vertex.child; - let child_start_face = start_face.children[vert_num]; - child.get_mut().unwrap().start_face = child_start_face; + // Snapshot parent arrays before writing into children to avoid + // reading stale data from the same Vec. + + // Even vertex start_face pointers + for &vi in &old_verts { + let sf = verts[vi as usize].start_face; + let vnum = faces[sf as usize].vnum(vi); + let child = verts[vi as usize].child; + verts[child as usize].start_face = faces[sf as usize].children[vnum]; } - for &face_ptr in &f { - let face = face_ptr; - // Copy the data we need so we don't hold a borrow into the parent - // while we mutably borrow its children. - let face_children = face.children; - let face_f = face.f; - let face_v = face.v; + // Face neighbor pointers + for &fi in &old_faces { + let children = faces[fi as usize].children; + let face_f = faces[fi as usize].f; + let face_v = faces[fi as usize].v; - for j in 0..3 { - let c3 = face_children[3]; - let c_j = face_children[j]; - let c_next_j = face_children[next(j)]; + for j in 0..3usize { + // Center child (children[3]) neighbors the three corner children + faces[children[3] as usize].f[j] = children[next(j)]; + faces[children[j] as usize].f[next(j)] = children[3]; - // Sibling links - if !c3.is_null() { - c3.get_mut().unwrap().f[j] = c_next_j; - } - if !c_j.is_null() { - c_j.get_mut().unwrap().f[next(j)] = c3; - } - - // Neighbor links + // Corner child j's f[j] = neighbor face's child at the shared vertex let f2 = face_f[j]; - if !f2.is_null() { - let f2_vnum = f2.vnum(face_v[j]); - let f2_child = f2.children[f2_vnum]; - if !c_j.is_null() { - c_j.get_mut().unwrap().f[j] = f2_child; - } - } else if !c_j.is_null() { - c_j.get_mut().unwrap().f[j] = Ptr::null(); - } + faces[children[j] as usize].f[j] = if f2 != NULL { + let vnum2 = faces[f2 as usize].vnum(face_v[j]); + faces[f2 as usize].children[vnum2] + } else { + NULL + }; - let f2_prev = face_f[prev(j)]; - if !f2_prev.is_null() { - let f2_vnum = f2_prev.vnum(face_v[j]); - let f2_child = f2_prev.children[f2_vnum]; - if !c_j.is_null() { - c_j.get_mut().unwrap().f[prev(j)] = f2_child; - } - } else if !c_j.is_null() { - c_j.get_mut().unwrap().f[prev(j)] = Ptr::null(); - } + let f2 = face_f[prev(j)]; + faces[children[j] as usize].f[prev(j)] = if f2 != NULL { + let vnum2 = faces[f2 as usize].vnum(face_v[j]); + faces[f2 as usize].children[vnum2] + } else { + NULL + }; } } - // Update face vertex pointers - for &face_ptr in &f { - let face = face_ptr; - let face_v = face.v; - let face_children = face.children; + // Face vertex pointers + for &fi in &old_faces { + let children = faces[fi as usize].children; + let face_v = faces[fi as usize].v; - for j in 0..3 { - let v_j_child = face_v[j].child; - let c_j = face_children[j]; - if !c_j.is_null() { - c_j.get_mut().unwrap().v[j] = v_j_child; - } + for j in 0..3usize { + let child_v = verts[face_v[j] as usize].child; + let edge_v = edge_verts[&EdgeKey::new(face_v[j], face_v[next(j)])]; - let key = canonical_key(face_v[j], face_v[next(j)]); - let vert = edge_verts[&key]; - - let c_next_j = face_children[next(j)]; - let c3 = face_children[3]; - - if !c_j.is_null() { - c_j.get_mut().unwrap().v[next(j)] = vert; - } - if !c_next_j.is_null() { - c_next_j.get_mut().unwrap().v[j] = vert; - } - if !c3.is_null() { - c3.get_mut().unwrap().v[j] = vert; - } + faces[children[j] as usize].v[j] = child_v; + faces[children[j] as usize].v[next(j)] = edge_v; + faces[children[next(j)] as usize].v[j] = edge_v; + faces[children[3] as usize].v[j] = edge_v; } } - // Prepare for next level - f = new_faces; - v = new_vertices; + live_verts = new_verts; + live_faces = new_faces; } - let mut p_limit: Vec = Vec::with_capacity(v.len()); - for &vertex_ptr in &v { - let vertex = vertex_ptr; - if vertex.boundary { - p_limit.push(weight_boundary(vertex_ptr, 1.0 / 5.0)); + let n_final = live_verts.len(); + let mut p_limit: Vec = Vec::with_capacity(n_final); + + for &vi in &live_verts { + if verts[vi as usize].boundary { + p_limit.push(weight_boundary(&verts, &faces, vi, 1.0 / 5.0)); } else { - let val = valence(vertex_ptr); - p_limit.push(weight_one_ring(vertex_ptr, loop_gamma(val))); + let val = valence(&verts, &faces, vi); + p_limit.push(weight_one_ring(&verts, &faces, vi, loop_gamma(val))); } } - for (i, &vertex_ptr) in v.iter().enumerate() { - vertex_ptr.get_mut().unwrap().p = p_limit[i]; + for (i, &vi) in live_verts.iter().enumerate() { + verts[vi as usize].p = p_limit[i]; } - let mut ns: Vec = Vec::with_capacity(v.len()); - let mut p_ring: Vec = Vec::with_capacity(16); + let pi = std::f64::consts::PI as Float; + let mut normals: Vec = Vec::with_capacity(n_final); - for &vertex_ptr in &v { - let vertex = vertex_ptr; - let valence = valence(vertex_ptr); - p_ring.resize(valence, Point3f::default()); - one_ring(vertex_ptr, &mut p_ring[..valence]); + for &vi in &live_verts { + let ring = one_ring(&verts, &faces, vi); + let val = ring.len(); + let vert_p = verts[vi as usize].p; - let mut s = Vector3f::default(); - let mut t = Vector3f::default(); - - if !vertex.boundary { - for j in 0..valence { - let angle = 2.0 * std::f64::consts::PI * j as f64 / valence as f64; - s += Vector3f::from(p_ring[j]) * angle.cos() as Float; - t += Vector3f::from(p_ring[j]) * angle.sin() as Float; + let (s, t) = if !verts[vi as usize].boundary { + let mut s = Vector3f::default(); + let mut t = Vector3f::default(); + for j in 0..val { + let angle = 2.0 * pi * j as Float / val as Float; + s += angle.cos() * Vector3f::from(ring[j]); + t += angle.sin() * Vector3f::from(ring[j]); } + (s, t) } else { - s = Vector3f::from(p_ring[valence - 1] - p_ring[0]); - if valence == 2 { - t = Vector3f::from(p_ring[0] + p_ring[1] - vertex_ptr.p * 2.0); - } else if valence == 3 { - t = Vector3f::from(p_ring[1] - vertex_ptr.p); - } else if valence == 4 { - t = Vector3f::from( - p_ring[0] * -1.0 - + p_ring[1] * 2.0 - + p_ring[2] * 2.0 - + p_ring[3] * -1.0 - + vertex_ptr.p * -2.0, - ); + let s = Vector3f::from(ring[val - 1]) - Vector3f::from(ring[0]); + let t = if val == 2 { + Vector3f::from(ring[0]) + Vector3f::from(ring[1]) + - 2.0 * Vector3f::from(vert_p) + } else if val == 3 { + Vector3f::from(ring[1]) - Vector3f::from(vert_p) + } else if val == 4 { + -1.0 * Vector3f::from(ring[0]) + + 2.0 * Vector3f::from(ring[1]) + + 2.0 * Vector3f::from(ring[2]) + - 1.0 * Vector3f::from(ring[3]) + - 2.0 * Vector3f::from(vert_p) } else { - let theta = std::f64::consts::PI / (valence - 1) as f64; - t = Vector3f::from(p_ring[0] + p_ring[valence - 1].into()) * theta.sin() as Float; - for k in 1..(valence - 1) { - let wt = (2.0 * theta.cos() - 2.0) * ((k as f64) * theta).sin(); - t += Vector3f::from(p_ring[k]) * wt as Float; + let theta = pi / (val - 1) as Float; + let mut t = theta.sin() + * (Vector3f::from(ring[0]) + Vector3f::from(ring[val - 1])); + for k in 1..(val - 1) { + let wt = (2.0 * theta.cos() - 2.0) * (k as Float * theta).sin(); + t += wt * Vector3f::from(ring[k]); } - t = -t; - } - } - ns.push(Normal3f::from(Vector3f::cross(s, t))); + -t + }; + (s, t) + }; + + normals.push(Normal3f::from(s.cross(t))); } - let ntris = f.len(); - let mut verts: Vec = Vec::with_capacity(3 * ntris); - let mut used_verts: HashMap, usize> = HashMap::new(); - for (i, &vertex_ptr) in v.iter().enumerate() { - used_verts.insert(vertex_ptr, i); + let mut vert_remap: HashMap = HashMap::with_capacity(n_final); + for (i, &vi) in live_verts.iter().enumerate() { + vert_remap.insert(vi, i as i32); } - for &face_ptr in &f { - let face = face_ptr; + + let mut indices: Vec = Vec::with_capacity(live_faces.len() * 3); + for &fi in &live_faces { for j in 0..3 { - verts.push(used_verts[&face.v[j]]); + indices.push(vert_remap[&faces[fi as usize].v[j]]); } } TriangleMesh::new( render_from_object, reverse_orientation, - &verts, + &indices, &p_limit, + &normals, &[], - &ns, &[], &[], ) diff --git a/src/utils/parameters.rs b/src/utils/parameters.rs index 39c55f4..3a3ffda 100644 --- a/src/utils/parameters.rs +++ b/src/utils/parameters.rs @@ -2,7 +2,7 @@ use crate::core::spectrum::SPECTRUM_FILE_CACHE; use crate::core::texture::{FloatTexture, SpectrumTexture}; use crate::spectra::data::get_named_spectrum; use crate::spectra::piecewise::ReadFromFile; -use crate::utils::FileLoc; +use crate::utils::{FileLoc, resolve_filename}; use anyhow::{bail, Result}; use shared::core::color::RGB; use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f}; @@ -597,7 +597,13 @@ impl ParameterDictionary { match param.type_name.as_str() { "rgb" | "color" => self.extract_rgb_spectrum(param, spectrum_type), "blackbody" => self.extract_file_spectrum(param), - "spectrum" => self.extract_sampled_spectrum(param), + "spectrum" => { + if !param.strings.is_empty() { + self.extract_file_spectrum(param) + } else { + self.extract_sampled_spectrum(param) + } + } _ => Vec::new(), } } @@ -695,7 +701,10 @@ impl ParameterDictionary { .map(|s| { get_named_spectrum(s) .ok_or(()) - .or_else(|_| read_spectrum_from_file(s).map_err(|_| ())) + .or_else(|_| { + let resolved = resolve_filename(s); + read_spectrum_from_file(&resolved).map_err(|_| ()) + }) .unwrap_or_else(|_| panic!("{}: {}: unable to read spectrum", param.loc, s)) }) .collect() diff --git a/src/utils/parser.rs b/src/utils/parser.rs index f97fe80..57a3679 100644 --- a/src/utils/parser.rs +++ b/src/utils/parser.rs @@ -7,9 +7,9 @@ use std::io::{self, Read}; use std::path::{Path, PathBuf}; use std::sync::Arc; -use crate::Arena; use crate::utils::error::FileLoc; use crate::utils::parameters::{ParameterDictionary, ParsedParameter, ParsedParameterVector}; +use crate::Arena; use shared::Float; pub trait ParserTarget { @@ -886,9 +886,11 @@ impl<'a> SceneParser<'a> { let mut val_type = match type_name { "integer" => ValType::Int, "bool" => ValType::Bool, - "float" | "point" | "vector" | "normal" | "color" | "spectrum" | "rgb" - | "blackbody" => ValType::Float, + "float" | "point" | "vector" | "normal" | "color" | "rgb" | "blackbody" => { + ValType::Float + } "string" | "texture" => ValType::String, + "spectrum" => ValType::Unknown, _ => ValType::Unknown, };