pub use luminance::tess::{Mode, Tess, TessVertices};
use std::collections::BTreeMap;
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use wavefront_obj::ParseError;
use wavefront_obj::obj;
use sys::res::{FSKey, Load, Loaded, Storage};
use sys::res::helpers::{TyDesc, load_with};
use scene::aabb::AABB;
use sys::ignite::Ignite;
pub enum ModelTree<V> {
Leaf(AABB, Tess<V>),
Node(AABB, Vec<ModelTree<V>>)
}
pub type ObjModel = ModelTree<ObjVertex>;
pub type ObjVertex = (ObjVertexPos, ObjVertexNor, ObjVertexTexCoord);
pub type ObjVertexPos = [f32; 3];
pub type ObjVertexNor = [f32; 3];
pub type ObjVertexTexCoord = [f32; 2];
impl TyDesc for ObjModel {
const TY_DESC: &'static str = "model";
}
impl Load<Ignite> for ObjModel {
type Key = FSKey;
type Error = ModelError;
fn load(key: Self::Key, _: &mut Storage<Ignite>, ctx: &mut Ignite) -> Result<Loaded<Self>, Self::Error> {
let path = key.as_path();
load_with::<Self, _, _, _>(path, move || {
let mut input = String::new();
{
let mut file = File::open(path).map_err(|_| ModelError::FileNotFound(path.to_owned()))?;
let _ = file.read_to_string(&mut input);
}
let obj_set = obj::parse(input).map_err(ModelError::ParseFailed)?;
convert_obj(ctx, obj_set).map(Into::into)
})
}
impl_reload_passthrough!(Ignite);
}
fn convert_obj(ignite: &mut Ignite, obj_set: obj::ObjSet) -> Result<ObjModel, ModelError> {
let mut parts = Vec::new();
info!("{} objects to convert…", obj_set.objects.len());
for obj in &obj_set.objects {
info!(" converting {} geometries in object {}", obj.geometry.len(), obj.name);
for geometry in &obj.geometry {
info!(" {} vertices, {} normals, {} tex vertices", obj.vertices.len(), obj.normals.len(), obj.tex_vertices.len());
let (vertices, indices, mode, aabb) = convert_geometry(geometry, &obj.vertices, &obj.normals, &obj.tex_vertices)?;
let part = (aabb, Tess::new(ignite.surface(), mode, TessVertices::Fill(&vertices), &indices[..]));
parts.push(part);
}
}
let model_aabb = AABB::from_aabbs(parts.iter().map(|&(aabb, _)| aabb));
let nodes = parts.into_iter().map(|(aabb, tess)| ModelTree::Leaf(aabb, tess)).collect();
model_aabb.map(|aabb| ModelTree::Node(aabb, nodes)).ok_or(ModelError::NoGeometry)
}
fn convert_geometry(geo: &obj::Geometry, positions: &[obj::Vertex], normals: &[obj::Normal], tvertices: &[obj::TVertex]) -> Result<(Vec<ObjVertex>, Vec<u32>, Mode, AABB), ModelError> {
if geo.shapes.is_empty() {
return Err(ModelError::NoShape);
}
let mut vertices = Vec::new(); let mut indices = Vec::new();
let mut index_map = BTreeMap::new();
info!(" converting geometry");
let mode = guess_mode(geo.shapes[0].primitive);
for prim in geo.shapes.iter().map(|s| s.primitive) {
let keys = create_keys_from_primitive(prim)?;
for key in keys {
match index_map.get(&key).cloned() {
Some(index) => {
indices.push(index);
},
None => {
let vertex = interleave_vertex(&positions[key.0], &normals[key.1], key.2.map(|ki| &tvertices[ki]));
let index = vertices.len() as u32;
vertices.push(vertex);
indices.push(index);
index_map.insert(key, index);
}
}
}
}
AABB::from_vertices(vertices.iter().map(|v| v.0.into()))
.map(|aabb| (vertices, indices, mode, aabb))
.ok_or(ModelError::NoVertex)
}
fn create_keys_from_primitive(prim: obj::Primitive) -> Result<Vec<(usize, usize, Option<usize>)>, ModelError> {
match prim {
obj::Primitive::Point(i) => {
let a = vtnindex_to_key(i)?;
Ok(vec![a])
},
obj::Primitive::Line(i, j) => {
let a = vtnindex_to_key(i)?;
let b = vtnindex_to_key(j)?;
Ok(vec![a, b])
},
obj::Primitive::Triangle(i, j, k) => {
let a = vtnindex_to_key(i)?;
let b = vtnindex_to_key(j)?;
let c = vtnindex_to_key(k)?;
Ok(vec![a, b, c])
}
}
}
fn vtnindex_to_key(i: obj::VTNIndex) -> Result<(usize, usize, Option<usize>), ModelError> {
match i {
(pi, ti, Some(ni)) => Ok((pi, ni, ti)),
_ => Err(ModelError::UnsupportedVertex)
}
}
fn interleave_vertex(p: &obj::Vertex, n: &obj::Normal, t: Option<&obj::TVertex>) -> ObjVertex {
(convert_vertex(p), convert_nor(n), t.map_or([0., 0.], convert_tvertex))
}
fn convert_vertex(v: &obj::Vertex) -> ObjVertexPos {
[v.x as f32, v.y as f32, v.z as f32]
}
fn convert_nor(n: &obj::Normal) -> ObjVertexNor {
convert_vertex(n)
}
fn convert_tvertex(t: &obj::TVertex) -> ObjVertexTexCoord {
[t.u as f32, t.v as f32]
}
fn guess_mode(prim: obj::Primitive) -> Mode {
match prim {
obj::Primitive::Point(_) => Mode::Point,
obj::Primitive::Line(_, _) => Mode::Line,
obj::Primitive::Triangle(_, _, _) => Mode::Triangle
}
}
#[derive(Debug, PartialEq)]
pub enum ModelError {
FileNotFound(PathBuf),
ParseFailed(ParseError),
UnsupportedVertex,
NoVertex,
NoGeometry,
NoShape
}
impl fmt::Display for ModelError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(self.description())
}
}
impl Error for ModelError {
fn description(&self) -> &str {
match *self {
ModelError::FileNotFound(_) => "file not found",
ModelError::ParseFailed(_) => "parse failed",
ModelError::UnsupportedVertex => "unsupported vertex",
ModelError::NoVertex => "no vertex found",
ModelError::NoGeometry => "no geometry found",
ModelError::NoShape => "no shape found"
}
}
}
pub enum MaterialTree<M> {
Leaf(M),
Node(Vec<MaterialTree<M>>)
}
impl<M> MaterialTree<M> {
pub fn represent<V, F>(&self, model_tree: &ModelTree<V>, f: &mut F) where F: FnMut(&M, &Tess<V>) {
match (self, model_tree) {
(&MaterialTree::Leaf(ref material), &ModelTree::Leaf(_, ref tess)) => f(material, tess),
(&MaterialTree::Node(ref material_nodes), &ModelTree::Node(_, ref model_nodes)) => {
for (material, model) in material_nodes.iter().zip(model_nodes) {
material.represent(model, f);
}
}
_ => ()
}
}
}