157 lines
4.3 KiB
Rust
157 lines
4.3 KiB
Rust
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<Cache> = 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<RGB> {
|
|
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::<ffi::PtexFilter_Options>() 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!()
|
|
}
|
|
}
|