287 lines
8.5 KiB
Rust
287 lines
8.5 KiB
Rust
use crate::core::texture::{get_texture_cache, CreateTextureMapping, TexInfo};
|
|
use crate::core::texture::{
|
|
CreateFloatTexture, CreateSpectrumTexture, FloatTexture, FloatTextureTrait, SpectrumTexture,
|
|
SpectrumTextureTrait,
|
|
};
|
|
use crate::utils::mipmap::{MIPMap, MIPMapFilterOptions};
|
|
use crate::utils::{resolve_filename, FileLoc, TextureParameterDictionary};
|
|
use crate::Arena;
|
|
use anyhow::Result;
|
|
use shared::core::color::RGB;
|
|
use shared::core::color::{ColorEncoding, SRGBEncoding};
|
|
use shared::core::geometry::Vector2f;
|
|
use shared::core::image::{FilterFunction, WrapMode};
|
|
use shared::core::spectrum::SpectrumTrait;
|
|
use shared::core::texture::{SpectrumType, TexCoord2D, TextureEvalContext, TextureMapping2D};
|
|
use shared::spectra::{
|
|
RGBAlbedoSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum, SampledSpectrum,
|
|
SampledWavelengths,
|
|
};
|
|
use shared::utils::Transform;
|
|
use shared::Float;
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ImageTextureBase {
|
|
pub mapping: TextureMapping2D,
|
|
pub filename: String,
|
|
pub scale: Float,
|
|
pub invert: bool,
|
|
pub mipmap: Arc<MIPMap>,
|
|
}
|
|
|
|
impl ImageTextureBase {
|
|
pub fn new(
|
|
mapping: TextureMapping2D,
|
|
filename: String,
|
|
filter_options: MIPMapFilterOptions,
|
|
wrap_mode: WrapMode,
|
|
scale: Float,
|
|
invert: bool,
|
|
encoding: ColorEncoding,
|
|
) -> Self {
|
|
let tex_info = TexInfo {
|
|
filename: filename.clone(),
|
|
filter_options,
|
|
wrap_mode,
|
|
encoding,
|
|
};
|
|
|
|
let cache_mutex = get_texture_cache();
|
|
|
|
{
|
|
let cache = cache_mutex.lock().unwrap();
|
|
if let Some(mipmap) = cache.get(&tex_info) {
|
|
return Self {
|
|
mapping,
|
|
filename,
|
|
scale,
|
|
invert,
|
|
mipmap: mipmap.clone(),
|
|
};
|
|
}
|
|
}
|
|
|
|
let path = Path::new(&filename);
|
|
let mipmap_raw = MIPMap::create_from_file(path, filter_options, wrap_mode, encoding)
|
|
.expect("Failed to create MIPMap from file");
|
|
|
|
let mipmap_arc = Arc::new(mipmap_raw);
|
|
|
|
{
|
|
let mut cache = cache_mutex.lock().unwrap();
|
|
|
|
let stored_mipmap = cache.entry(tex_info).or_insert(mipmap_arc);
|
|
|
|
Self {
|
|
mapping,
|
|
filename,
|
|
scale,
|
|
invert,
|
|
mipmap: stored_mipmap.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn clear_cache() {
|
|
let mut cache = get_texture_cache().lock().unwrap();
|
|
cache.clear();
|
|
}
|
|
|
|
pub fn multiply_scale(&mut self, s: Float) {
|
|
self.scale *= s;
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SpectrumImageTexture {
|
|
pub base: ImageTextureBase,
|
|
pub spectrum_type: SpectrumType,
|
|
}
|
|
|
|
impl SpectrumImageTexture {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
mapping: TextureMapping2D,
|
|
filename: String,
|
|
filter_options: MIPMapFilterOptions,
|
|
wrap_mode: WrapMode,
|
|
scale: Float,
|
|
invert: bool,
|
|
encoding: ColorEncoding,
|
|
spectrum_type: SpectrumType,
|
|
) -> Self {
|
|
let base = ImageTextureBase::new(
|
|
mapping,
|
|
filename,
|
|
filter_options,
|
|
wrap_mode,
|
|
scale,
|
|
invert,
|
|
encoding,
|
|
);
|
|
|
|
Self {
|
|
base,
|
|
spectrum_type,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SpectrumTextureTrait for SpectrumImageTexture {
|
|
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
|
let mut c = self.base.mapping.map(ctx);
|
|
c.st[1] = 1. - c.st[1];
|
|
let dst0 = Vector2f::new(c.dsdx, c.dtdx);
|
|
let dst1 = Vector2f::new(c.dsdy, c.dtdy);
|
|
let rgb_unclamp = self.base.scale * self.base.mipmap.filter::<RGB>(c.st, dst0, dst1);
|
|
let rgb = RGB::clamp_zero(&rgb_unclamp);
|
|
if let Some(cs) = self.base.mipmap.get_rgb_colorspace() {
|
|
match self.spectrum_type {
|
|
SpectrumType::Unbounded => {
|
|
return RGBUnboundedSpectrum::new(&cs, rgb).sample(lambda);
|
|
}
|
|
SpectrumType::Albedo => {
|
|
return RGBAlbedoSpectrum::new(&cs, rgb.clamp(0., 1.)).sample(lambda);
|
|
}
|
|
_ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda),
|
|
}
|
|
}
|
|
assert!(rgb[0] == rgb[1] && rgb[1] == rgb[2]);
|
|
SampledSpectrum::new(rgb[0])
|
|
}
|
|
}
|
|
|
|
impl CreateSpectrumTexture for SpectrumImageTexture {
|
|
fn create(
|
|
render_from_texture: Transform,
|
|
parameters: TextureParameterDictionary,
|
|
spectrum_type: SpectrumType,
|
|
loc: FileLoc,
|
|
) -> Result<SpectrumTexture> {
|
|
let mapping = TextureMapping2D::create(¶meters, &render_from_texture, &loc)?;
|
|
|
|
let filename = crate::utils::resolve_filename(¶meters.get_one_string("filename", "")?);
|
|
let scale = parameters.get_one_float("scale", 1.0)?;
|
|
let invert = parameters.get_one_bool("invert", false)?;
|
|
|
|
let filter_options = MIPMapFilterOptions::default();
|
|
let wrap_str = parameters.get_one_string("wrap", "repeat")?;
|
|
// let wrap_mode = WrapMode::parse(wrap_str)?;
|
|
let wrap_mode = match wrap_str.as_str() {
|
|
"repeat" => WrapMode::Repeat,
|
|
"clamp" => WrapMode::Clamp,
|
|
"black" => WrapMode::Black,
|
|
_ => WrapMode::Repeat,
|
|
};
|
|
|
|
let encoding = ColorEncoding::SRGB(SRGBEncoding);
|
|
|
|
let tex = SpectrumImageTexture::new(
|
|
mapping,
|
|
filename,
|
|
filter_options,
|
|
wrap_mode,
|
|
scale,
|
|
invert,
|
|
encoding,
|
|
spectrum_type,
|
|
);
|
|
|
|
Ok(SpectrumTexture::Image(tex))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct FloatImageTexture {
|
|
pub base: ImageTextureBase,
|
|
}
|
|
|
|
impl FloatImageTexture {
|
|
pub fn new(
|
|
mapping: TextureMapping2D,
|
|
filename: String,
|
|
filter_options: MIPMapFilterOptions,
|
|
wrap_mode: WrapMode,
|
|
scale: Float,
|
|
invert: bool,
|
|
encoding: ColorEncoding,
|
|
) -> Self {
|
|
Self {
|
|
base: ImageTextureBase::new(
|
|
mapping,
|
|
filename,
|
|
filter_options,
|
|
wrap_mode,
|
|
scale,
|
|
invert,
|
|
encoding,
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FloatTextureTrait for FloatImageTexture {
|
|
fn evaluate(&self, ctx: &TextureEvalContext) -> Float {
|
|
let mut c: TexCoord2D = self.base.mapping.map(ctx);
|
|
c.st[1] = 1. - c.st[1];
|
|
let v: Float = self.base.scale
|
|
* self.base.mipmap.filter::<Float>(
|
|
c.st,
|
|
Vector2f::new(c.dsdx, c.dtdx),
|
|
Vector2f::new(c.dsdy, c.dtdy),
|
|
);
|
|
|
|
if self.base.invert {
|
|
(1. - v).max(0.)
|
|
} else {
|
|
v
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CreateFloatTexture for FloatImageTexture {
|
|
fn create(
|
|
render_from_texture: Transform,
|
|
parameters: TextureParameterDictionary,
|
|
loc: FileLoc,
|
|
_arena: &Arena,
|
|
) -> Result<FloatTexture> {
|
|
let mapping = TextureMapping2D::create(¶meters, &render_from_texture, &loc)?;
|
|
let max_aniso = parameters.get_one_float("maxanisotropy", 8.)?;
|
|
let filter = parameters.get_one_string("filter", "bilinear")?;
|
|
let mut filter_options = MIPMapFilterOptions::default();
|
|
filter_options.max_anisotropy = max_aniso;
|
|
|
|
let ff = FilterFunction::parse(&filter)?;
|
|
filter_options.filter = ff;
|
|
let wrap_string = parameters.get_one_string("wrap", "repeat")?;
|
|
let wrap_mode = WrapMode::parse(&wrap_string)?;
|
|
let scale = parameters.get_one_float("scale", 1.)?;
|
|
let invert = parameters.get_one_bool("invert", false)?;
|
|
let filename = resolve_filename(¶meters.get_one_string("filename", "")?);
|
|
let default_encoding = if Path::new(&filename)
|
|
.extension()
|
|
.map_or(false, |ext| ext == "png")
|
|
{
|
|
"sRGB"
|
|
} else {
|
|
"linear"
|
|
};
|
|
let encoding_str = parameters.get_one_string("encoding", default_encoding)?;
|
|
let encoding = ColorEncoding::from_name(&encoding_str)?;
|
|
|
|
let tex = FloatImageTexture::new(
|
|
mapping,
|
|
filename,
|
|
filter_options,
|
|
wrap_mode,
|
|
scale,
|
|
invert,
|
|
encoding,
|
|
);
|
|
|
|
Ok(FloatTexture::Image(tex))
|
|
}
|
|
}
|