889 lines
27 KiB
Rust
889 lines
27 KiB
Rust
use super::entities::*;
|
|
use super::BasicScene;
|
|
use crate::spectra::get_colorspace_device;
|
|
use crate::utils::error::FileLoc;
|
|
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
|
use crate::utils::parser::{ParserError, ParserTarget};
|
|
use crate::Arena;
|
|
use anyhow::Context;
|
|
use shared::core::camera::CameraTransform;
|
|
use shared::core::geometry::Vector3f;
|
|
use shared::spectra::RGBColorSpace;
|
|
use shared::utils::options::RenderingCoordinateSystem;
|
|
use shared::utils::transform;
|
|
use shared::utils::transform::{AnimatedTransform, Transform};
|
|
use shared::Float;
|
|
use std::collections::{HashMap, HashSet};
|
|
use std::ops::{Index, IndexMut};
|
|
use std::sync::Arc;
|
|
use unicode_normalization::UnicodeNormalization;
|
|
|
|
const MAX_TRANSFORMS: usize = 2;
|
|
|
|
fn normalize_utf8(input: &str) -> String {
|
|
input.nfc().collect::<String>()
|
|
}
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
struct TransformSet {
|
|
t: [Transform; MAX_TRANSFORMS],
|
|
}
|
|
|
|
impl TransformSet {
|
|
fn is_animated(&self) -> bool {
|
|
self.t[0] != self.t[1]
|
|
}
|
|
|
|
fn inverse(&self) -> Self {
|
|
Self {
|
|
t: [self.t[0].inverse(), self.t[1].inverse()],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Index<usize> for TransformSet {
|
|
type Output = Transform;
|
|
fn index(&self, i: usize) -> &Self::Output {
|
|
&self.t[i]
|
|
}
|
|
}
|
|
|
|
impl IndexMut<usize> for TransformSet {
|
|
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
|
|
&mut self.t[i]
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct PendingAreaLight {
|
|
name: String,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
struct GraphicsState {
|
|
pub current_inside_medium: String,
|
|
pub current_outside_medium: String,
|
|
pub current_material_name: String,
|
|
pub current_material_index: Option<usize>,
|
|
pub pending_area_light: Option<PendingAreaLight>,
|
|
pub shape_attributes: ParsedParameterVector,
|
|
pub light_attributes: ParsedParameterVector,
|
|
pub material_attributes: ParsedParameterVector,
|
|
pub medium_attributes: ParsedParameterVector,
|
|
pub texture_attributes: ParsedParameterVector,
|
|
pub reverse_orientation: bool,
|
|
pub color_space: Option<Arc<RGBColorSpace>>,
|
|
pub ctm: TransformSet,
|
|
pub active_transform_bits: u32,
|
|
pub transform_start_time: Float,
|
|
pub transform_end_time: Float,
|
|
}
|
|
|
|
impl Default for GraphicsState {
|
|
fn default() -> Self {
|
|
Self {
|
|
color_space: Some(crate::spectra::default_colorspace_arc()),
|
|
active_transform_bits: BasicSceneBuilder::ALL_TRANSFORM_BITS,
|
|
current_inside_medium: String::new(),
|
|
current_outside_medium: String::new(),
|
|
current_material_name: String::new(),
|
|
current_material_index: None,
|
|
pending_area_light: None,
|
|
shape_attributes: Vec::new(),
|
|
light_attributes: Vec::new(),
|
|
material_attributes: Vec::new(),
|
|
medium_attributes: Vec::new(),
|
|
texture_attributes: Vec::new(),
|
|
reverse_orientation: false,
|
|
ctm: TransformSet::default(),
|
|
transform_start_time: 0.0,
|
|
transform_end_time: 1.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(PartialEq, Eq)]
|
|
enum BlockState {
|
|
OptionsBlock,
|
|
WorldBlock,
|
|
}
|
|
|
|
pub struct BasicSceneBuilder {
|
|
scene: Arc<BasicScene>,
|
|
current_block: BlockState,
|
|
graphics_state: GraphicsState,
|
|
pushed_graphics_states: Vec<GraphicsState>,
|
|
push_stack: Vec<(char, FileLoc)>,
|
|
render_from_world: Transform,
|
|
named_coordinate_systems: HashMap<String, TransformSet>,
|
|
active_instance_definition: Option<InstanceDefinitionSceneEntity>,
|
|
|
|
float_texture_names: HashSet<String>,
|
|
spectrum_texture_names: HashSet<String>,
|
|
named_material_names: HashSet<String>,
|
|
medium_names: HashSet<String>,
|
|
|
|
current_camera: Option<CameraSceneEntity>,
|
|
current_film: Option<SceneEntity>,
|
|
current_integrator: Option<SceneEntity>,
|
|
current_sampler: Option<SceneEntity>,
|
|
current_filter: Option<SceneEntity>,
|
|
current_accelerator: Option<SceneEntity>,
|
|
}
|
|
|
|
impl BasicSceneBuilder {
|
|
pub const START_TRANSFORM_BITS: u32 = 1 << 0;
|
|
pub const END_TRANSFORM_BITS: u32 = 1 << 1;
|
|
pub const ALL_TRANSFORM_BITS: u32 = (1 << MAX_TRANSFORMS) - 1;
|
|
|
|
fn render_from_object_at(&self, index: usize) -> Transform {
|
|
self.render_from_world * self.graphics_state.ctm[index]
|
|
}
|
|
|
|
fn render_from_object(&self) -> AnimatedTransform {
|
|
AnimatedTransform::new(
|
|
&self.render_from_object_at(0),
|
|
self.graphics_state.transform_start_time,
|
|
&self.render_from_object_at(1),
|
|
self.graphics_state.transform_end_time,
|
|
)
|
|
}
|
|
|
|
pub fn new(scene: Arc<BasicScene>) -> Self {
|
|
Self {
|
|
scene,
|
|
current_block: BlockState::OptionsBlock,
|
|
graphics_state: GraphicsState {
|
|
active_transform_bits: Self::ALL_TRANSFORM_BITS,
|
|
..Default::default()
|
|
},
|
|
pushed_graphics_states: Vec::new(),
|
|
push_stack: Vec::new(),
|
|
render_from_world: Transform::identity(),
|
|
named_coordinate_systems: HashMap::new(),
|
|
active_instance_definition: None,
|
|
float_texture_names: HashSet::new(),
|
|
spectrum_texture_names: HashSet::new(),
|
|
named_material_names: HashSet::new(),
|
|
medium_names: HashSet::new(),
|
|
current_camera: Some(CameraSceneEntity {
|
|
base: SceneEntity {
|
|
name: "perspective".into(),
|
|
..Default::default()
|
|
},
|
|
camera_transform: CameraTransform::from_world(
|
|
AnimatedTransform::default(),
|
|
RenderingCoordinateSystem::World,
|
|
),
|
|
medium: String::new(),
|
|
}),
|
|
current_sampler: Some(SceneEntity {
|
|
name: "zsobol".into(),
|
|
..Default::default()
|
|
}),
|
|
current_filter: Some(SceneEntity {
|
|
name: "gaussian".into(),
|
|
..Default::default()
|
|
}),
|
|
current_integrator: Some(SceneEntity {
|
|
name: "volpath".into(),
|
|
..Default::default()
|
|
}),
|
|
current_accelerator: Some(SceneEntity {
|
|
name: "bvh".into(),
|
|
..Default::default()
|
|
}),
|
|
current_film: Some(SceneEntity {
|
|
name: "rgb".into(),
|
|
..Default::default()
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn for_active_transforms<F>(&mut self, f: F)
|
|
where
|
|
F: Fn(&Transform) -> Transform,
|
|
{
|
|
let bits = self.graphics_state.active_transform_bits;
|
|
if (bits & 1) != 0 {
|
|
self.graphics_state.ctm.t[0] = f(&self.graphics_state.ctm.t[0]);
|
|
}
|
|
if (bits & 2) != 0 {
|
|
self.graphics_state.ctm.t[1] = f(&self.graphics_state.ctm.t[1]);
|
|
}
|
|
}
|
|
|
|
fn verify_world(&self, name: &str, loc: &FileLoc) -> Result<(), ParserError> {
|
|
if self.current_block != BlockState::WorldBlock {
|
|
return Err(ParserError::Generic(
|
|
format!("{} not allowed outside WorldBlock", name),
|
|
loc.clone(),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn verify_options(&self, name: &str, loc: &FileLoc) -> Result<(), ParserError> {
|
|
if self.current_block != BlockState::OptionsBlock {
|
|
return Err(ParserError::Generic(
|
|
format!("{} not allowed inside WorldBlock", name),
|
|
loc.clone(),
|
|
));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn make_params(
|
|
&self,
|
|
params: ParsedParameterVector,
|
|
loc: &FileLoc,
|
|
) -> Result<ParameterDictionary, ParserError> {
|
|
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone())
|
|
.map_err(|e| ParserError::Generic(format!("parameter error: {}", e), loc.clone()))
|
|
}
|
|
}
|
|
|
|
impl From<anyhow::Error> for ParserError {
|
|
fn from(e: anyhow::Error) -> Self {
|
|
ParserError::Generic(e.to_string(), FileLoc::default())
|
|
}
|
|
}
|
|
|
|
impl ParserTarget for BasicSceneBuilder {
|
|
fn reverse_orientation(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
|
self.verify_world("ReverseOrientation", &loc)?;
|
|
self.graphics_state.reverse_orientation = !self.graphics_state.reverse_orientation;
|
|
Ok(())
|
|
}
|
|
|
|
fn color_space(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError> {
|
|
let stdcs = get_colorspace_device();
|
|
let _ = match stdcs.get_named(name) {
|
|
Some(cs) => {
|
|
self.graphics_state.color_space = unsafe { Some(Arc::new(*cs.as_ref())) };
|
|
}
|
|
None => {
|
|
eprintln!("Error: Color space '{}' unknown at {}", name, loc);
|
|
}
|
|
};
|
|
Ok(())
|
|
}
|
|
|
|
fn identity(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
|
self.for_active_transforms(|_| Transform::identity());
|
|
Ok(())
|
|
}
|
|
|
|
fn translate(
|
|
&mut self,
|
|
dx: Float,
|
|
dy: Float,
|
|
dz: Float,
|
|
_loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
let t = Transform::translate(Vector3f::new(dx, dy, dz));
|
|
self.for_active_transforms(|cur| cur * &t);
|
|
Ok(())
|
|
}
|
|
|
|
fn rotate(
|
|
&mut self,
|
|
angle: Float,
|
|
ax: Float,
|
|
ay: Float,
|
|
az: Float,
|
|
_loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
let t = Transform::rotate_around_axis(angle, Vector3f::new(ax, ay, az));
|
|
self.for_active_transforms(|cur| cur * &t);
|
|
Ok(())
|
|
}
|
|
|
|
fn scale(&mut self, sx: Float, sy: Float, sz: Float, _loc: FileLoc) -> Result<(), ParserError> {
|
|
let t = Transform::scale(sx, sy, sz);
|
|
self.for_active_transforms(|cur| cur * &t);
|
|
Ok(())
|
|
}
|
|
|
|
fn look_at(
|
|
&mut self,
|
|
ex: Float,
|
|
ey: Float,
|
|
ez: Float,
|
|
lx: Float,
|
|
ly: Float,
|
|
lz: Float,
|
|
ux: Float,
|
|
uy: Float,
|
|
uz: Float,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
let t = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz))
|
|
.with_context(|| format!("at {}", loc))?;
|
|
self.for_active_transforms(|cur| cur * &t);
|
|
Ok(())
|
|
}
|
|
|
|
fn concat_transform(&mut self, m: &[Float; 16], loc: FileLoc) -> Result<(), ParserError> {
|
|
let result = Transform::from_flat(m);
|
|
match result {
|
|
Some(t) => {
|
|
self.for_active_transforms(|cur| cur * &t);
|
|
}
|
|
None => {
|
|
eprintln!("Error: Could not invert transform at {}", loc);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn transform(&mut self, m: &[Float; 16], loc: FileLoc) -> Result<(), ParserError> {
|
|
let result = Transform::from_flat(m);
|
|
match result {
|
|
Some(t) => {
|
|
self.for_active_transforms(|_| t);
|
|
}
|
|
None => {
|
|
eprintln!("Error: Could not invert transform at {}", loc);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn coordinate_system(&mut self, name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
|
self.named_coordinate_systems
|
|
.insert(name.to_string(), self.graphics_state.ctm);
|
|
Ok(())
|
|
}
|
|
|
|
fn coord_sys_transform(&mut self, name: &str, loc: FileLoc) -> Result<(), ParserError> {
|
|
if let Some(saved_ctm) = self.named_coordinate_systems.get(name) {
|
|
self.graphics_state.ctm = *saved_ctm;
|
|
} else {
|
|
eprintln!(
|
|
"Warning: Couldn't find named coordinate system \"{}\" at {}",
|
|
name, loc
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn camera(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("Camera", &loc)?;
|
|
|
|
let camera_from_world = self.graphics_state.ctm;
|
|
let world_from_camera = camera_from_world.inverse();
|
|
|
|
self.named_coordinate_systems
|
|
.insert("camera".to_string(), world_from_camera);
|
|
|
|
let animated_world_from_cam = AnimatedTransform::new(
|
|
&world_from_camera.t[0],
|
|
self.graphics_state.transform_start_time,
|
|
&world_from_camera.t[1],
|
|
self.graphics_state.transform_end_time,
|
|
);
|
|
|
|
let rendering_space = RenderingCoordinateSystem::CameraWorld;
|
|
let camera_transform =
|
|
CameraTransform::from_world(animated_world_from_cam, rendering_space);
|
|
|
|
self.render_from_world = camera_transform.render_from_world();
|
|
|
|
let parameters = self.make_params(params, &loc)?;
|
|
|
|
self.current_camera = Some(CameraSceneEntity {
|
|
base: SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters,
|
|
},
|
|
camera_transform,
|
|
|
|
medium: self.graphics_state.current_outside_medium.clone(),
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn active_transform_all(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
|
self.graphics_state.active_transform_bits = Self::ALL_TRANSFORM_BITS;
|
|
Ok(())
|
|
}
|
|
|
|
fn active_transform_end_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
|
self.graphics_state.active_transform_bits = Self::END_TRANSFORM_BITS;
|
|
Ok(())
|
|
}
|
|
fn active_transform_start_time(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
|
self.graphics_state.active_transform_bits = Self::START_TRANSFORM_BITS;
|
|
Ok(())
|
|
}
|
|
|
|
fn transform_times(
|
|
&mut self,
|
|
start: Float,
|
|
end: Float,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("TransformTimes", &loc)?;
|
|
self.graphics_state.transform_start_time = start;
|
|
self.graphics_state.transform_end_time = end;
|
|
Ok(())
|
|
}
|
|
|
|
fn option(&mut self, _name: &str, _value: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
|
todo!()
|
|
}
|
|
|
|
fn pixel_filter(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("PixelFilter", &loc)?;
|
|
let parameters = self.make_params(params, &loc)?;
|
|
self.current_filter = Some(SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn film(
|
|
&mut self,
|
|
type_name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("Film", &loc)?;
|
|
let parameters = self.make_params(params, &loc)?;
|
|
self.current_film = Some(SceneEntity {
|
|
name: type_name.to_string(),
|
|
loc,
|
|
parameters,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn accelerator(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("Accelerator", &loc)?;
|
|
let parameters = self.make_params(params, &loc)?;
|
|
self.current_accelerator = Some(SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn integrator(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("Integrator", &loc)?;
|
|
let parameters = self.make_params(params, &loc)?;
|
|
self.current_integrator = Some(SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn make_named_medium(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_world("MakeNamedMaterial", &loc)?;
|
|
let curr_name = normalize_utf8(name);
|
|
|
|
if !self.named_material_names.insert(curr_name.to_string()) {
|
|
return Err(ParserError::Generic(
|
|
format!("Named material '{}' redefined.", name),
|
|
loc,
|
|
));
|
|
}
|
|
let parameters = self.make_params(params, &loc)?;
|
|
let entity = SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters,
|
|
};
|
|
|
|
self.scene.add_named_material(&curr_name, entity);
|
|
Ok(())
|
|
}
|
|
|
|
fn medium_interface(
|
|
&mut self,
|
|
inside_name: &str,
|
|
outside_name: &str,
|
|
_loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
let inside = normalize_utf8(inside_name);
|
|
let outside = normalize_utf8(outside_name);
|
|
|
|
self.graphics_state.current_inside_medium = inside;
|
|
self.graphics_state.current_outside_medium = outside;
|
|
Ok(())
|
|
}
|
|
|
|
fn sampler(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_options("Sampler", &loc)?;
|
|
let parameters = self.make_params(params, &loc)?;
|
|
self.current_sampler = Some(SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn world_begin(&mut self, loc: FileLoc, arena: &Arena) -> Result<(), ParserError> {
|
|
self.verify_options("WorldBegin", &loc)?;
|
|
self.current_block = BlockState::WorldBlock;
|
|
for i in 0..MAX_TRANSFORMS {
|
|
self.graphics_state.ctm[i] = Transform::default();
|
|
}
|
|
self.graphics_state.active_transform_bits = Self::ALL_TRANSFORM_BITS;
|
|
self.named_coordinate_systems
|
|
.insert("world".to_string(), self.graphics_state.ctm);
|
|
let scene = Arc::clone(&self.scene);
|
|
scene.set_options(
|
|
self.current_filter
|
|
.take()
|
|
.expect("Filter not set before WorldBegin"),
|
|
self.current_film
|
|
.take()
|
|
.expect("Film not set before WorldBegin"),
|
|
self.current_camera
|
|
.take()
|
|
.expect("Camera not set before WorldBegin"),
|
|
self.current_sampler
|
|
.take()
|
|
.expect("Sampler not set before WorldBegin"),
|
|
self.current_integrator
|
|
.take()
|
|
.expect("Integrator not set before WorldBegin"),
|
|
self.current_accelerator
|
|
.take()
|
|
.expect("Accelerator not set before WorldBegin"),
|
|
&arena,
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
fn attribute_begin(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
|
self.verify_world("AttributeBegin", &loc)?;
|
|
self.pushed_graphics_states
|
|
.push(self.graphics_state.clone());
|
|
self.push_stack.push(('a', loc));
|
|
Ok(())
|
|
}
|
|
|
|
fn attribute_end(&mut self, loc: FileLoc) -> Result<(), ParserError> {
|
|
self.verify_world("AttributeEnd", &loc)?;
|
|
if self.pushed_graphics_states.is_empty() {
|
|
return Err(ParserError::Generic(
|
|
"Unmatched AttributeEnd encountered".into(),
|
|
loc,
|
|
));
|
|
}
|
|
|
|
if let Some(state) = self.pushed_graphics_states.pop() {
|
|
self.graphics_state = state;
|
|
}
|
|
|
|
if let Some((kind, start_loc)) = self.push_stack.pop() {
|
|
if kind == 'o' {
|
|
return Err(ParserError::Generic(
|
|
format!("Mismatched nesting: open ObjectBegin from {}", start_loc),
|
|
loc,
|
|
));
|
|
} else {
|
|
debug_assert_eq!(kind, 'a', "Expected AttributeBegin on the stack");
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn attribute(
|
|
&mut self,
|
|
target: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
let current_attributes = match target {
|
|
"shape" => &mut self.graphics_state.shape_attributes,
|
|
"light" => &mut self.graphics_state.light_attributes,
|
|
"material" => &mut self.graphics_state.material_attributes,
|
|
"medium" => &mut self.graphics_state.medium_attributes,
|
|
"texture" => &mut self.graphics_state.texture_attributes,
|
|
_ => {
|
|
return Err(ParserError::Generic(
|
|
format!(
|
|
"Unknown attribute target \"{}\". Must be \"shape\", \"light\", \
|
|
\"material\", \"medium\", or \"texture\".",
|
|
target
|
|
),
|
|
loc,
|
|
));
|
|
}
|
|
};
|
|
|
|
let active_color_space = self.graphics_state.color_space.clone();
|
|
|
|
for mut p in params {
|
|
p.may_be_unused = true;
|
|
p.color_space = active_color_space.clone();
|
|
|
|
current_attributes.push(p.clone());
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn texture(
|
|
&mut self,
|
|
orig_name: &str,
|
|
type_name: &str,
|
|
tex_name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
arena: Arc<Arena>,
|
|
) -> Result<(), ParserError> {
|
|
eprintln!(
|
|
"TEXTURE: name='{}' type='{}' tex='{}'",
|
|
orig_name, type_name, tex_name
|
|
);
|
|
let name = normalize_utf8(orig_name);
|
|
self.verify_world("Texture", &loc)?;
|
|
let dict = ParameterDictionary::from_array(
|
|
params.clone(),
|
|
&self.graphics_state.texture_attributes,
|
|
self.graphics_state.color_space.clone(),
|
|
)?;
|
|
|
|
if type_name != "float" && type_name != "spectrum" {
|
|
return Err(ParserError::Generic(
|
|
format!(
|
|
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
|
|
tex_name
|
|
),
|
|
loc,
|
|
));
|
|
}
|
|
|
|
{
|
|
let names = if type_name == "float" {
|
|
&mut self.float_texture_names
|
|
} else {
|
|
&mut self.spectrum_texture_names
|
|
};
|
|
|
|
if names.contains(&name) {
|
|
return Err(ParserError::Generic(
|
|
format!("Redefining texture \"{}\".", name),
|
|
loc,
|
|
));
|
|
}
|
|
names.insert(name.to_string());
|
|
}
|
|
|
|
let base = SceneEntity {
|
|
name: tex_name.to_string(),
|
|
parameters: dict,
|
|
loc,
|
|
};
|
|
let entity = TextureSceneEntity {
|
|
base,
|
|
render_from_object: self.render_from_object(),
|
|
};
|
|
|
|
if type_name == "float" {
|
|
self.scene
|
|
.add_float_texture(name.to_string(), entity, arena);
|
|
} else {
|
|
self.scene
|
|
.add_spectrum_texture(name.to_string(), entity, arena);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn material(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_world("material", &loc)?;
|
|
let entity = SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters: ParameterDictionary::new(params.clone(), None).unwrap(),
|
|
};
|
|
self.graphics_state.current_material_name = self.scene.add_material(entity).to_string();
|
|
Ok(())
|
|
}
|
|
fn make_named_material(
|
|
&mut self,
|
|
_name: &str,
|
|
_params: ParsedParameterVector,
|
|
_loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
todo!()
|
|
}
|
|
|
|
fn named_material(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
|
todo!()
|
|
}
|
|
|
|
fn light_source(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_world("LightSource", &loc)?;
|
|
let dict = ParameterDictionary::from_array(
|
|
params.clone(),
|
|
&self.graphics_state.medium_attributes,
|
|
self.graphics_state.color_space.clone(),
|
|
)?;
|
|
|
|
let render_from_light = self.render_from_object();
|
|
|
|
let entity = LightSceneEntity {
|
|
transformed_base: TransformedSceneEntity {
|
|
base: SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters: dict,
|
|
},
|
|
render_from_object: render_from_light,
|
|
},
|
|
medium: self.graphics_state.current_outside_medium.clone(),
|
|
};
|
|
|
|
self.scene.add_light(entity);
|
|
Ok(())
|
|
}
|
|
|
|
fn area_light_source(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_world("AreaLightSource", &loc)?;
|
|
self.graphics_state.pending_area_light = Some(PendingAreaLight {
|
|
name: name.to_string(),
|
|
params: params.clone(),
|
|
loc,
|
|
});
|
|
Ok(())
|
|
}
|
|
|
|
fn shape(
|
|
&mut self,
|
|
name: &str,
|
|
params: ParsedParameterVector,
|
|
loc: FileLoc,
|
|
) -> Result<(), ParserError> {
|
|
self.verify_world("Shape", &loc)?;
|
|
|
|
let dict = ParameterDictionary::from_array(
|
|
params.clone(),
|
|
&self.graphics_state.shape_attributes,
|
|
self.graphics_state.color_space.clone(),
|
|
)?;
|
|
|
|
let render_from_object = self.render_from_object_at(0);
|
|
let object_from_render = render_from_object.inverse();
|
|
|
|
let light_index = if let Some(ref al) = self.graphics_state.pending_area_light {
|
|
let al_dict = self.make_params(al.params.clone(), &al.loc)?;
|
|
let light_entity = SceneEntity {
|
|
name: al.name.clone(),
|
|
loc: al.loc.clone(),
|
|
parameters: al_dict,
|
|
};
|
|
Some(self.scene.add_area_light(light_entity))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let material = if !self.graphics_state.current_material_name.is_empty() {
|
|
MaterialRef::Name(self.graphics_state.current_material_name.clone())
|
|
} else if let Some(idx) = self.graphics_state.current_material_index {
|
|
MaterialRef::Index(idx)
|
|
} else {
|
|
MaterialRef::None
|
|
};
|
|
|
|
let entity = ShapeSceneEntity {
|
|
base: SceneEntity {
|
|
name: name.to_string(),
|
|
loc,
|
|
parameters: dict,
|
|
},
|
|
render_from_object: Arc::new(render_from_object),
|
|
object_from_render: Arc::new(object_from_render),
|
|
reverse_orientation: self.graphics_state.reverse_orientation,
|
|
material,
|
|
light_index,
|
|
inside_medium: self.graphics_state.current_inside_medium.clone(),
|
|
outside_medium: self.graphics_state.current_outside_medium.clone(),
|
|
};
|
|
|
|
if self.active_instance_definition.is_some() {
|
|
self.active_instance_definition
|
|
.as_mut()
|
|
.unwrap()
|
|
.shapes
|
|
.push(entity)
|
|
} else {
|
|
self.scene.add_shape(entity);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn object_begin(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
|
Ok(())
|
|
}
|
|
fn object_end(&mut self, _loc: FileLoc) -> Result<(), ParserError> {
|
|
Ok(())
|
|
}
|
|
fn object_instance(&mut self, _name: &str, _loc: FileLoc) -> Result<(), ParserError> {
|
|
Ok(())
|
|
}
|
|
fn end_of_files(&mut self) -> Result<(), ParserError> {
|
|
Ok(())
|
|
}
|
|
}
|