diff --git a/psychoblend/assembly.py b/psychoblend/assembly.py index 8d5c9fd..11b090d 100644 --- a/psychoblend/assembly.py +++ b/psychoblend/assembly.py @@ -166,11 +166,15 @@ class Mesh: w.write("SubdivisionSurface $%s {\n" % self.name) w.indent() - # Write vertices + # Write vertices and (if it's smooth shaded) normals for ti in range(len(self.time_meshes)): w.write("Vertices [") w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.co]), False) w.write("]\n", False) + if self.time_meshes[0].polygons[0].use_smooth and self.ob.data.psychopath.is_subdivision_surface == False: + w.write("Normals [") + w.write(" ".join([("%f" % i) for vert in self.time_meshes[ti].vertices for i in vert.normal]), False) + w.write("]\n", False) # Write face vertex counts w.write("FaceVertCounts [") diff --git a/src/parse/psy_mesh_surface.rs b/src/parse/psy_mesh_surface.rs index c47bc74..06ee27d 100644 --- a/src/parse/psy_mesh_surface.rs +++ b/src/parse/psy_mesh_surface.rs @@ -6,7 +6,7 @@ use nom::IResult; use mem_arena::MemArena; -use math::Point; +use math::{Point, Normal}; use surface::triangle_mesh::TriangleMesh; use super::basics::{ws_usize, ws_f32}; @@ -26,6 +26,7 @@ pub fn parse_mesh_surface<'a>( tree: &'a DataTree, ) -> Result, PsyParseError> { let mut verts = Vec::new(); // Vec of vecs, one for each time sample + let mut normals = Vec::new(); // Vec of vecs, on for each time sample let mut face_vert_counts = Vec::new(); let mut face_vert_indices = Vec::new(); @@ -54,6 +55,30 @@ pub fn parse_mesh_surface<'a>( assert_eq!(vert_count, vs.len()); } + // Get normals, if they exist + for (_, text, _) in tree.iter_leaf_children_with_type("Normals") { + let mut raw_text = text.trim().as_bytes(); + + // Collect verts for this time sample + let mut tnormals = Vec::new(); + while let IResult::Done(remaining, nor) = + closure!(tuple!(ws_f32, ws_f32, ws_f32))(raw_text) + { + raw_text = remaining; + + tnormals.push(Normal::new(nor.0, nor.1, nor.2).normalized()); + } + normals.push(tnormals); + } + + // Make sure normal's time samples and vert count match the vertices + if !normals.is_empty() { + assert_eq!(normals.len(), verts.len()); + for ns in &normals { + assert_eq!(vert_count, ns.len()); + } + } + // Get face vert counts if let Some((_, text, _)) = tree.iter_leaf_children_with_type("FaceVertCounts").nth(0) { let mut raw_text = text.trim().as_bytes(); @@ -101,6 +126,11 @@ pub fn parse_mesh_surface<'a>( Ok(TriangleMesh::from_verts_and_indices( arena, verts, + if normals.is_empty() { + None + } else { + Some(normals) + }, tri_vert_indices, )) } diff --git a/src/surface/triangle_mesh.rs b/src/surface/triangle_mesh.rs index 547d918..ab21470 100644 --- a/src/surface/triangle_mesh.rs +++ b/src/surface/triangle_mesh.rs @@ -8,7 +8,7 @@ use boundable::Boundable; use color::XYZ; use fp_utils::fp_gamma; use lerp::lerp_slice; -use math::{Point, Matrix4x4, cross}; +use math::{Point, Normal, Matrix4x4, dot, cross}; use ray::{Ray, AccelRay}; use shading::surface_closure::{SurfaceClosureUnion, GTRClosure, LambertClosure}; @@ -20,6 +20,7 @@ use super::triangle; pub struct TriangleMesh<'a> { time_sample_count: usize, vertices: &'a [Point], // Vertices, with the time samples for each vertex stored contiguously + normals: Option<&'a [Normal]>, // Vertex normals, organized the same as `vertices` indices: &'a [(u32, u32, u32, u32)], // (v0_idx, v1_idx, v2_idx, original_tri_idx) accel: BVH4<'a>, } @@ -28,6 +29,7 @@ impl<'a> TriangleMesh<'a> { pub fn from_verts_and_indices<'b>( arena: &'b MemArena, verts: Vec>, + vert_normals: Option>>, tri_indices: Vec<(usize, usize, usize)>, ) -> TriangleMesh<'b> { let vert_count = verts[0].len(); @@ -48,6 +50,25 @@ impl<'a> TriangleMesh<'a> { vertices }; + // Copy vertex normals, if any, organizing them the same as vertices + // above. + let normals = match vert_normals { + Some(ref vnors) => { + let mut normals = + unsafe { arena.alloc_array_uninitialized(vert_count * time_sample_count) }; + + for vi in 0..vert_count { + for ti in 0..time_sample_count { + normals[(vi * time_sample_count) + ti] = vnors[ti][vi]; + } + } + + Some(&normals[..]) + } + + None => None, + }; + // Copy triangle vertex indices over, appending the triangle index itself to the tuple let mut indices = { let mut indices = unsafe { arena.alloc_array_uninitialized(tri_indices.len()) }; @@ -82,6 +103,7 @@ impl<'a> TriangleMesh<'a> { TriangleMesh { time_sample_count: time_sample_count, vertices: vertices, + normals: normals, indices: indices, accel: accel, } @@ -110,106 +132,138 @@ impl<'a> Surface for TriangleMesh<'a> { Matrix4x4::new() }; - self.accel - .traverse( - &mut accel_rays[..], self.indices, |tri_indices, rs| { - for r in rs { - let wr = &wrays[r.id as usize]; + self.accel.traverse( + &mut accel_rays[..], + self.indices, + |tri_indices, rs| { + for r in rs { + let wr = &wrays[r.id as usize]; - // Get triangle - let tri = { - let p0_slice = &self.vertices[ - (tri_indices.0 as usize * self.time_sample_count).. - ((tri_indices.0 as usize + 1) * self.time_sample_count) - ]; - let p1_slice = &self.vertices[ - (tri_indices.1 as usize * self.time_sample_count).. - ((tri_indices.1 as usize + 1) * self.time_sample_count) - ]; - let p2_slice = &self.vertices[ - (tri_indices.2 as usize * self.time_sample_count).. - ((tri_indices.2 as usize + 1) * self.time_sample_count) - ]; + // Get triangle + let tri = { + let p0_slice = &self.vertices[(tri_indices.0 as usize * + self.time_sample_count).. + ((tri_indices.0 as usize + 1) * + self.time_sample_count)]; + let p1_slice = &self.vertices[(tri_indices.1 as usize * + self.time_sample_count).. + ((tri_indices.1 as usize + 1) * + self.time_sample_count)]; + let p2_slice = &self.vertices[(tri_indices.2 as usize * + self.time_sample_count).. + ((tri_indices.2 as usize + 1) * + self.time_sample_count)]; - let p0 = lerp_slice(p0_slice, wr.time); - let p1 = lerp_slice(p1_slice, wr.time); - let p2 = lerp_slice(p2_slice, wr.time); + let p0 = lerp_slice(p0_slice, wr.time); + let p1 = lerp_slice(p1_slice, wr.time); + let p2 = lerp_slice(p2_slice, wr.time); - (p0, p1, p2) - }; + (p0, p1, p2) + }; - // Transform triangle as necessary, and get transform - // space. - let (mat_space, tri) = if !space.is_empty() { - if space.len() > 1 { - // Per-ray transform, for motion blur - let mat_space = lerp_slice(space, wr.time).inverse(); - (mat_space, - (tri.0 * mat_space, - tri.1 * mat_space, - tri.2 * mat_space) - ) - } else { - // Same transform for all rays - (static_mat_space, - (tri.0 * static_mat_space, - tri.1 * static_mat_space, - tri.2 * static_mat_space) - ) - } + // Transform triangle as necessary, and get transform + // space. + let (mat_space, tri) = if !space.is_empty() { + if space.len() > 1 { + // Per-ray transform, for motion blur + let mat_space = lerp_slice(space, wr.time).inverse(); + (mat_space, ( + tri.0 * mat_space, + tri.1 * mat_space, + tri.2 * mat_space, + )) } else { - // No transforms - (Matrix4x4::new(), tri) - }; + // Same transform for all rays + (static_mat_space, ( + tri.0 * static_mat_space, + tri.1 * static_mat_space, + tri.2 * static_mat_space, + )) + } + } else { + // No transforms + (Matrix4x4::new(), tri) + }; - // Test ray against triangle - if let Some((t, b0, b1, b2)) = triangle::intersect_ray(wr, tri) { - if t < r.max_t { - if r.is_occlusion() { - isects[r.id as usize] = SurfaceIntersection::Occlude; - r.mark_done(); + // Test ray against triangle + if let Some((t, b0, b1, b2)) = triangle::intersect_ray(wr, tri) { + if t < r.max_t { + if r.is_occlusion() { + isects[r.id as usize] = SurfaceIntersection::Occlude; + r.mark_done(); + } else { + // Calculate intersection point and error magnitudes + let pos = ((tri.0.into_vector() * b0) + (tri.1.into_vector() * b1) + + (tri.2.into_vector() * b2)) + .into_point(); + + let pos_err = (((tri.0.into_vector().abs() * b0) + + (tri.1.into_vector().abs() * b1) + + (tri.2.into_vector().abs() * b2)) * + fp_gamma(7)).co + .h_max(); + + // Calculate geometric surface normal + let geo_normal = cross(tri.0 - tri.1, tri.0 - tri.2).into_normal(); + + // Calculate interpolated surface normal, if any + let shading_normal = if let Some(normals) = self.normals { + let n0_slice = &normals[(tri_indices.0 as usize * + self.time_sample_count).. + ((tri_indices.0 as usize + 1) * + self.time_sample_count)]; + let n1_slice = &normals[(tri_indices.1 as usize * + self.time_sample_count).. + ((tri_indices.1 as usize + 1) * + self.time_sample_count)]; + let n2_slice = &normals[(tri_indices.2 as usize * + self.time_sample_count).. + ((tri_indices.2 as usize + 1) * + self.time_sample_count)]; + + let n0 = lerp_slice(n0_slice, wr.time).normalized(); + let n1 = lerp_slice(n1_slice, wr.time).normalized(); + let n2 = lerp_slice(n2_slice, wr.time).normalized(); + + let s_nor = (n0 * b0) + (n1 * b1) + (n2 * b2); + if dot(s_nor, geo_normal) >= 0.0 { + s_nor + } else { + -s_nor + } } else { - // Calculate intersection point and error magnitudes - let pos = ((tri.0.into_vector() * b0) - + (tri.1.into_vector() * b1) - + (tri.2.into_vector() * b2)).into_point(); + geo_normal + }; - let pos_err = (((tri.0.into_vector().abs() * b0) - + (tri.1.into_vector().abs() * b1) - + (tri.2.into_vector().abs() * b2)) - * fp_gamma(7)).co.h_max(); - - // Fill in intersection data - isects[r.id as usize] = SurfaceIntersection::Hit { - intersection_data: SurfaceIntersectionData { - incoming: wr.dir, - t: t, - pos: pos, - pos_err: pos_err, - nor: cross(tri.0 - tri.1, tri.0 - tri.2) - .into_normal(), // TODO - nor_g: cross(tri.0 - tri.1, tri.0 - tri.2) - .into_normal(), - uv: (0.0, 0.0), // TODO - local_space: mat_space, - }, - // TODO: get surface closure from surface shader. - closure: SurfaceClosureUnion::LambertClosure( - LambertClosure::new(XYZ::new(0.8, 0.8, 0.8)) - ), + // Fill in intersection data + isects[r.id as usize] = SurfaceIntersection::Hit { + intersection_data: SurfaceIntersectionData { + incoming: wr.dir, + t: t, + pos: pos, + pos_err: pos_err, + nor: shading_normal, + nor_g: geo_normal, + uv: (0.0, 0.0), // TODO + local_space: mat_space, + }, + // TODO: get surface closure from surface shader. + closure: SurfaceClosureUnion::LambertClosure( + LambertClosure::new(XYZ::new(0.8, 0.8, 0.8)), + ), // closure: // SurfaceClosureUnion::GTRClosure( // GTRClosure::new(XYZ::new(0.8, 0.8, 0.8), // 0.1, // 2.0, // 1.0)), - }; - r.max_t = t; - } + }; + r.max_t = t; } } } } - ); + }, + ); } }