pbrt/src/core/texture.rs
2026-01-22 14:18:57 +00:00

198 lines
6.9 KiB
Rust

use crate::textures::*;
use crate::utils::mipmap::MIPMap;
use crate::utils::mipmap::MIPMapFilterOptions;
use crate::utils::{Arena, FileLoc, TextureParameterDictionary};
use enum_dispatch::enum_dispatch;
use shared::Float;
use shared::core::color::ColorEncoding;
use shared::core::geometry::Vector3f;
use shared::core::image::WrapMode;
use shared::core::texture::{
CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
UVMapping,
};
use shared::spectra::{SampledSpectrum, SampledWavelengths};
use shared::textures::*;
use shared::utils::Transform;
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock};
pub trait FloatTextureTrait {
fn evaluate(&self, ctx: &TextureEvalContext) -> Float;
}
pub trait SpectrumTextureTrait {
fn evaluate(&self, _ctx: &TextureEvalContext, _lambda: &SampledWavelengths) -> SampledSpectrum;
}
#[enum_dispatch(FloatTextureTrait)]
#[derive(Debug, Clone)]
pub enum FloatTexture {
Constant(FloatConstantTexture),
Image(FloatImageTexture),
Mix(FloatMixTexture),
DirectionMix(FloatDirectionMixTexture),
Scaled(FloatScaledTexture),
Bilerp(FloatBilerpTexture),
Checkerboard(FloatCheckerboardTexture),
Dots(FloatDotsTexture),
FBm(FBmTexture),
Ptex(FloatPtexTexture),
Windy(WindyTexture),
Wrinkled(WrinkledTexture),
}
impl FloatTextureTrait for Arc<FloatTexture> {
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
self.as_ref().evaluate(ctx)
}
}
impl FloatTexture {
pub fn create(
name: &str,
render_from_texture: &Transform,
params: &TextureParameterDictionary,
loc: &FileLoc,
arena: &mut Arena,
) -> Result<Self, String> {
match name {
"constant" => {
let tex = FloatConstantTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Constant(tex))
}
"scale" => Ok(FloatScaledTexture::create(
render_from_texture,
params,
loc,
arena,
)),
"mix" => {
let tex = FloatMixTexture::create(render_from_texture, params, loc, arena);
Ok(FloatTexture::Mix(tex))
}
"directionmix" => {
let tex = FloatDirectionMixTexture::create(render_from_texture, params, loc, arena);
Ok(FloatTexture::DirectionMix(tex))
}
"bilerp" => {
let tex = FloatBilerpTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Bilerp(tex))
}
"imagemap" => {
let tex = FloatImageTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Image(tex))
}
"checkerboard" => {
let tex = FloatCheckerboardTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Checkerboard(tex))
}
"dots" => {
let tex = FloatDotsTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Dots(tex))
}
"fbm" => {
let tex = FBmTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::FBm(tex))
}
"wrinkled" => {
let tex = WrinkledTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Wrinkled(tex))
}
"windy" => {
let tex = WindyTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Windy(tex))
}
"ptex" => {
let tex = FloatPtexTexture::create(render_from_texture, params, loc);
Ok(FloatTexture::Ptex(tex))
}
_ => Err(format!("Float texture type '{}' unknown at {}", name, loc)),
}
}
}
#[derive(Clone, Debug)]
#[enum_dispatch(SpectrumTextureTrait)]
pub enum SpectrumTexture {
// RGBConstant(RGBConstantTexture),
// RGBReflectanceConstant(RGBReflectanceConstantTexture),
Constant(SpectrumConstantTexture),
Bilerp(SpectrumBilerpTexture),
Checkerboard(SpectrumCheckerboardTexture),
Image(SpectrumImageTexture),
Marble(MarbleTexture),
Mix(SpectrumMixTexture),
DirectionMix(SpectrumDirectionMixTexture),
Dots(SpectrumDotsTexture),
// Ptex(SpectrumPtexTexture),
Scaled(SpectrumScaledTexture),
}
impl SpectrumTextureTrait for Arc<SpectrumTexture> {
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
self.as_ref().evaluate(ctx, lambda)
}
}
pub trait CreateTextureMapping {
fn create(
params: &TextureParameterDictionary,
render_from_texture: &Transform,
loc: &FileLoc,
) -> Self;
}
impl CreateTextureMapping for TextureMapping2D {
fn create(
params: &TextureParameterDictionary,
render_from_texture: &Transform,
loc: &FileLoc,
) -> Self {
let mtype = params.get_one_string("mapping", "uv");
match mtype.as_str() {
"uv" => {
let su = params.get_one_float("uscale", 1.);
let sv = params.get_one_float("vscale", 1.);
let du = params.get_one_float("udelta", 0.);
let dv = params.get_one_float("vdelta", 0.);
let mapping = UVMapping::new(su, sv, du, dv);
TextureMapping2D::UV(mapping)
}
"spherical" => {
let mapping = SphericalMapping::new(&render_from_texture.inverse());
TextureMapping2D::Spherical(mapping)
}
"cylindrical" => {
let mapping = CylindricalMapping::new(&render_from_texture.inverse());
TextureMapping2D::Cylindrical(mapping)
}
"planar" => {
let vs = params.get_one_vector3f("v1", Vector3f::new(1., 0., 0.));
let vt = params.get_one_vector3f("v2", Vector3f::new(0., 1., 0.));
let ds = params.get_one_float("udelta", 0.);
let dt = params.get_one_float("vdelta", 0.);
let mapping = PlanarMapping::new(&render_from_texture.inverse(), vs, vt, ds, dt);
TextureMapping2D::Planar(mapping)
}
_ => {
log::error!("{}: 2D texture mapping unknown {}", loc, mtype);
TextureMapping2D::UV(UVMapping::default())
}
}
}
}
pub static TEXTURE_CACHE: OnceLock<Mutex<HashMap<TexInfo, Arc<MIPMap>>>> = OnceLock::new();
pub fn get_texture_cache() -> &'static Mutex<HashMap<TexInfo, Arc<MIPMap>>> {
TEXTURE_CACHE.get_or_init(|| Mutex::new(HashMap::new()))
}
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
pub struct TexInfo {
pub filename: String,
pub filter_options: MIPMapFilterOptions,
pub wrap_mode: WrapMode,
pub encoding: ColorEncoding,
}