pbrt/src/core/scene/builder.rs

620 lines
20 KiB
Rust

use super::BasicScene;
use super::entities::*;
use crate::Arena;
use crate::spectra::get_colorspace_device;
use crate::utils::error::FileLoc;
use crate::utils::normalize_utf8;
use crate::utils::parameters::error_exit;
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
use crate::utils::parser::ParserTarget;
use shared::Float;
use shared::core::camera::CameraTransform;
use shared::core::geometry::Vector3f;
use shared::core::options::RenderingCoordinateSystem;
use shared::spectra::RGBColorSpace;
use shared::utils::transform;
use shared::utils::transform::{AnimatedTransform, Transform};
use std::collections::{HashMap, HashSet};
use std::ops::{Index, IndexMut};
use std::sync::Arc;
const MAX_TRANSFORMS: usize = 2;
#[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(Default, 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 area_light_name: String,
pub area_light_params: ParsedParameterVector,
pub area_light_loc: FileLoc,
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,
}
#[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;
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) {
if self.current_block != BlockState::WorldBlock {
log::error!("{}: {} not allowed outside WorldBlock", loc, name);
}
}
fn verify_options(&self, name: &str, loc: &FileLoc) {
if self.current_block != BlockState::OptionsBlock {
log::error!("{}: {} not allowed inside WorldBlock", loc, name);
}
}
}
impl ParserTarget for BasicSceneBuilder {
fn reverse_orientation(&mut self, loc: FileLoc) {
self.verify_world("ReverseOrientation", &loc);
self.graphics_state.reverse_orientation = !self.graphics_state.reverse_orientation;
}
fn color_space(&mut self, name: &str, loc: FileLoc) {
let stdcs = get_colorspace_device();
let _ = match stdcs.get_named(name) {
Ok(cs) => {
self.graphics_state.color_space = unsafe { Some(Arc::new(*cs.as_ref())) };
}
Err(_) => {
eprintln!("Error: Color space '{}' unknown at {}", name, loc);
}
};
}
fn identity(&mut self, _loc: FileLoc) {
self.for_active_transforms(|_| Transform::identity());
}
fn translate(&mut self, dx: Float, dy: Float, dz: Float, _loc: FileLoc) {
let t = Transform::translate(Vector3f::new(dx, dy, dz));
self.for_active_transforms(|cur| cur * &t);
}
fn rotate(&mut self, angle: Float, ax: Float, ay: Float, az: Float, _loc: FileLoc) {
let t = Transform::rotate_around_axis(angle, Vector3f::new(ax, ay, az));
self.for_active_transforms(|cur| cur * &t);
}
fn scale(&mut self, sx: Float, sy: Float, sz: Float, _loc: FileLoc) {
let t = Transform::scale(sx, sy, sz);
self.for_active_transforms(|cur| cur * &t);
}
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,
) {
let result = transform::look_at((ex, ey, ez), (lx, ly, lz), (ux, uy, uz));
match result {
Ok(t) => {
self.for_active_transforms(|cur| cur * &t);
}
Err(e) => {
eprintln!("Error: {} at {}", e, loc);
}
}
}
fn concat_transform(&mut self, m: &[Float; 16], loc: FileLoc) {
let result = Transform::from_flat(m);
match result {
Ok(t) => {
self.for_active_transforms(|cur| cur * &t);
}
Err(e) => {
eprintln!("Error: {} at {}", e, loc);
}
}
}
fn transform(&mut self, m: &[Float; 16], loc: FileLoc) {
let result = Transform::from_flat(m);
match result {
Ok(t) => {
self.for_active_transforms(|_| t);
}
Err(e) => {
eprintln!("Error: {} at {}", e, loc);
}
}
}
fn coordinate_system(&mut self, name: &str, _loc: FileLoc) {
self.named_coordinate_systems
.insert(name.to_string(), self.graphics_state.ctm);
}
fn coord_sys_transform(&mut self, name: &str, loc: FileLoc) {
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
);
}
}
fn camera(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
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_from_world.t[0];
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
self.current_camera = Some(CameraSceneEntity {
base: SceneEntity {
name: name.to_string(),
loc,
parameters,
},
camera_transform,
medium: self.graphics_state.current_outside_medium.clone(),
});
}
fn active_transform_all(&mut self, _loc: FileLoc) {
self.graphics_state.active_transform_bits = Self::ALL_TRANSFORM_BITS;
}
fn active_transform_end_time(&mut self, _loc: FileLoc) {
self.graphics_state.active_transform_bits = Self::END_TRANSFORM_BITS;
}
fn active_transform_start_time(&mut self, _loc: FileLoc) {
self.graphics_state.active_transform_bits = Self::START_TRANSFORM_BITS;
}
fn transform_times(&mut self, start: Float, end: Float, loc: FileLoc) {
self.verify_options("TransformTimes", &loc);
self.graphics_state.transform_start_time = start;
self.graphics_state.transform_end_time = end;
}
fn option(&mut self, _name: &str, _value: &str, _loc: FileLoc) {
todo!()
}
fn pixel_filter(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
self.verify_options("PixelFilter", &loc);
self.current_filter = Some(SceneEntity {
name: name.to_string(),
loc,
parameters,
});
}
fn film(&mut self, type_name: &str, params: &ParsedParameterVector, loc: FileLoc) {
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
self.verify_options("Film", &loc);
self.current_filter = Some(SceneEntity {
name: type_name.to_string(),
loc,
parameters,
});
}
fn accelerator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
self.verify_options("PixelFilter", &loc);
self.current_filter = Some(SceneEntity {
name: name.to_string(),
loc,
parameters,
});
}
fn integrator(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
self.verify_options("PixelFilter", &loc);
self.current_filter = Some(SceneEntity {
name: name.to_string(),
loc,
parameters,
});
}
fn make_named_medium(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
let curr_name = normalize_utf8(name);
self.verify_world("MakeNamedMaterial", &loc);
if !self.named_material_names.insert(curr_name.to_string()) {
eprintln!("Error: {}: named material '{}' redefined.", loc, name);
return;
}
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
let entity = SceneEntity {
name: name.to_string(),
loc,
parameters,
};
self.scene.add_named_material(&curr_name, entity);
}
fn medium_interface(&mut self, inside_name: &str, outside_name: &str, _loc: FileLoc) {
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;
}
fn sampler(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
let parameters =
ParameterDictionary::new(params.clone(), self.graphics_state.color_space.clone());
self.verify_options("Sampler", &loc);
self.current_sampler = Some(SceneEntity {
name: name.to_string(),
loc,
parameters,
})
}
fn world_begin(&mut self, loc: FileLoc, arena: &Arena) {
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,
);
}
fn attribute_begin(&mut self, loc: FileLoc) {
self.verify_world("AttributeBegin", &loc);
self.pushed_graphics_states
.push(self.graphics_state.clone());
self.push_stack.push(('a', loc));
}
fn attribute_end(&mut self, loc: FileLoc) {
self.verify_world("AttributeEnd", &loc);
if self.pushed_graphics_states.is_empty() {
log::error!(
"[{:?}] Unmatched AttributeEnd encountered. Ignoring it.",
loc
);
return;
}
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' {
log::error!(
"[{:?}] Mismatched nesting: open ObjectBegin from {} at AttributeEnd",
loc,
start_loc
);
std::process::exit(1);
} else {
debug_assert_eq!(kind, 'a', "Expected AttributeBegin on the stack");
}
}
}
fn attribute(&mut self, target: &str, params: ParsedParameterVector, loc: FileLoc) {
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,
_ => {
log::error!(
"[{:?}] Unknown attribute target \"{}\". Must be \"shape\", \"light\", \
\"material\", \"medium\", or \"texture\".",
loc,
target
);
return;
}
};
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());
}
}
fn texture(
&mut self,
orig_name: &str,
type_name: &str,
tex_name: &str,
params: &ParsedParameterVector,
loc: FileLoc,
arena: Arc<Arena>,
) {
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" {
error_exit(
Some(&loc),
&format!(
"{}: texture type unknown. Must be \"float\" or \"spectrum\".",
tex_name
),
);
}
{
let names = if type_name == "float" {
&mut self.float_texture_names
} else {
&mut self.spectrum_texture_names
};
if names.contains(&name) {
error_exit(Some(&loc), &format!("Redefining texture \"{}\".", name));
}
names.insert(name.to_string());
}
let base = SceneEntity {
name: tex_name.to_string(),
parameters: dict,
loc,
};
let entity = TextureSceneEntity {
base,
render_from_object: self.graphics_state.ctm[0].clone(),
};
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);
}
}
fn material(&mut self, name: &str, params: &ParsedParameterVector, loc: FileLoc) {
self.verify_world("material", &loc);
let entity = SceneEntity {
name: name.to_string(),
loc,
parameters: ParameterDictionary::new(params.clone(), None),
};
self.graphics_state.current_material_name = self.scene.add_material(entity).to_string();
}
fn make_named_material(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
todo!()
}
fn named_material(&mut self, _name: &str, _loc: FileLoc) {
todo!()
}
fn light_source(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
todo!()
}
fn area_light_source(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
todo!()
}
fn shape(&mut self, _name: &str, _params: &ParsedParameterVector, _loc: FileLoc) {
todo!()
}
fn object_begin(&mut self, _name: &str, _loc: FileLoc) {
todo!()
}
fn object_end(&mut self, _loc: FileLoc) {
todo!()
}
fn object_instance(&mut self, _name: &str, _loc: FileLoc) {
todo!()
}
fn end_of_files(&mut self) {
todo!()
}
}