psychopath/src/parse/psy_mesh_surface.rs
Nathan Vegdahl 6b188c4451 Parse errors now use Strings for error messages.
This allows the error messages to be more informative by including
dynamically determined information.

Also made a utility function for ensuring that nodes get closed.
2020-05-01 16:08:37 +09:00

178 lines
5.2 KiB
Rust

#![allow(dead_code)]
use std::{io::BufRead, result::Result};
use nom::{sequence::tuple, IResult};
use kioku::Arena;
use data_tree::{reader::DataTreeReader, Event};
use crate::{
math::{Normal, Point},
surface::triangle_mesh::TriangleMesh,
};
use super::{
parse_utils::{ws_f32, ws_usize},
psy::{PsyError, PsyResult},
};
// pub struct TriangleMesh {
// time_samples: usize,
// geo: Vec<(Point, Point, Point)>,
// indices: Vec<usize>,
// accel: BVH,
// }
pub fn parse_mesh_surface<'a>(
arena: &'a Arena,
events: &mut DataTreeReader<impl BufRead>,
) -> PsyResult<TriangleMesh<'a>> {
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();
loop {
match events.next_event()? {
Event::Leaf {
type_name: "SurfaceShaderBind",
..
} => {
// TODO
}
Event::Leaf {
type_name: "Vertices",
contents,
..
} => {
// Collect verts for this time sample
let mut text = contents;
let mut tverts = Vec::new();
while let IResult::Ok((remaining, vert)) = tuple((ws_f32, ws_f32, ws_f32))(text) {
text = remaining;
tverts.push(Point::new(vert.0, vert.1, vert.2));
}
verts.push(tverts);
}
Event::Leaf {
type_name: "Normals",
contents,
..
} => {
// Collect normals for this time sample
let mut text = contents;
let mut tnormals = Vec::new();
while let IResult::Ok((remaining, nor)) = tuple((ws_f32, ws_f32, ws_f32))(text) {
text = remaining;
tnormals.push(Normal::new(nor.0, nor.1, nor.2).normalized());
}
normals.push(tnormals);
}
Event::Leaf {
type_name: "FaceVertCounts",
contents,
byte_offset,
} => {
if !face_vert_counts.is_empty() {
return Err(PsyError::WrongNodeCount(
byte_offset,
"Meshes can only have one FaceVertCounts section.".into(),
));
}
let mut text = contents;
while let IResult::Ok((remaining, count)) = ws_usize(text) {
text = remaining;
face_vert_counts.push(count);
}
}
Event::Leaf {
type_name: "FaceVertIndices",
contents,
byte_offset,
} => {
if !face_vert_indices.is_empty() {
return Err(PsyError::WrongNodeCount(
byte_offset,
"Meshes can only have one FaceVertIndices section.".into(),
));
}
let mut text = contents;
while let IResult::Ok((remaining, index)) = ws_usize(text) {
text = remaining;
face_vert_indices.push(index);
}
}
Event::InnerClose { .. } => {
break;
}
_ => {
todo!(); // Return error.
}
}
}
// Validation: make sure all time samples have same vert count.
let vert_count = verts[0].len();
for vs in &verts {
assert_eq!(vert_count, vs.len());
}
// Validation: 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());
}
}
// Validation: make sure we have any mesh data.
if verts.is_empty() || face_vert_counts.is_empty() || face_vert_indices.is_empty() {
todo!("Meshes must have at least one non-empty of each of the following sections: Vertices, FaceVertCounts, FaceVertIndices.");
// Return an error.
}
// Build triangle mesh
let mut tri_vert_indices = Vec::new();
let mut ii = 0;
for fvc in &face_vert_counts {
if *fvc >= 3 {
// Store the polygon, split up into triangles if >3 verts
let v1 = ii;
for vi in 0..(fvc - 2) {
tri_vert_indices.push((
face_vert_indices[v1],
face_vert_indices[v1 + vi + 1],
face_vert_indices[v1 + vi + 2],
));
}
} else {
// TODO: proper error
panic!("Cannot handle polygons with less than three vertices.");
}
ii += *fvc;
}
Ok(TriangleMesh::from_verts_and_indices(
arena,
&verts,
&if normals.is_empty() {
None
} else {
Some(normals)
},
&tri_vert_indices,
))
}