Skip to content
This repository has been archived by the owner on Jun 6, 2018. It is now read-only.

Commit

Permalink
feature(resource::collada): more robust collada mesh processing
Browse files Browse the repository at this point in the history
Rework COLLADA processing to use the new mesh builder system. Also remove
(most) hard-coded processing and actually process the COLLADA data
according to the spec.
  • Loading branch information
randomPoison committed Dec 5, 2015
1 parent 907bf23 commit bb075ef
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 55 deletions.
4 changes: 2 additions & 2 deletions lib/polygon_rs/src/geometry/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,13 @@ impl MeshBuilder {

position: VertexAttribute {
offset: 0,
stride: 0, // TODO: Should stride 4 (as in every 4 floats) or 0 (as in tightly packed)? Does that change between OpenGL and DirectX?
stride: 4,
},

normal: if self.normal_data.len() > 0 {
Some(VertexAttribute {
offset: self.position_data.len() * 4,
stride: 0, // TODO: Same as above.
stride: 3,
})
} else {
None
Expand Down
13 changes: 9 additions & 4 deletions src/debug_draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::sync::Mutex;
use math::*;
use polygon::Camera;
use polygon::gl_render::{GLRender, ShaderProgram, GLMeshData};
use polygon::geometry::Mesh;
use polygon::geometry::*;
use resource::ResourceManager;

static mut instance: *const Mutex<DebugDrawInner> = 0 as *const _;
Expand Down Expand Up @@ -35,7 +35,7 @@ static CUBE_VERTS: [f32; 32] =
-0.5, 0.5, -0.5, 1.0,
-0.5, -0.5, 0.5, 1.0,
-0.5, -0.5, -0.5, 1.0,];
static CUBE_INDICES: [u32; 24] =
static CUBE_INDICES: [MeshIndex; 24] =
[0, 1,
1, 3,
3, 2,
Expand Down Expand Up @@ -178,8 +178,13 @@ impl Drop for DebugDraw {
}

/// Creates a mesh from a list of vertices and indices.
fn build_mesh(renderer: &GLRender, vertices: &[f32], indices: &[u32]) -> GLMeshData {
let mesh = Mesh::from_raw_data(vertices, indices);
fn build_mesh(renderer: &GLRender, vertices: &[f32], indices: &[MeshIndex]) -> GLMeshData {
let mesh = MeshBuilder::new()
.set_position_data(Point::slice_from_f32_slice(vertices))
.set_indices(indices)
.build()
.unwrap(); // TODO: Don't panic? I think in this case panicking is a bug in the engine and is pretty legit.

renderer.gen_mesh(&mesh)
}

Expand Down
222 changes: 173 additions & 49 deletions src/resource/collada.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@ extern crate parse_collada as collada;

pub use self::collada::{ArrayElement, Collada, GeometricElement, Geometry, Node, PrimitiveElements, VisualScene};

use polygon::geometry::mesh::Mesh;
use math::*;
use polygon::geometry::*;
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub enum Error {
/// Indicates an error that occurred when the MeshBuilder was validating the mesh data. If the
/// COLLADA document passed parsing this should not occur.
BuildMeshError(BuildMeshError),

IncorrectPrimitiveIndicesCount {
primitive_count: usize,
stride: usize,
index_count: usize,
},

/// Indicates that there was an input with the "NORMAL" semantic but the associated source
/// was missing.
MissingNormalSource,
Expand All @@ -28,13 +39,27 @@ pub enum Error {
/// fixed.
MissingPositionSemantic,

/// Indicates that the <mesh> had no primitive elements.
MissingPrimitiveElement,

/// Indicates that one of the primitive elements (e.g. <trianges> et al) were missing a <p>
/// child element. While this is technically allowed by the standard I'm not really sure what
/// to do with that? Like how do you define a mesh without indices?
MissingPrimitiveIndices,

UnsupportedGeometricElement,
UnsupportedPrimitiveType,
UnsupportedSourceElement,
}

pub type Result<T> = ::std::result::Result<T, Error>;

pub enum VertexSemantic {
Position,
Normal,
TexCoord,
}

/// Load the mesh data from a COLLADA .dae file.
///
/// The data in a COLLADA files is formatted for efficiency, but isn't necessarily
Expand All @@ -51,65 +76,123 @@ pub fn geometry_to_mesh(geometry: &Geometry) -> Result<Mesh> {
}

fn collada_mesh_to_mesh(mesh: &collada::Mesh) -> Result<Mesh> {
let position_data_raw = try!(get_raw_positions(&mesh));

let triangles = match mesh.primitive_elements[0] {
// First, pick a primitive element to parse into a Mesh.
// TODO: Handle all primitive elements in the mesh, not just one. This is dependent on polygon
// being able to support submeshes.
let primitive = try!(
mesh.primitive_elements.first()
.ok_or(Error::MissingPrimitiveElement));

// TODO: A mesh may have no primitive elements and still be well-formed by the COLLADA spec.
// We need to gracefully handle that case by either returning and error about unsupported
// format or return an empty mesh (which is what a mesh with no primitives is).
let triangles = match *primitive {
PrimitiveElements::Triangles(ref triangles) => triangles,
_ => return Err(Error::UnsupportedPrimitiveType),
};

let normal_data_raw = try!(get_normals_for_triangles(mesh, triangles)).unwrap(); // TODO: Handle the case where there's no normal data.
let primitive_indices = &triangles.p.as_ref().unwrap();
let primitive_indices = try!(
triangles.p
.as_ref()
.ok_or(Error::MissingPrimitiveIndices));

// Create a new array for the positions so we can add the w coordinate.
let mut position_data: Vec<f32> = Vec::with_capacity(position_data_raw.len() / 3 * 4);
// Iterate over the indices, rearranging the normal data to match the position data.
let stride = triangles.input.len(); // TODO: Do we have a better way of calculating stride? What if one of the sources isn't used? OR USED TWICE!?
let count = triangles.count;
let index_count = primitive_indices.len();
let vertex_count = count as u32 * 3;

// Verify we have the right number of indices to build the vertices.
if count * stride * 3 != index_count {
return Err(Error::IncorrectPrimitiveIndicesCount {
primitive_count: count,
stride: stride,
index_count: index_count,
});
}

// Create a new array for the normals and rearrange them to match the order of position attributes.
let mut normal_data: Vec<f32> = Vec::with_capacity(position_data.len());
// Retrieve the f32 arrays for position and normal data.
let position_data_raw = try!(get_raw_positions(&mesh));
let normal_data_raw = try!(get_normals_for_element(mesh, primitive));

// The indices list is a just a raw list of indices. They are implicity grouped based on the
// number of inputs for the primitive element (e.g. if there are 3 inputs for the primitive
// then there are 3 indices per vertex). To handle this we use GroupBy to do a strided
// iteration over indices list and build each vertex one at a time. Internally the mesh
// builder handles the details of how to assemble the vertex data in memory.

// Build a mapping between the vertex indices and the source that they use.
let mut source_map = Vec::new();
for (offset, input) in triangles.input.iter().enumerate() {
// Retrieve the approriate source. If the semantic is "VERTEX" then the offset is
// associated with all of the sources specified by the <vertex> element.
let source_ids = match &*input.semantic {
"VERTEX" => {
mesh.vertices.input
.iter()
.map(|input| &*input.semantic)
.collect()
},
_ => vec![(&*input.semantic)],
};

// Iterate over the indices, rearranging the normal data to match the position data.
let stride = mesh.source.len(); // TODO: Do we have a better way of calculating stride? What if one of the sources isn't used? OR USED TWICE!?
let mut vertex_index_map: HashMap<(usize, usize), u32> = HashMap::new();
let mut indices: Vec<u32> = Vec::new();
let vertex_count = triangles.count * 3;
let mut index_count = 0;
for offset in 0..vertex_count {
// Determine the offset of the the current vertex's attributes
let position_index = primitive_indices[offset * stride];
let normal_index = primitive_indices[offset * stride + 1];

// Push the index of the vertex, either reusing an existing vertex or creating a new one.
let vertex_key = (position_index, normal_index);
let vertex_index = if vertex_index_map.contains_key(&vertex_key) {
// Vertex has already been assembled, reuse the index.
(*vertex_index_map.get(&vertex_key).unwrap()) as u32
} else {
// Assemble new vertex.
let vertex_index = index_count;
index_count += 1;
vertex_index_map.insert(vertex_key, vertex_index as u32);

// Append position to position data array.
for offset in 0..3 {
position_data.push(position_data_raw[position_index * 3 + offset]);
}
position_data.push(1.0);
// For each of the semantics at the current offset, push their info into the source map.
for semantic in source_ids {
source_map.push((offset, semantic));
}
}

// Append normal to normal data array.
for offset in 0..3 {
normal_data.push(normal_data_raw[normal_index * 3 + offset]);
let mut mesh_builder = MeshBuilder::new();
let mut unsupported_semantic_flag = false;
for vertex_indices in GroupBy::new(primitive_indices, stride).unwrap() { // TODO: This can't fail... right? I'm pretty sure the above checks make sure this is correct.
// We iterate over each group of indices where each group represents the indices for a
// single vertex. Within that vertex we need
let mut vertex = Vertex::new(Point::origin());

for (offset, index) in vertex_indices.iter().enumerate() {
let mut filter =
source_map.iter()
.filter(|map| map.0 == offset)
.map(|&(_, semantic)| semantic);
for semantic in filter {
match &*semantic {
"POSITION" => {
vertex.position = Point::new(
// TODO: Don't assume that the position data is encoded as 3 coordinate
// vectors. The <technique_common> element for the source should have
// an <accessor> describing how the data is laid out.
position_data_raw[index * 3 + 0],
position_data_raw[index * 3 + 1],
position_data_raw[index * 3 + 2],
);
},
"NORMAL" => {
let normal_data = try!(
normal_data_raw
.ok_or(Error::MissingNormalData));
vertex.normal = Some(Vector3::new(
normal_data[index * 3 + 0],
normal_data[index * 3 + 1],
normal_data[index * 3 + 2],
));
}
_ => if !unsupported_semantic_flag {
unsupported_semantic_flag = true;
println!("WARNING: Unsupported vertex semantic {} in mesh will not be used", semantic);
},
}
}
}

vertex_index
};

indices.push(vertex_index);
mesh_builder = mesh_builder.add_vertex(vertex);
}

let mut mesh = Mesh::from_raw_data(position_data.as_ref(), indices.as_ref());
mesh.add_normals(normal_data.as_ref());
let indices: Vec<u32> = (0..vertex_count).collect();

Ok(mesh)
mesh_builder
.set_indices(&*indices)
.build()
.map_err(|err| Error::BuildMeshError(err))
}

/// Parses the <mesh> element to find the <source> element associated with the "POSITION" input semantic.
Expand Down Expand Up @@ -152,7 +235,8 @@ fn get_raw_positions(mesh: &collada::Mesh) -> Result<&[f32]> {
Ok(position_data)
}

fn get_normals_for_triangles<'a>(mesh: &'a collada::Mesh, triangles: &'a collada::Triangles) -> Result<Option<&'a [f32]>> {
// TODO: This shouldn't be specific to triangles, it should work for all PrimitiveElements variants
fn get_normals_for_element<'a>(mesh: &'a collada::Mesh, element: &'a PrimitiveElements) -> Result<Option<&'a [f32]>> {
// First we have to find the input with the correct semantic.
// Check the mesh's <vertices> element first.
let source_id = {
Expand All @@ -163,7 +247,7 @@ fn get_normals_for_triangles<'a>(mesh: &'a collada::Mesh, triangles: &'a collada
.find(|input| input.semantic == "NORMAL")
.map(|input| &input.source)
.or_else(||
triangles.input
element.input()
.iter()
.find(|input| input.semantic == "NORMAL")
.map(|input| &input.source)
Expand Down Expand Up @@ -201,3 +285,43 @@ fn get_normals_for_triangles<'a>(mesh: &'a collada::Mesh, triangles: &'a collada

Ok(Some(normal_data))
}

// TODO: Where even should this live? It's generally useful but I'm only using it here right now.
struct GroupBy<'a, T: 'a> {
next: *const T,
end: *const T,
stride: usize,
_phantom: ::std::marker::PhantomData<&'a T>,
}

impl<'a, T: 'a> GroupBy<'a, T> {
fn new(slice: &'a [T], stride: usize) -> ::std::result::Result<GroupBy<'a, T>, ()> {
if slice.len() % stride != 0 {
return Err(());
}

Ok(GroupBy {
next: slice.as_ptr(),
end: unsafe { slice.as_ptr().offset(slice.len() as isize) },
stride: stride,
_phantom: ::std::marker::PhantomData,
})
}
}

impl<'a, T: 'a> Iterator for GroupBy<'a, T> {
type Item = &'a [T];

fn next(&mut self) -> Option<&'a [T]> {
if self.next == self.end {
return None;
}

let next = self.next;
self.next = unsafe { self.next.offset(self.stride as isize) };

Some(unsafe {
::std::slice::from_raw_parts(next, self.stride)
})
}
}

0 comments on commit bb075ef

Please sign in to comment.