970 lines
29 KiB
Rust
970 lines
29 KiB
Rust
use crate::core::spectrum::SPECTRUM_FILE_CACHE;
|
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
|
use crate::spectra::data::get_named_spectrum;
|
|
use crate::spectra::piecewise::PiecewiseLinearSpectrumBuffer;
|
|
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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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] {
|
|
¶m.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 \"{}\".",
|
|
¶m.loc,
|
|
name
|
|
);
|
|
}
|
|
|
|
if values.len() != T::N_PER_ITEM {
|
|
bail!(
|
|
"{}: Expected {} values for parameter \"{}\". Found {}.",
|
|
¶m.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 {}",
|
|
¶m.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();
|
|
}
|
|
}
|
|
return "".to_string();
|
|
}
|
|
|
|
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 = PiecewiseLinearSpectrumBuffer::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)
|
|
}
|
|
}
|