use crate::utils::{FileLoc, TextureParameterDictionary}; use shared::core::texture::{SpectrumType, TextureEvalContext}; use shared::spectra::ColorEncoding; use shared::spectra::color::RGB; use ptex::Cache; use ptex_sys::ffi; use std::sync::OnceLock; static PTEX_CACHE: OnceLock = OnceLock::new(); fn get_ptex_cache() -> &'static Cache { PTEX_CACHE.get_or_init(|| { let max_files = 100; let max_mem = 1 << 32; let premultiply = true; Cache::new(max_files, max_mem, premultiply) }) } #[derive(Debug, Clone)] pub struct PtexTextureBase { pub valid: bool, pub filename: String, pub encoding: ColorEncoding, pub scale: Float, } impl PtexTextureBase { pub fn new(filename: String, encoding: ColorEncoding, scale: Float) -> Self { let cache = get_ptex_cache(); // Attempt to get the texture to verify it exists and is valid let (valid, num_channels) = match cache.get(&filename) { Ok(tex) => { let nc = tex.num_channels(); (nc == 1 || nc == 3, nc) } Err(e) => { log::error!("Ptex Error for {}: {}", filename, e); (false, 0) } }; if !valid && num_channels != 0 { log::error!( "{}: only 1 and 3 channel ptex textures are supported", filename ); } Self { filename, encoding, scale, valid, } } pub fn sample_texture(ctx: &TextureEvalContext) -> Option { if !self.valid { return None; } let mut result = [0.0; 3]; let nc = self.ptex_handle.eval( &mut result, ctx.face_index, ctx.uv[0], ctx.uv[1], ctx.dudx, ctx.dvdx, ctx.dudy, ctx.dvdy, ); let cache = get_ptex_cache(); let texture = match cache.get(&self.filename) { Ok(t) => t, Err(e) => { log::error!("Ptex cache lookup failed for {}: {}", self.filename, e); return None; } }; let nc = texture.num_channels(); let mut result = [0.0f32; 4]; unsafe { let opts = ffi::PtexFilter_Options { filter: ffi::FilterType::f_bspline, lerp: 1, sharpness: 0.0, noedgeblend: 0, __structSize: std::mem::size_of::() as i32, }; // Get the raw filter from the low-level FFI // Assuming your 'texture' can provide the raw C++ pointer let filter_ptr = ffi::PtexFilter_getFilter(texture.as_ptr(), &opts); if filter_ptr.is_null() { return None; } // Evaluate (*filter_ptr).eval( result.as_mut_ptr(), 0, // first channel texture.num_channels(), ctx.face_index as i32, ctx.uv[0], ctx.uv[1], ctx.dudx, ctx.dvdx, ctx.dudy, ctx.dvdy, ); // Crucial: Manually release the C++ object (*filter_ptr).release(); } let mut rgb = RGB::new(result[0], result[1], result[2]); if self.encoding != ColorEncoding::Linear { // Convert to 8-bit, process, and convert back let r8 = (rgb.r * 255.0 + 0.5).clamp(0.0, 255.0) as u8; let g8 = (rgb.g * 255.0 + 0.5).clamp(0.0, 255.0) as u8; let b8 = (rgb.b * 255.0 + 0.5).clamp(0.0, 255.0) as u8; rgb = self.encoding.to_linear_rgb([r8, g8, b8]); } Some(rgb * self.scale) } } #[derive(Debug, Clone)] pub struct FloatPtexTexture { pub base: PtexTextureBase, } // impl FloatPtexTexture { // pub fn evaluate(&self, _ctx: &TextureEvalContext) -> Float { // let // // } // } #[derive(Clone, Debug)] pub struct SpectrumPtexTexture { pub base: PtexTextureBase, pub spectrum_type: SpectrumType, } impl SpectrumTextureTrait for SpectrumPtexTexture { fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum { todo!() } }