From ad55aa4f6deafc10a1755eb271d4f6652e243d14 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Thu, 3 Aug 2017 20:32:07 -0700 Subject: [PATCH] Materials are now working in both Psychopath and PsychoBlend. Except that Emit is still not properly supported, because it needs special handling. --- psychoblend/assembly.py | 53 +++++++++++++++- src/parse/psy_surface_shader.rs | 104 +++++++++++++++++++++++++++++++- src/tracer.rs | 7 ++- 3 files changed, 158 insertions(+), 6 deletions(-) diff --git a/psychoblend/assembly.py b/psychoblend/assembly.py index e70bd24..ce391e7 100644 --- a/psychoblend/assembly.py +++ b/psychoblend/assembly.py @@ -12,6 +12,7 @@ class Assembly: self.objects = [] self.instances = [] + self.material_names = set() self.mesh_names = set() self.assembly_names = set() @@ -119,10 +120,19 @@ class Assembly: if should_export_mesh: self.mesh_names.add(mesh_name) self.objects += [Mesh(self.render_engine, ob, mesh_name)] + + # Get materials + for ms in ob.material_slots: + if ms != None: + if ms.material.name not in self.material_names: + self.material_names.add(ms.material.name) + self.materials += [Material(self.render_engine, ms.material)] + return mesh_name else: return None - + + def get_sphere_lamp(self, ob, group_prefix): name = group_prefix + "__" + escape_name(ob.name) self.objects += [SphereLamp(self.render_engine, ob, name)] @@ -281,5 +291,46 @@ class Instance: w.write("Data [$%s]\n" % self.data_name) for mat in self.time_xforms: w.write("Transform [%s]\n" % mat2str(mat.inverted())) + for ms in self.ob.material_slots: + if ms != None: + w.write("SurfaceShaderBind [$%s]\n" % escape_name(ms.material.name)) + break w.unindent() w.write("}\n") + + +class Material: + def __init__(self, render_engine, material): + self.mat = material + + def take_sample(self, render_engine, time, translation_offset): + # TODO: motion blur of material settings + pass + + def export(self, render_engine, w): + render_engine.update_stats("", "Psychopath: Exporting %s" % self.mat.name) + + w.write("SurfaceShader $%s {\n" % escape_name(self.mat.name)) + w.indent() + if self.mat.psychopath.surface_shader_type == 'Emit': + w.write("Type [Emit]\n") + color = self.mat.psychopath.color + w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2])) + elif self.mat.psychopath.surface_shader_type == 'Lambert': + w.write("Type [Lambert]\n") + color = self.mat.psychopath.color + w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2])) + elif self.mat.psychopath.surface_shader_type == 'GTR': + w.write("Type [GTR]\n") + color = self.mat.psychopath.color + w.write("Color [%f %f %f]\n" % (color[0], color[1], color[2])) + w.write("Roughness [%f]\n" % self.mat.psychopath.roughness) + w.write("TailShape [%f]\n" % self.mat.psychopath.tail_shape) + w.write("Fresnel [%f]\n" % self.mat.psychopath.fresnel) + else: + raise "Unsupported surface shader type '%s'" % self.mat.psychopath.surface_shader_type + w.unindent() + w.write("}\n") + + def cleanup(self): + pass diff --git a/src/parse/psy_surface_shader.rs b/src/parse/psy_surface_shader.rs index e1b8432..33c4fe7 100644 --- a/src/parse/psy_surface_shader.rs +++ b/src/parse/psy_surface_shader.rs @@ -35,7 +35,30 @@ pub fn parse_surface_shader<'a>( }; let shader = match type_name { - "Emit" => unimplemented!(), + "Emit" => { + 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 Emit SurfaceShader.", + )); + }; + + arena.alloc(SimpleSurfaceShader::Emit { color: color }) + } "Lambert" => { let color = if let Some((_, contents, byte_offset)) = tree.iter_leaf_children_with_type("Color").nth(0) @@ -60,7 +83,84 @@ pub fn parse_surface_shader<'a>( arena.alloc(SimpleSurfaceShader::Lambert { color: color }) } - "GTR" => unimplemented!(), + "GTR" => { + // Color + 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 GTR SurfaceShader.", + )); + }; + + // Roughness + let roughness = if let Some((_, contents, byte_offset)) = + tree.iter_leaf_children_with_type("Roughness").nth(0) + { + if let IResult::Done(_, roughness) = ws_f32(contents.as_bytes()) { + roughness + } else { + return Err(PsyParseError::UnknownError(byte_offset)); + } + } else { + return Err(PsyParseError::MissingNode( + tree.byte_offset(), + "Expected a Roughness field in GTR SurfaceShader.", + )); + }; + + // TailShape + let tail_shape = if let Some((_, contents, byte_offset)) = + tree.iter_leaf_children_with_type("TailShape").nth(0) + { + if let IResult::Done(_, tail_shape) = ws_f32(contents.as_bytes()) { + tail_shape + } else { + return Err(PsyParseError::UnknownError(byte_offset)); + } + } else { + return Err(PsyParseError::MissingNode( + tree.byte_offset(), + "Expected a TailShape field in GTR SurfaceShader.", + )); + }; + + // Fresnel + let fresnel = if let Some((_, contents, byte_offset)) = + tree.iter_leaf_children_with_type("Fresnel").nth(0) + { + if let IResult::Done(_, fresnel) = ws_f32(contents.as_bytes()) { + fresnel + } else { + return Err(PsyParseError::UnknownError(byte_offset)); + } + } else { + return Err(PsyParseError::MissingNode( + tree.byte_offset(), + "Expected a Fresnel field in GTR SurfaceShader.", + )); + }; + + arena.alloc(SimpleSurfaceShader::GTR { + color: color, + roughness: roughness, + tail_shape: tail_shape, + fresnel: fresnel, + }) + } _ => unimplemented!(), }; diff --git a/src/tracer.rs b/src/tracer.rs index 5df5967..4159b40 100644 --- a/src/tracer.rs +++ b/src/tracer.rs @@ -7,7 +7,7 @@ use scene::{Assembly, Object, InstanceType}; use surface::SurfaceIntersection; use transform_stack::TransformStack; use shading::{SurfaceShader, SimpleSurfaceShader}; -use color::XYZ; +use color::{XYZ, rec709_to_xyz}; pub struct Tracer<'a> { @@ -185,8 +185,9 @@ impl<'a> TracerInner<'a> { ) { match *obj { Object::Surface(surface) => { - let unassigned_shader = - SimpleSurfaceShader::Lambert { color: XYZ::new(1.0, 0.0, 1.0) }; + let unassigned_shader = SimpleSurfaceShader::Lambert { + color: XYZ::from_tuple(rec709_to_xyz((1.0, 0.0, 1.0))), + }; let shader = surface_shader.unwrap_or(&unassigned_shader); surface.intersect_rays(