From 516803e78aae4e9ed198404c6b85cfeada8c6461 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Thu, 3 Aug 2017 19:31:58 -0700 Subject: [PATCH] Got basic material parsing and rendering working. Currently only Lambert is supported. --- src/parse/mod.rs | 1 + src/parse/psy_assembly.rs | 50 +++++++++++++++--------- src/parse/psy_surface_shader.rs | 68 +++++++++++++++++++++++++++++++++ src/scene/assembly.rs | 45 +++++++++++++++++++++- src/shading/mod.rs | 4 +- src/surface/mod.rs | 2 + src/surface/triangle_mesh.rs | 15 ++------ src/tracer.rs | 25 +++++++++++- 8 files changed, 175 insertions(+), 35 deletions(-) create mode 100644 src/parse/psy_surface_shader.rs diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 7feb859..2405194 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -2,6 +2,7 @@ mod data_tree; mod psy_assembly; mod psy_light; mod psy_mesh_surface; +mod psy_surface_shader; mod psy; pub mod basics; diff --git a/src/parse/psy_assembly.rs b/src/parse/psy_assembly.rs index 36417be..8fea94c 100644 --- a/src/parse/psy_assembly.rs +++ b/src/parse/psy_assembly.rs @@ -9,6 +9,7 @@ use scene::{Assembly, AssemblyBuilder, Object}; use super::DataTree; use super::psy_light::{parse_sphere_light, parse_rectangle_light}; use super::psy_mesh_surface::parse_mesh_surface; +use super::psy_surface_shader::parse_surface_shader; use super::psy::{parse_matrix, PsyParseError}; @@ -45,6 +46,22 @@ pub fn parse_assembly<'a>( child.iter_leaf_children_with_type("Data").nth(0).unwrap().1 }; + // Get surface shader binding, if any. + let surface_shader_name = if child + .iter_leaf_children_with_type("SurfaceShaderBind") + .count() > 0 + { + Some( + child + .iter_leaf_children_with_type("SurfaceShaderBind") + .nth(0) + .unwrap() + .1, + ) + } else { + None + }; + // Get xforms let mut xforms = Vec::new(); for (_, contents, _) in child.iter_leaf_children_with_type("Transform") { @@ -53,7 +70,7 @@ pub fn parse_assembly<'a>( // Add instance if builder.name_exists(name) { - builder.add_instance(name, Some(&xforms)); + builder.add_instance(name, surface_shader_name, Some(&xforms)); } else { return Err(PsyParseError::InstancedMissingData( child.iter_leaf_children_with_type("Data").nth(0).unwrap().2, @@ -66,6 +83,20 @@ pub fn parse_assembly<'a>( } } + // SurfaceShader + "SurfaceShader" => { + if let DataTree::Internal { ident: Some(ident), .. } = *child { + builder.add_surface_shader(ident, parse_surface_shader(arena, child)?); + } else { + // TODO: error condition of some kind, because no ident + panic!( + "SurfaceShader encountered that was a leaf, but SurfaceShaders cannot \ + be a leaf: {}", + child.byte_offset() + ); + } + } + // MeshSurface "MeshSurface" => { if let DataTree::Internal { ident: Some(ident), .. } = *child { @@ -109,17 +140,6 @@ pub fn parse_assembly<'a>( } } - // Surface shader - "SurfaceShader" => { - if let DataTree::Internal { ident: Some(_), .. } = *child { - // TODO - //unimplemented!() - } else { - // No ident - return Err(PsyParseError::UnknownError(child.byte_offset())); - } - } - _ => { // TODO: some kind of error, because not a known type name } @@ -143,12 +163,6 @@ pub fn parse_assembly<'a>( // else if (child.type == "Sphere") { // assembly->add_object(child.name, parse_sphere(child)); // } - // - // // Surface shader - // else if (child.type == "SurfaceShader") { - // assembly->add_surface_shader(child.name, parse_surface_shader(child)); - // } - // } } } else { diff --git a/src/parse/psy_surface_shader.rs b/src/parse/psy_surface_shader.rs new file mode 100644 index 0000000..e1b8432 --- /dev/null +++ b/src/parse/psy_surface_shader.rs @@ -0,0 +1,68 @@ +#![allow(dead_code)] + +use std::result::Result; + +use nom::IResult; + +use mem_arena::MemArena; + +use color::{XYZ, rec709_e_to_xyz}; +use shading::{SurfaceShader, SimpleSurfaceShader}; + +use super::basics::ws_f32; +use super::DataTree; +use super::psy::PsyParseError; + + +// pub struct TriangleMesh { +// time_samples: usize, +// geo: Vec<(Point, Point, Point)>, +// indices: Vec, +// accel: BVH, +// } + +pub fn parse_surface_shader<'a>( + arena: &'a MemArena, + tree: &'a DataTree, +) -> Result<&'a SurfaceShader, PsyParseError> { + let type_name = if let Some((_, text, _)) = tree.iter_leaf_children_with_type("Type").nth(0) { + text.trim() + } else { + return Err(PsyParseError::MissingNode( + tree.byte_offset(), + "Expected a Type field in SurfaceShader.", + )); + }; + + let shader = match type_name { + "Emit" => unimplemented!(), + "Lambert" => { + let color = if let Some((_, contents, byte_offset)) = + tree.iter_leaf_children_with_type("Color").nth(0) + { + if let IResult::Done(_, color) = + closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) + { + // TODO: handle color space conversions properly. + // Probably will need a special color type with its + // own parser...? + XYZ::from_tuple(rec709_e_to_xyz(color)) + } else { + // Found color, but its contents is not in the right format + return Err(PsyParseError::UnknownError(byte_offset)); + } + } else { + return Err(PsyParseError::MissingNode( + tree.byte_offset(), + "Expected a Color field in Lambert SurfaceShader.", + )); + }; + + arena.alloc(SimpleSurfaceShader::Lambert { color: color }) + } + "GTR" => unimplemented!(), + _ => unimplemented!(), + }; + + Ok(shader) +} diff --git a/src/scene/assembly.rs b/src/scene/assembly.rs index d4c4a22..ba7ba4c 100644 --- a/src/scene/assembly.rs +++ b/src/scene/assembly.rs @@ -11,6 +11,7 @@ use lerp::lerp_slice; use light::LightSource; use math::{Matrix4x4, Vector}; use surface::{Surface, SurfaceIntersection}; +use shading::SurfaceShader; use transform_stack::TransformStack; @@ -21,6 +22,9 @@ pub struct Assembly<'a> { pub light_instances: &'a [Instance], pub xforms: &'a [Matrix4x4], + // Surface shader list + pub surface_shaders: &'a [&'a SurfaceShader], + // Object list pub objects: &'a [Object<'a>], @@ -150,6 +154,10 @@ pub struct AssemblyBuilder<'a> { instances: Vec, xforms: Vec, + // Shader list + surface_shaders: Vec<&'a SurfaceShader>, + surface_shader_map: HashMap, // map Name -> Index + // Object list objects: Vec>, object_map: HashMap, // map Name -> Index @@ -166,6 +174,8 @@ impl<'a> AssemblyBuilder<'a> { arena: arena, instances: Vec::new(), xforms: Vec::new(), + surface_shaders: Vec::new(), + surface_shader_map: HashMap::new(), objects: Vec::new(), object_map: HashMap::new(), assemblies: Vec::new(), @@ -173,6 +183,20 @@ impl<'a> AssemblyBuilder<'a> { } } + pub fn add_surface_shader(&mut self, name: &str, shader: &'a SurfaceShader) { + // Make sure the name hasn't already been used. + if self.surface_shader_map.contains_key(name) { + panic!("Attempted to add surface shader to assembly with a name that already exists."); + } + + // Add shader + self.surface_shader_map.insert( + name.to_string(), + self.surface_shaders.len(), + ); + self.surface_shaders.push(shader); + } + pub fn add_object(&mut self, name: &str, obj: Object<'a>) { // Make sure the name hasn't already been used. if self.name_exists(name) { @@ -201,7 +225,12 @@ impl<'a> AssemblyBuilder<'a> { self.assemblies.push(asmb); } - pub fn add_instance(&mut self, name: &str, xforms: Option<&[Matrix4x4]>) { + pub fn add_instance( + &mut self, + name: &str, + surface_shader_name: Option<&str>, + xforms: Option<&[Matrix4x4]>, + ) { // Make sure name exists if !self.name_exists(name) { panic!("Attempted to add instance with a name that doesn't exist."); @@ -219,6 +248,12 @@ impl<'a> AssemblyBuilder<'a> { Instance { instance_type: InstanceType::Object, data_index: self.object_map[name], + surface_shader_index: surface_shader_name.map(|name| { + *self.surface_shader_map.get(name).expect(&format!( + "Unknown surface shader '{}'.", + name + )) + }), id: self.instances.len(), transform_indices: xforms.map( |xf| (self.xforms.len(), self.xforms.len() + xf.len()), @@ -228,6 +263,12 @@ impl<'a> AssemblyBuilder<'a> { Instance { instance_type: InstanceType::Assembly, data_index: self.assembly_map[name], + surface_shader_index: surface_shader_name.map(|name| { + *self.surface_shader_map.get(name).expect(&format!( + "Unknown surface shader '{}'.", + name + )) + }), id: self.instances.len(), transform_indices: xforms.map( |xf| (self.xforms.len(), self.xforms.len() + xf.len()), @@ -303,6 +344,7 @@ impl<'a> AssemblyBuilder<'a> { instances: self.arena.copy_slice(&self.instances), light_instances: self.arena.copy_slice(&light_instances), xforms: self.arena.copy_slice(&self.xforms), + surface_shaders: self.arena.copy_slice(&self.surface_shaders), objects: self.arena.copy_slice(&self.objects), assemblies: self.arena.copy_slice(&self.assemblies), object_accel: object_accel, @@ -370,6 +412,7 @@ pub enum Object<'a> { pub struct Instance { pub instance_type: InstanceType, pub data_index: usize, + pub surface_shader_index: Option, pub id: usize, pub transform_indices: Option<(usize, usize)>, } diff --git a/src/shading/mod.rs b/src/shading/mod.rs index 4ab97b9..3c7bcef 100644 --- a/src/shading/mod.rs +++ b/src/shading/mod.rs @@ -7,7 +7,7 @@ use self::surface_closure::{SurfaceClosureUnion, EmitClosure, LambertClosure, GT use surface::SurfaceIntersectionData; /// Trait for surface shaders. -pub trait SurfaceShader: Debug { +pub trait SurfaceShader: Debug + Sync { /// Takes the result of a surface intersection and returns the surface /// closure to be evaluated at that intersection point. fn shade(&self, data: &SurfaceIntersectionData, wavelength: f32) -> SurfaceClosureUnion; @@ -24,7 +24,7 @@ pub trait SurfaceShader: Debug { /// are no ordinary donuts. To call them large is actually doing /// them a great injustice, for they are each the size of a small /// building. -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] pub enum SimpleSurfaceShader { Emit { color: XYZ }, Lambert { color: XYZ }, diff --git a/src/surface/mod.rs b/src/surface/mod.rs index 5fee6f2..f2134c2 100644 --- a/src/surface/mod.rs +++ b/src/surface/mod.rs @@ -8,6 +8,7 @@ use std::fmt::Debug; use boundable::Boundable; use math::{Point, Vector, Normal, Matrix4x4}; use ray::{Ray, AccelRay}; +use shading::SurfaceShader; use shading::surface_closure::SurfaceClosureUnion; @@ -17,6 +18,7 @@ pub trait Surface: Boundable + Debug + Sync { accel_rays: &mut [AccelRay], wrays: &[Ray], isects: &mut [SurfaceIntersection], + shader: &SurfaceShader, space: &[Matrix4x4], ); } diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index 4ecd8c7..d718178 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -5,12 +5,11 @@ use mem_arena::MemArena; use accel::BVH4; use bbox::BBox; use boundable::Boundable; -use color::XYZ; use fp_utils::fp_gamma; use lerp::lerp_slice; use math::{Point, Normal, Matrix4x4, dot, cross}; use ray::{Ray, AccelRay}; -use shading::{SurfaceShader, SimpleSurfaceShader}; +use shading::SurfaceShader; use super::{Surface, SurfaceIntersection, SurfaceIntersectionData}; use super::triangle; @@ -123,6 +122,7 @@ impl<'a> Surface for TriangleMesh<'a> { accel_rays: &mut [AccelRay], wrays: &[Ray], isects: &mut [SurfaceIntersection], + shader: &SurfaceShader, space: &[Matrix4x4], ) { // Precalculate transform for non-motion blur cases @@ -249,16 +249,7 @@ impl<'a> Surface for TriangleMesh<'a> { // Fill in intersection data isects[r.id as usize] = SurfaceIntersection::Hit { intersection_data: intersection_data, - // TODO: get surface shader from user-defined shader. - closure: SimpleSurfaceShader::Lambert { - color: XYZ::new(0.8, 0.8, 0.8), - }.shade(&intersection_data, wr.wavelength), - // closure: SimpleSurfaceShader::GTR { - // color: XYZ::new(0.8, 0.8, 0.8), - // roughness: 0.1, - // tail_shape: 2.0, - // fresnel: 1.0, - // }.shade(&intersection_data, wr.wavelength), + closure: shader.shade(&intersection_data, wr.wavelength), }; r.max_t = t; } diff --git a/src/tracer.rs b/src/tracer.rs index 25733d5..5df5967 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -6,6 +6,8 @@ use ray::{Ray, AccelRay}; use scene::{Assembly, Object, InstanceType}; use surface::SurfaceIntersection; use transform_stack::TransformStack; +use shading::{SurfaceShader, SimpleSurfaceShader}; +use color::XYZ; pub struct Tracer<'a> { @@ -128,6 +130,9 @@ impl<'a> TracerInner<'a> { InstanceType::Object => { self.trace_object( &assembly.objects[inst.data_index], + inst.surface_shader_index.map( + |i| assembly.surface_shaders[i], + ), wrays, ray_set, ); @@ -171,10 +176,26 @@ impl<'a> TracerInner<'a> { ); } - fn trace_object<'b>(&'b mut self, obj: &Object, wrays: &[Ray], rays: &mut [AccelRay]) { + fn trace_object<'b>( + &'b mut self, + obj: &Object, + surface_shader: Option<&SurfaceShader>, + wrays: &[Ray], + rays: &mut [AccelRay], + ) { match *obj { Object::Surface(surface) => { - surface.intersect_rays(rays, wrays, &mut self.isects, self.xform_stack.top()); + let unassigned_shader = + SimpleSurfaceShader::Lambert { color: XYZ::new(1.0, 0.0, 1.0) }; + let shader = surface_shader.unwrap_or(&unassigned_shader); + + surface.intersect_rays( + rays, + wrays, + &mut self.isects, + shader, + self.xform_stack.top(), + ); } Object::Light(_) => {