pbrt/src/textures/ptex.rs

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!()
}
}