use crate::textures::*; use crate::utils::{MIPMap, MIPMapFilterOptions, TextureParameterDictionary}; use crate::{Arena, FileLoc}; use anyhow::{Result, anyhow}; 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::spectrum::Spectrum; use shared::core::texture::SpectrumType; use shared::core::texture::{ CylindricalMapping, PlanarMapping, PointTransformMapping, SphericalMapping, TextureEvalContext, TextureMapping2D, TextureMapping3D, UVMapping, }; use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::textures::{ FBmTexture, FloatBilerpTexture, FloatCheckerboardTexture, FloatConstantTexture, FloatDotsTexture, MarbleTexture, SpectrumBilerpTexture, SpectrumCheckerboardTexture, SpectrumConstantTexture, SpectrumDotsTexture, WindyTexture, WrinkledTexture, }; use shared::utils::Transform; use std::collections::HashMap; use std::sync::{Arc, Mutex, OnceLock}; #[derive(Clone, Debug)] pub enum FloatTexture { Constant(FloatConstantTexture), Checkerboard(FloatCheckerboardTexture), Dots(FloatDotsTexture), FBm(FBmTexture), Windy(WindyTexture), Wrinkled(WrinkledTexture), Mix(FloatMixTexture), DirectionMix(FloatDirectionMixTexture), // #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")] Scaled(FloatScaledTexture), Image(FloatImageTexture), Bilerp(FloatBilerpTexture), } impl Default for FloatTexture { fn default() -> Self { FloatTexture::Constant(FloatConstantTexture::new(1.0)) } } pub trait CreateFloatTexture { fn create( render_from_texture: Transform, params: TextureParameterDictionary, loc: FileLoc, arena: &Arena, ) -> Result; } impl FloatTexture { pub fn create( name: &str, render_from_texture: Transform, params: TextureParameterDictionary, loc: FileLoc, arena: &Arena, ) -> Result { match name { "constant" => FloatConstantTexture::create(render_from_texture, params, loc, arena), "scale" => FloatScaledTexture::create(&render_from_texture, ¶ms, &loc, arena), "mix" => FloatMixTexture::create(&render_from_texture, ¶ms, &loc, arena), "directionmix" => { FloatDirectionMixTexture::create(&render_from_texture, ¶ms, &loc, arena) } "bilerp" => FloatBilerpTexture::create(render_from_texture, params, loc, arena), "imagemap" => FloatImageTexture::create(render_from_texture, params, loc, arena), "checkerboard" => { FloatCheckerboardTexture::create(render_from_texture, params, loc, arena) } "dots" => FloatDotsTexture::create(render_from_texture, params, loc, arena), "fbm" => FBmTexture::create(render_from_texture, params, loc, arena), "wrinkled" => WrinkledTexture::create(render_from_texture, params, loc, arena), "windy" => WindyTexture::create(render_from_texture, params, loc, arena), _ => Err(anyhow!("Float texture type '{}' unknown at {}", name, loc)), } } } #[derive(Clone, Debug)] pub enum SpectrumTexture { Constant(SpectrumConstantTexture), Checkerboard(SpectrumCheckerboardTexture), Dots(SpectrumDotsTexture), Image(SpectrumImageTexture), Bilerp(SpectrumBilerpTexture), Scaled(SpectrumScaledTexture), Marble(MarbleTexture), Mix(SpectrumMixTexture), DirectionMix(SpectrumDirectionMixTexture), } /// A bare `Spectrum` used as a texture is always a constant texture. Saves /// writing `SpectrumTexture::Constant(SpectrumConstantTexture::new(s))` at every /// default-value site. impl From for SpectrumTexture { fn from(s: Spectrum) -> Self { SpectrumTexture::Constant(SpectrumConstantTexture::new(s)) } } impl From for FloatTexture { fn from(v: Float) -> Self { FloatTexture::Constant(FloatConstantTexture::new(v)) } } pub trait CreateSpectrumTexture { fn create( render_from_texture: Transform, parameters: TextureParameterDictionary, spectrum_type: SpectrumType, loc: FileLoc, arena: &Arena, ) -> Result; } impl SpectrumTexture { pub fn create( name: &str, render_from_texture: Transform, params: TextureParameterDictionary, spectrum_type: SpectrumType, loc: FileLoc, arena: &Arena, ) -> Result { match name { "constant" => SpectrumConstantTexture::create( render_from_texture, params, spectrum_type, loc, arena, ), "scale" => SpectrumScaledTexture::create( render_from_texture, params, spectrum_type, loc, arena, ), "mix" => { SpectrumMixTexture::create(render_from_texture, params, spectrum_type, loc, arena) } "directionmix" => SpectrumDirectionMixTexture::create( render_from_texture, params, spectrum_type, loc, arena, ), "bilerp" => SpectrumBilerpTexture::create( render_from_texture, params, spectrum_type, loc, arena, ), "imagemap" => { SpectrumImageTexture::create(render_from_texture, params, spectrum_type, loc, arena) } "checkerboard" => SpectrumCheckerboardTexture::create( render_from_texture, params, spectrum_type, loc, arena, ), "dots" => { SpectrumDotsTexture::create(render_from_texture, params, spectrum_type, loc, arena) } "marble" => { MarbleTexture::create(render_from_texture, params, spectrum_type, loc, arena) } _ => Err(anyhow!( "Spectrum texture type '{}' unknown at {}", name, loc )), } } } pub trait CreateTextureMapping { fn create( params: &TextureParameterDictionary, render_from_texture: &Transform, loc: &FileLoc, ) -> Result where Self: Sized; } impl CreateTextureMapping for TextureMapping2D { fn create( params: &TextureParameterDictionary, render_from_texture: &Transform, loc: &FileLoc, ) -> Result where Self: Sized, { 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); Ok(TextureMapping2D::UV(mapping)) } "spherical" => { let mapping = SphericalMapping::new(&render_from_texture.inverse()); Ok(TextureMapping2D::Spherical(mapping)) } "cylindrical" => { let mapping = CylindricalMapping::new(&render_from_texture.inverse()); Ok(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); Ok(TextureMapping2D::Planar(mapping)) } _ => { log::error!("{}: 2D texture mapping unknown {}", loc, mtype); Ok(TextureMapping2D::UV(UVMapping::default())) } } } } impl CreateTextureMapping for TextureMapping3D { fn create( params: &TextureParameterDictionary, render_from_texture: &Transform, loc: &FileLoc, ) -> Result where Self: Sized, { let mapping = PointTransformMapping::new(render_from_texture.inverse()); Ok(TextureMapping3D::PointTransform(mapping)) } } 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, }