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 { 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 { 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 { 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>>> = OnceLock::new(); pub fn get_texture_cache() -> &'static Mutex>> { 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, }