pbrt/src/core/texture.rs

269 lines
9 KiB
Rust

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<FloatTexture>;
}
impl FloatTexture {
pub fn create(
name: &str,
render_from_texture: Transform,
params: TextureParameterDictionary,
loc: FileLoc,
arena: &Arena,
) -> Result<Self> {
match name {
"constant" => FloatConstantTexture::create(render_from_texture, params, loc, arena),
"scale" => FloatScaledTexture::create(&render_from_texture, &params, &loc, arena),
"mix" => FloatMixTexture::create(&render_from_texture, &params, &loc, arena),
"directionmix" => {
FloatDirectionMixTexture::create(&render_from_texture, &params, &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<Spectrum> for SpectrumTexture {
fn from(s: Spectrum) -> Self {
SpectrumTexture::Constant(SpectrumConstantTexture::new(s))
}
}
impl From<Float> 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<SpectrumTexture>;
}
impl SpectrumTexture {
pub fn create(
name: &str,
render_from_texture: Transform,
params: TextureParameterDictionary,
spectrum_type: SpectrumType,
loc: FileLoc,
arena: &Arena,
) -> Result<Self> {
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<Self>
where
Self: Sized;
}
impl CreateTextureMapping for TextureMapping2D {
fn create(
params: &TextureParameterDictionary,
render_from_texture: &Transform,
loc: &FileLoc,
) -> Result<Self>
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<Self>
where
Self: Sized,
{
let mapping = PointTransformMapping::new(render_from_texture.inverse());
Ok(TextureMapping3D::PointTransform(mapping))
}
}
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,
}