pbrt/src/utils/parameters.rs

970 lines
29 KiB
Rust

use crate::core::spectrum::SPECTRUM_FILE_CACHE;
use crate::spectra::piecewise::ReadFromFile;
use crate::core::texture::{FloatTexture, SpectrumTexture};
use crate::spectra::data::get_named_spectrum;
use crate::utils::FileLoc;
use anyhow::{bail, Result};
use shared::core::color::RGB;
use shared::core::geometry::{Normal3f, Point2f, Point3f, Vector2f, Vector3f};
use shared::core::spectrum::Spectrum;
use shared::core::texture::{FloatConstantTexture, SpectrumConstantTexture, SpectrumType};
use shared::spectra::{
PiecewiseLinearSpectrum, RGBAlbedoSpectrum, RGBColorSpace, RGBIlluminantSpectrum,
RGBUnboundedSpectrum,
};
use shared::Float;
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
pub fn error_exit(loc: Option<&FileLoc>, message: &str) -> String {
if let Some(l) = loc {
format!("{}: {}", l, message)
} else {
message.to_string()
}
}
#[derive(Debug)]
pub struct ParsedParameter {
pub type_name: String,
pub name: String,
pub loc: FileLoc,
pub floats: Vec<Float>,
pub ints: Vec<i32>,
pub strings: Vec<String>,
pub bools: Vec<bool>,
pub looked_up: AtomicBool,
pub may_be_unused: bool,
pub color_space: Option<Arc<RGBColorSpace>>,
}
impl Default for ParsedParameter {
fn default() -> Self {
Self {
type_name: String::new(),
name: String::new(),
loc: FileLoc::default(),
floats: Vec::new(),
ints: Vec::new(),
strings: Vec::new(),
bools: Vec::new(),
looked_up: AtomicBool::new(false),
may_be_unused: false,
color_space: None,
}
}
}
impl Clone for ParsedParameter {
fn clone(&self) -> Self {
let looked_up_val = self.looked_up.load(Ordering::Relaxed);
Self {
type_name: self.type_name.clone(),
name: self.name.clone(),
loc: self.loc.clone(),
floats: self.floats.clone(),
ints: self.ints.clone(),
strings: self.strings.clone(),
bools: self.bools.clone(),
looked_up: AtomicBool::new(looked_up_val),
may_be_unused: self.may_be_unused,
color_space: self.color_space.clone(),
}
}
}
impl ParsedParameter {
pub fn new(loc: FileLoc) -> Self {
Self {
type_name: String::new(),
name: String::new(),
loc,
floats: Vec::new(),
ints: Vec::new(),
strings: Vec::new(),
bools: Vec::new(),
looked_up: AtomicBool::new(false),
may_be_unused: false,
color_space: None,
}
}
pub fn add_float(&mut self, v: Float) {
self.floats.push(v);
}
pub fn add_int(&mut self, i: i32) {
self.ints.push(i);
}
pub fn add_string(&mut self, s: String) {
self.strings.push(s);
}
pub fn add_bool(&mut self, v: bool) {
self.bools.push(v);
}
pub fn to_string(&self) -> String {
format!("[ ParsedParameter {} {} ]", self.type_name, self.name)
}
}
pub type ParsedParameterVector = Vec<ParsedParameter>;
pub trait PBRTParameter: Sized {
type Raw: Clone;
const TYPE_NAME: &'static str;
const N_PER_ITEM: usize;
fn convert(v: &[Self::Raw]) -> Self;
fn get_values(param: &ParsedParameter) -> &[Self::Raw];
}
impl PBRTParameter for bool {
type Raw = bool;
const TYPE_NAME: &'static str = "bool";
const N_PER_ITEM: usize = 1;
fn convert(v: &[Self::Raw]) -> Self {
v[0]
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.bools
}
}
impl PBRTParameter for Float {
type Raw = Float;
const TYPE_NAME: &'static str = "float";
const N_PER_ITEM: usize = 1;
fn convert(v: &[Self::Raw]) -> Self {
v[0]
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for i32 {
type Raw = i32;
const TYPE_NAME: &'static str = "integer";
const N_PER_ITEM: usize = 1;
fn convert(v: &[Self::Raw]) -> Self {
v[0]
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.ints
}
}
impl PBRTParameter for Point2f {
type Raw = Float;
const TYPE_NAME: &'static str = "point2";
const N_PER_ITEM: usize = 2;
fn convert(v: &[Self::Raw]) -> Self {
Point2f::new(v[0], v[1])
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for Point3f {
type Raw = Float;
const TYPE_NAME: &'static str = "point3";
const N_PER_ITEM: usize = 3;
fn convert(v: &[Self::Raw]) -> Self {
Point3f::new(v[0], v[1], v[2])
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for Vector2f {
type Raw = Float;
const TYPE_NAME: &'static str = "vector2";
const N_PER_ITEM: usize = 2;
fn convert(v: &[Self::Raw]) -> Self {
Vector2f::new(v[0], v[1])
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for Vector3f {
type Raw = Float;
const TYPE_NAME: &'static str = "vector3";
const N_PER_ITEM: usize = 3;
fn convert(v: &[Self::Raw]) -> Self {
Vector3f::new(v[0], v[1], v[2])
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for Normal3f {
type Raw = Float;
const TYPE_NAME: &'static str = "normal";
const N_PER_ITEM: usize = 3;
fn convert(v: &[Self::Raw]) -> Self {
Normal3f::new(v[0], v[1], v[2])
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.floats
}
}
impl PBRTParameter for String {
type Raw = String;
const TYPE_NAME: &'static str = "string";
const N_PER_ITEM: usize = 1;
fn convert(v: &[Self::Raw]) -> Self {
v[0].clone()
}
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
&param.strings
}
}
#[derive(Default, Clone)]
pub struct NamedTextures {
pub float_textures: Arc<HashMap<String, Arc<FloatTexture>>>,
pub albedo_spectrum_textures: Arc<HashMap<String, Arc<SpectrumTexture>>>,
pub unbounded_spectrum_textures: Arc<HashMap<String, Arc<SpectrumTexture>>>,
pub illuminant_spectrum_textures: Arc<HashMap<String, Arc<SpectrumTexture>>>,
}
#[derive(Debug, Default, Clone)]
pub struct ParameterDictionary {
pub params: ParsedParameterVector,
pub color_space: Option<Arc<RGBColorSpace>>,
pub n_owned_params: usize,
}
impl ParameterDictionary {
pub fn new(
mut params: ParsedParameterVector,
color_space: Option<Arc<RGBColorSpace>>,
) -> Result<Self> {
let n_owned_params = params.len();
params.reverse();
let dict = Self {
params,
color_space,
n_owned_params,
};
dict.check_parameter_types()?;
Ok(dict)
}
pub fn from_array(
mut p0: ParsedParameterVector,
params: &[ParsedParameter],
color_space: Option<Arc<RGBColorSpace>>,
) -> Result<Self> {
let n_owned_params = params.len();
p0.extend(params.iter().rev().cloned());
let dict = Self {
params: p0,
color_space,
n_owned_params,
};
dict.check_parameter_types()?;
Ok(dict)
}
fn check_parameter_types(&self) -> Result<()> {
for p in &self.params {
match p.type_name.as_str() {
bool::TYPE_NAME => {
if p.bools.is_empty() {
bail!(
"{}: non-Boolean values provided for Boolean-valued parameter \"{}\"",
&p.loc,
p.name
);
}
}
Float::TYPE_NAME
| i32::TYPE_NAME
| Point2f::TYPE_NAME
| Vector2f::TYPE_NAME
| Point3f::TYPE_NAME
| Vector3f::TYPE_NAME
| Normal3f::TYPE_NAME
| "rgb"
| "blackbody" => {
if p.ints.is_empty() && p.floats.is_empty() {
bail!(
"{}: non-numeric values provided for numeric-valued parameter \"{}\"",
&p.loc,
p.name
);
}
}
String::TYPE_NAME | "texture" => {
if p.strings.is_empty() {
bail!(
"{}: non-string values provided for string-valued parameter \"{}\"",
&p.loc,
p.name
);
}
}
"spectrum" => {
if p.strings.is_empty() && p.ints.is_empty() && p.floats.is_empty() {
bail!(
"{}: expecting string or numeric-valued parameter for spectrum parameter \"{}\"",
&p.loc,
p.name
);
}
}
unknown => {
bail!(
"{}: unknown parameter type \"{}\" '{}'",
&p.loc,
p.name,
unknown,
);
}
}
}
Ok(())
}
fn lookup_single<T>(&self, name: &str, default_val: T) -> Result<T>
where
T: PBRTParameter,
{
for param in &self.params {
if param.name == name && param.type_name == T::TYPE_NAME {
let values = T::get_values(param);
if values.is_empty() {
bail!(
"{}: No values provided for parameter \"{}\".",
&param.loc,
name
);
}
if values.len() != T::N_PER_ITEM {
bail!(
"{}: Expected {} values for parameter \"{}\". Found {}.",
&param.loc,
T::N_PER_ITEM,
name,
values.len()
);
}
param.looked_up.store(true, Ordering::Relaxed);
return Ok(T::convert(values));
}
}
Ok(default_val)
}
pub fn lookup_array<T>(&self, name: &str) -> Result<Vec<T>>
where
T: PBRTParameter,
{
for param in &self.params {
if param.name == name && param.type_name == T::TYPE_NAME {
let values = T::get_values(param);
if values.len() % T::N_PER_ITEM != 0 {
bail!(
"{}: Number of values for \"{}\" is not a multiple of {}",
&param.loc,
name,
T::N_PER_ITEM
);
}
param.looked_up.store(true, Ordering::Relaxed);
return Ok(values
.chunks(T::N_PER_ITEM)
.map(|chunk| T::convert(chunk))
.collect());
}
}
Ok(Vec::new())
}
pub fn get_one_float(&self, name: &str, def: Float) -> Result<Float> {
self.lookup_single(name, def)
}
pub fn get_one_int(&self, name: &str, def: i32) -> Result<i32> {
self.lookup_single(name, def)
}
pub fn get_one_bool(&self, name: &str, def: bool) -> Result<bool> {
self.lookup_single(name, def)
}
pub fn get_one_string(&self, name: &str, def: &str) -> Result<String> {
self.lookup_single(name, def.to_string())
}
pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Result<Point2f> {
self.lookup_single(name, def)
}
pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Result<Point3f> {
self.lookup_single(name, def)
}
pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Result<Vector2f> {
self.lookup_single(name, def)
}
pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Result<Vector3f> {
self.lookup_single(name, def)
}
pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Result<Normal3f> {
self.lookup_single(name, def)
}
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
self.lookup_array(name)
}
pub fn get_int_array(&self, name: &str) -> Result<Vec<i32>> {
self.lookup_array(name)
}
pub fn get_bool_array(&self, name: &str) -> Result<Vec<bool>> {
self.lookup_array(name)
}
pub fn get_string_array(&self, name: &str) -> Result<Vec<String>> {
self.lookup_array(name)
}
pub fn get_point2f_array(&self, name: &str) -> Result<Vec<Point2f>> {
self.lookup_array(name)
}
pub fn get_point3f_array(&self, name: &str) -> Result<Vec<Point3f>> {
self.lookup_array(name)
}
pub fn get_vector3f_array(&self, name: &str) -> Result<Vec<Vector3f>> {
self.lookup_array(name)
}
pub fn get_normal3f_array(&self, name: &str) -> Result<Vec<Normal3f>> {
self.lookup_array(name)
}
pub fn get_one_spectrum(
&self,
name: &str,
def: Option<Spectrum>,
stype: SpectrumType,
) -> Option<Spectrum> {
for param in &self.params {
if param.name == name {
param.looked_up.store(true, Ordering::Relaxed);
if param.type_name == "spectrum"
|| param.type_name == "rgb"
|| param.type_name == "blackbody"
{
return Some(self.extract_spectrum_array(param, stype)[0].clone());
}
}
}
def
}
pub fn get_spectrum_array(&self, name: &str, stype: SpectrumType) -> Vec<Spectrum> {
for param in &self.params {
if param.name == name {
param.looked_up.store(true, Ordering::Relaxed);
if param.type_name == "spectrum"
|| param.type_name == "rgb"
|| param.type_name == "blackbody"
{
return self.extract_spectrum_array(param, stype);
}
}
}
Vec::new()
}
pub fn get_texture(&self, name: &str) -> String {
for p in &self.params {
if p.name == name && p.type_name == "texture" {
if p.strings.len() != 1 {
panic!(
"[{:?}] Expected 1 texture name for {}, found {}",
p.loc,
name,
p.strings.len()
);
}
p.looked_up.store(true, Ordering::Relaxed);
return p.strings[0].clone();
}
}
String::new()
}
pub fn report_unused(&self) {
for param in &self.params {
if !param.looked_up.load(Ordering::Relaxed) && !param.may_be_unused {
eprintln!(
"Warning: Parameter '{}' ({}) was unused.",
param.name, param.type_name
);
}
}
}
pub fn remove_float(&mut self, name: &str) {
self.remove(name, "float");
}
pub fn remove_int(&mut self, name: &str) {
self.remove(name, "integer");
}
pub fn remove_bool(&mut self, name: &str) {
self.remove(name, "bool");
}
fn remove(&mut self, name: &str, type_name: &str) {
self.params
.retain(|p| !(p.name == name && p.type_name == type_name));
}
pub fn rename_parameter(&mut self, before: &str, after: &str) {
for param in &mut self.params {
if param.name == before {
param.name = after.to_string();
}
}
}
fn extract_spectrum_array(
&self,
param: &ParsedParameter,
spectrum_type: SpectrumType,
) -> Vec<Spectrum> {
match param.type_name.as_str() {
"rgb" | "color" => self.extract_rgb_spectrum(param, spectrum_type),
"blackbody" => self.extract_file_spectrum(param),
"spectrum" => self.extract_sampled_spectrum(param),
_ => Vec::new(),
}
}
fn extract_rgb_spectrum(
&self,
param: &ParsedParameter,
spectrum_type: SpectrumType,
) -> Vec<Spectrum> {
let color_space = param
.color_space
.as_ref()
.or(self.color_space.as_ref())
.expect("No color available");
param
.floats
.chunks_exact(3)
.map(|chunk| {
let rgb = RGB::new(chunk[0], chunk[1], chunk[2]);
if rgb.r < 0.0 || rgb.g < 0.0 || rgb.b < 0.0 {
panic!(
"{}: RGB parameter '{}' has negative component.",
param.loc, param.name
);
}
match spectrum_type {
SpectrumType::Albedo => {
if rgb.r > 1.0 || rgb.g > 1.0 || rgb.b > 1.0 {
panic!(
"{}: RGB parameter '{}' has > 1 component.",
param.loc, param.name
);
}
Spectrum::RGBAlbedo(RGBAlbedoSpectrum::new(color_space.as_ref(), rgb))
}
SpectrumType::Unbounded => {
Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(color_space.as_ref(), rgb))
}
SpectrumType::Illuminant => Spectrum::RGBIlluminant(
RGBIlluminantSpectrum::new(color_space.as_ref(), rgb),
),
}
})
.collect()
}
fn extract_sampled_spectrum(&self, param: &ParsedParameter) -> Vec<Spectrum> {
if param.floats.len() % 2 != 0 {
panic!(
"{}: Found odd number of values for '{}'",
param.loc, param.name
);
}
let n_samples = param.floats.len() / 2;
if n_samples == 1 {
eprintln!(
"{}: Specified spectrum is only non-zero at a single wavelength.",
param.loc
);
}
let (lambdas, values): (Vec<Float>, Vec<Float>) = param
.floats
.chunks(2)
.enumerate()
.map(|(i, chunk)| {
let (lam, val) = (chunk[0], chunk[1]);
if i > 0 && lam <= param.floats[(i - 1) * 2] {
panic!(
"{}: Spectrum invalid, wavelengths not increasing: {} >= {}.",
param.loc,
param.floats[(i - 1) * 2],
lam
);
}
(lam as Float, val as Float)
})
.unzip();
vec![Spectrum::Piecewise(PiecewiseLinearSpectrum {
lambdas: lambdas.as_ptr().into(),
values: values.as_ptr().into(),
count: lambdas.len() as u32,
})]
}
fn extract_file_spectrum(&self, param: &ParsedParameter) -> Vec<Spectrum> {
param
.strings
.iter()
.map(|s| {
get_named_spectrum(s)
.ok_or(())
.or_else(|_| read_spectrum_from_file(s).map_err(|_| ()))
.unwrap_or_else(|_| panic!("{}: {}: unable to read spectrum", param.loc, s))
})
.collect()
}
}
fn read_spectrum_from_file(filename: &str) -> Result<Spectrum, String> {
let fn_key = filename.to_string();
{
let cache = SPECTRUM_FILE_CACHE.lock();
if let Some(s) = cache.get(&fn_key) {
return Ok(s.clone());
}
}
let pls = PiecewiseLinearSpectrum::read(&fn_key)
.ok_or_else(|| format!("unable to read or parse spectrum file '{}'", fn_key))?;
let spectrum = Spectrum::Piecewise(*pls);
{
let mut cache = SPECTRUM_FILE_CACHE.lock();
cache.insert(fn_key, spectrum.clone());
}
Ok(spectrum)
}
pub struct TextureParameterDictionary {
dict: Arc<ParameterDictionary>,
textures: Option<NamedTextures>,
}
impl TextureParameterDictionary {
pub fn new(dict: Arc<ParameterDictionary>, textures: Option<&NamedTextures>) -> Self {
Self {
dict,
textures: textures.cloned(),
}
}
pub fn get_one_float(&self, name: &str, def: Float) -> Result<Float> {
self.dict.get_one_float(name, def)
}
pub fn get_one_int(&self, name: &str, def: i32) -> Result<i32> {
self.dict.get_one_int(name, def)
}
pub fn get_one_bool(&self, name: &str, def: bool) -> Result<bool> {
self.dict.get_one_bool(name, def)
}
pub fn get_one_string(&self, name: &str, def: &str) -> Result<String> {
self.dict.get_one_string(name, def)
}
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
self.dict.get_float_array(name)
}
pub fn get_int_array(&self, name: &str) -> Result<Vec<i32>> {
self.dict.get_int_array(name)
}
pub fn get_bool_array(&self, name: &str) -> Result<Vec<bool>> {
self.dict.get_bool_array(name)
}
pub fn get_string_array(&self, name: &str) -> Result<Vec<String>> {
self.dict.get_string_array(name)
}
pub fn get_one_point3f(&self, name: &str, def: Point3f) -> Result<Point3f> {
self.dict.get_one_point3f(name, def)
}
pub fn get_one_vector3f(&self, name: &str, def: Vector3f) -> Result<Vector3f> {
self.dict.get_one_vector3f(name, def)
}
pub fn get_one_normal3f(&self, name: &str, def: Normal3f) -> Result<Normal3f> {
self.dict.get_one_normal3f(name, def)
}
pub fn get_point3f_array(&self, name: &str) -> Result<Vec<Point3f>> {
self.dict.get_point3f_array(name)
}
pub fn get_vector3f_array(&self, name: &str) -> Result<Vec<Vector3f>> {
self.dict.get_vector3f_array(name)
}
pub fn get_normal3f_array(&self, name: &str) -> Result<Vec<Normal3f>> {
self.dict.get_normal3f_array(name)
}
pub fn get_one_point2f(&self, name: &str, def: Point2f) -> Result<Point2f> {
self.dict.get_one_point2f(name, def)
}
pub fn get_one_vector2f(&self, name: &str, def: Vector2f) -> Result<Vector2f> {
self.dict.get_one_vector2f(name, def)
}
pub fn get_one_spectrum(
&self,
name: &str,
def: Option<Spectrum>,
stype: SpectrumType,
) -> Option<Spectrum> {
self.dict.get_one_spectrum(name, def, stype)
}
pub fn get_spectrum_array(&self, name: &str, stype: SpectrumType) -> Vec<Spectrum> {
self.dict.get_spectrum_array(name, stype)
}
pub fn get_texture(&self, name: &str) -> String {
self.dict.get_texture(name)
}
pub fn report_unused(&self) {
self.dict.report_unused()
}
pub fn get_spectrum_texture(
&self,
name: &str,
val: Option<Spectrum>,
stype: SpectrumType,
) -> Option<Arc<SpectrumTexture>> {
let tex = self.get_spectrum_texture_or_null(name, stype);
if tex.is_some() {
tex
} else if val.is_some() {
Some(Arc::new(SpectrumTexture::Constant(
SpectrumConstantTexture::new(val.unwrap()),
)))
} else {
None
}
}
pub fn get_float_texture(&self, name: &str, val: Float) -> Result<Arc<FloatTexture>> {
if let Some(tex) = self.get_float_texture_or_null(name)? {
return Ok(tex);
} else {
return Ok(Arc::new(FloatTexture::Constant(FloatConstantTexture::new(
val,
))));
}
}
pub fn get_float_texture_with_fallback(
&self,
primary: &str,
fallback: &str,
default: Float,
) -> Result<Arc<FloatTexture>> {
match self.get_float_texture_or_null(primary)? {
Some(tex) => Ok(tex),
None => self.get_float_texture(fallback, default),
}
}
pub fn get_spectrum_texture_or_null(
&self,
name: &str,
stype: SpectrumType,
) -> Option<Arc<SpectrumTexture>> {
for p in &self.dict.params {
if p.name != name {
continue;
}
match p.type_name.as_str() {
"texture" => {
if p.strings.len() != 1 {
panic!(
"[{:?}] Expected 1 texture name for {}, found {}",
p.loc,
name,
p.strings.len()
);
}
p.looked_up.store(true, Ordering::Relaxed);
let tex_name = &p.strings[0];
if let Some(nt) = &self.textures {
let map = match stype {
SpectrumType::Unbounded => &nt.unbounded_spectrum_textures,
SpectrumType::Albedo => &nt.albedo_spectrum_textures,
_ => &nt.illuminant_spectrum_textures,
};
if let Some(tex) = map.get(tex_name) {
return Some(Arc::clone(tex));
}
panic!(
"[{:?}] Couldn't find spectrum texture named '{}'",
p.loc, tex_name
);
}
return None;
}
"rgb" => {
if p.floats.len() != 3 {
panic!("[{:?}] RGB parameter '{}' needs 3 floats", p.loc, name);
}
p.looked_up.store(true, Ordering::Relaxed);
let rgb = RGB::new(p.floats[0], p.floats[1], p.floats[2]);
if rgb.r < 0.0 || rgb.g < 0.0 || rgb.b < 0.0 {
panic!("[{:?}] Negative RGB values for '{}'", p.loc, name);
}
let cs = self.dict.color_space.as_ref().unwrap();
let s: Spectrum = match stype {
SpectrumType::Illuminant => {
Spectrum::RGBIlluminant(RGBIlluminantSpectrum::new(cs, rgb))
}
SpectrumType::Unbounded => {
Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(cs, rgb))
}
SpectrumType::Albedo => {
if rgb.r > 1.0 || rgb.g > 1.0 || rgb.b > 1.0 {
panic!("[{:?}] Albedo RGB > 1 for '{}'", p.loc, name);
}
Spectrum::RGBAlbedo(RGBAlbedoSpectrum::new(cs, rgb))
}
};
return Some(Arc::new(SpectrumTexture::Constant(
SpectrumConstantTexture::new(s),
)));
}
"spectrum" | "blackbody" => {
let s = self.dict.get_one_spectrum(name, None, stype)?;
return Some(Arc::new(SpectrumTexture::Constant(
SpectrumConstantTexture::new(s),
)));
}
_ => {}
}
}
return None;
}
pub fn get_float_texture_or_null(&self, name: &str) -> Result<Option<Arc<FloatTexture>>> {
for p in &self.dict.params {
if p.name != name {
continue;
}
match p.type_name.as_str() {
"texture" => {
if p.strings.len() != 1 {
panic!(
"[{:?}] Expected 1 texture name for {}, found {}",
p.loc,
name,
p.strings.len()
);
}
p.looked_up.store(true, Ordering::Relaxed);
let tex_name = &p.strings[0];
if let Some(nt) = &self.textures {
let map = &nt.float_textures;
if let Some(tex) = map.get(tex_name) {
return Ok(Some(Arc::clone(tex)));
}
panic!(
"[{:?}] Couldn't find float texture named '{}'",
p.loc, tex_name
);
}
return Ok(None);
}
"float" => {
let v = self.get_one_float(name, 0.)?;
return Ok(Some(Arc::new(FloatTexture::Constant(
FloatConstantTexture::new(v),
))));
}
_ => {
panic!("[{:?}] Couldn't find float texture", p.loc);
}
}
}
Ok(None)
}
}