use std::io::BufRead;
use std::collections::{HashMap, VecMap};
use std::simd::f32x4;
use error::ObjResult;
use raw::lexer::lex;
macro_rules! n {
($input:expr) => ( try!($input.parse::<usize>()) )
}
macro_rules! f {
($args:expr) => (
&{
let mut ret = Vec::new();
for &arg in $args.iter() {
ret.push(try!(arg.parse::<f32>()))
}
ret
}[..]
)
}
macro_rules! s {
($param:ident) => ( &$param.split('/').collect::<Vec<&str>>()[..] )
}
pub fn parse_obj<T: BufRead>(input: T) -> ObjResult<RawObj> {
let mut name = None;
let mut material_libraries = Vec::new();
let mut positions = Vec::new();
let mut tex_coords = Vec::new();
let mut normals = Vec::new();
let mut param_vertices = Vec::new();
let points = Vec::new();
let lines = Vec::new();
let mut polygons = Vec::new();
let counter = Counter::new(&points, &lines, &polygons);
let mut group_builder = counter.hash_map("default".to_string());
let mut mesh_builder = counter.hash_map(String::new());
let mut smoothing_builder = counter.vec_map();
let mut merging_builder = counter.vec_map();
try!(lex(input, |stmt, args| {
match stmt {
"v" => positions.push(match f!(args) {
[x, y, z, w] => f32x4(x, y, z, w),
[x, y, z] => f32x4(x, y, z, 1.0),
_ => error!(WrongNumberOfArguments, "Expected 3 or 4 arguments")
}),
"vt" => tex_coords.push(match f!(args) {
[u, v, w] => f32x4(u, v, w, 0.0),
[u, v] => f32x4(u, v, 0.0, 0.0),
[u] => f32x4(u, 0.0, 0.0, 0.0),
_ => error!(WrongNumberOfArguments, "Expected 1, 2 or 3 arguments")
}),
"vn" => normals.push(match f!(args) {
[x, y, z] => f32x4(x, y, z, 0.0),
_ => error!(WrongNumberOfArguments, "Expected 3 arguments")
}),
"vp" => param_vertices.push(match f!(args) {
[u, v, w] => f32x4(u, v, w, 0.0),
[u, v] => f32x4(u, v, 1.0, 0.0),
[u] => f32x4(u, 0.0, 1.0, 0.0),
_ => error!(WrongNumberOfArguments, "Expected 1, 2 or 3 arguments")
}),
"cstype" => {
let _rational: bool;
let geometry = match args {
["rat", ty] => { _rational = true; ty }
[ty] => { _rational = false; ty }
_ => error!(WrongTypeOfArguments, "Expected 'rat xxx' or 'xxx' format")
};
match geometry {
"bmatrix" => unimplemented!(),
"bezier" => unimplemented!(),
"bspline" => unimplemented!(),
"cardinal" => unimplemented!(),
"taylor" => unimplemented!(),
_ => error!(WrongTypeOfArguments, "Expected one of 'bmatrix', 'bezier', 'bspline', 'cardinal' and 'taylor'")
}
}
"deg" => match f!(args) {
[_deg_u, _deg_v] => unimplemented!(),
[_deg_u] => unimplemented!(),
_ => error!(WrongNumberOfArguments, "Expected 1 or 2 arguments")
},
"bmat" => unimplemented!(),
"step" => unimplemented!(),
"p" => unimplemented!(),
"l" => unimplemented!(),
"f" => {
if args.len() < 3 { error!(WrongNumberOfArguments, "Expected at least 3 arguments") }
let mut args = args.iter();
let first = args.next().unwrap();
macro_rules! m {
{ $($pat:pat => $name:ident[$exp:expr]),* } => (
match s!(first) {
$($pat => Polygon::$name({
let mut polygon = vec![ $exp ];
for param in args {
match s!(param) {
$pat => polygon.push($exp),
_ => unimplemented!()
}
}
polygon
}),)*
_ => error!(WrongTypeOfArguments, "Expected '#', '#/#', '#//#' or '#/#/#' format")
}
)
}
polygons.push(m! {
[p] => P[n!(p) - 1],
[p, t] => PT[(n!(p) - 1, n!(t) - 1)],
[p, "", u] => PN[(n!(p) - 1, n!(u) - 1)],
[p, t, u] => PTN[(n!(p) - 1, n!(t) - 1, n!(u) - 1)]
});
}
"curv" => unimplemented!(),
"curv2" => unimplemented!(),
"surf" => unimplemented!(),
"parm" => unimplemented!(),
"trim" => unimplemented!(),
"hole" => unimplemented!(),
"scrv" => unimplemented!(),
"sp" => unimplemented!(),
"end" => unimplemented!(),
"con" => unimplemented!(),
"g" => match args {
[name] => group_builder.start(name.to_string()),
_ => error!(WrongNumberOfArguments, "Expected group name parameter, but nothing has been supplied")
},
"s" => match args {
["off"] | ["0"] => smoothing_builder.end(),
[param] => smoothing_builder.start(n!(param)),
_ => error!(WrongNumberOfArguments, "Expected only 1 argument")
},
"mg" => match args {
["off"] | ["0"] => merging_builder.end(),
[param] => merging_builder.start(n!(param)),
_ => error!(WrongNumberOfArguments, "Expected only 1 argument")
},
"o" => name = match args {
[] => None,
args => Some(args.connect(" "))
},
"bevel" => unimplemented!(),
"c_interp" => unimplemented!(),
"d_interp" => unimplemented!(),
"lod" => unimplemented!(),
"usemtl" => match args {
[material] => mesh_builder.start(material.to_string()),
_ => error!(WrongNumberOfArguments, "Expected only 1 argument")
},
"mtllib" => {
let paths: Vec<String> = args.iter().map(|path| path.to_string()).collect();
material_libraries.push_all(&paths[..]);
}
"shadow_obj" => unimplemented!(),
"trace_obj" => unimplemented!(),
"ctech" => unimplemented!(),
"stech" => unimplemented!(),
_ => error!(UnexpectedStatement, "Received unknown statement")
}
Ok(())
}));
group_builder.end();
mesh_builder.end();
smoothing_builder.end();
merging_builder.end();
Ok(RawObj {
name: name,
material_libraries: material_libraries,
positions: positions,
tex_coords: tex_coords,
normals: normals,
param_vertices: param_vertices,
points: points,
lines: lines,
polygons: polygons,
groups: group_builder.result,
meshes: mesh_builder.result,
smoothing_groups: smoothing_builder.result,
merging_groups: merging_builder.result
})
}
struct Counter {
points: *const Vec<Point>,
lines: *const Vec<Line>,
polygons: *const Vec<Polygon>,
}
impl Counter {
fn new(points: *const Vec<Point>, lines: *const Vec<Line>, polygons: *const Vec<Polygon>) -> Self {
Counter {
points: points,
lines: lines,
polygons: polygons
}
}
fn get(&self) -> (usize, usize, usize) {
unsafe { ((*self.points).len(), (*self.lines).len(), (*self.polygons).len()) }
}
fn hash_map<'a>(&'a self, input: String) -> GroupBuilder<'a, HashMap<String, Group>, String> {
let mut result = HashMap::with_capacity(1);
result.insert(input.clone(), Group::new((0, 0, 0)));
GroupBuilder {
counter: self,
current: Some(input),
result: result
}
}
fn vec_map<'a>(&'a self) -> GroupBuilder<'a, VecMap<Group>, usize> {
GroupBuilder {
counter: self,
current: None,
result: VecMap::new()
}
}
}
struct GroupBuilder<'a, T, K> {
counter: &'a Counter,
current: Option<K>, result: T
}
impl<'a, T, K> GroupBuilder<'a, T, K> where
T: Map<K, Group>,
K: Clone + Key
{
fn start(&mut self, input: K) {
let count = self.counter.get();
if let Some(ref current) = self.current {
if *current == input { return }
if self.result.get_mut(current).unwrap().end(count) {
let res = self.result.remove(¤t);
assert!(res.is_some());
}
}
(|| {
if let Some(ref mut group) = self.result.get_mut(&input) { group.start(count); return }
let res = self.result.insert(input.clone(), Group::new(count));
assert!(res.is_none());
})();
self.current = Some(input);
}
fn end(&mut self) {
if let Some(ref current) = self.current {
if self.result.get_mut(current).unwrap().end(self.counter.get()) {
let result = self.result.remove(current);
assert!(result.is_some());
}
} else { return }
self.current = None;
}
}
const UNDEFINED: usize = ::std::usize::MAX;
impl Group {
fn new(count: (usize, usize, usize)) -> Self {
let mut ret = Group {
points: Vec::with_capacity(1),
lines: Vec::with_capacity(1),
polygons: Vec::with_capacity(1)
};
ret.start(count);
ret
}
fn start(&mut self, count: (usize, usize, usize)) {
self.points.push(Range { start: count.0, end: UNDEFINED });
self.lines.push(Range { start: count.1, end: UNDEFINED });
self.polygons.push(Range { start: count.2, end: UNDEFINED })
}
fn end(&mut self, count: (usize, usize, usize)) -> bool {
end(&mut self.points, count.0);
end(&mut self.lines, count.1);
end(&mut self.polygons, count.2);
fn end(vec: &mut Vec<Range>, end: usize) {
let last = vec.len() - 1;
assert_eq!(vec[last].end, UNDEFINED);
if vec[last].start != end {
vec[last].end = end;
} else {
vec.pop();
}
}
self.points.is_empty() && self.lines.is_empty() && self.polygons.is_empty()
}
}
trait Map<K: Key, V: ?Sized> {
fn insert(&mut self, K, V) -> Option<V>;
fn get_mut(&mut self, k: &K) -> Option<&mut V>;
fn remove(&mut self, k: &K) -> Option<V>;
}
impl<V> Map<String, V> for HashMap<String, V> {
fn insert(&mut self, k: String, v: V) -> Option<V> { self.insert(k, v) }
fn get_mut(&mut self, k: &String) -> Option<&mut V> { self.get_mut(k) }
fn remove(&mut self, k: &String) -> Option<V> { self.remove(k) }
}
impl<V> Map<usize, V> for VecMap<V> {
fn insert(&mut self, k: usize, v: V) -> Option<V> { self.insert(k, v) }
fn get_mut(&mut self, k: &usize) -> Option<&mut V> { self.get_mut(k) }
fn remove(&mut self, k: &usize) -> Option<V> { self.remove(k) }
}
trait Key : Eq {}
impl Key for String {}
impl Key for usize {}
pub struct RawObj {
pub name: Option<String>,
pub material_libraries: Vec<String>,
pub positions: Vec<f32x4>,
pub tex_coords: Vec<f32x4>,
pub normals: Vec<f32x4>,
pub param_vertices: Vec<f32x4>,
pub points: Vec<Point>,
pub lines: Vec<Line>,
pub polygons: Vec<Polygon>,
pub groups: HashMap<String, Group>,
pub meshes: HashMap<String, Group>,
pub smoothing_groups: VecMap<Group>,
pub merging_groups: VecMap<Group>
}
pub type Point = usize;
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum Line {
P([usize; 2]),
PT([(usize, usize); 2])
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Polygon {
P(Vec<usize>),
PT(Vec<(usize, usize)>),
PN(Vec<(usize, usize)>),
PTN(Vec<(usize, usize, usize)>)
}
#[derive(Clone, Debug)]
pub struct Group {
pub points: Vec<Range>,
pub lines: Vec<Range>,
pub polygons: Vec<Range>
}
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub struct Range {
pub start: usize,
pub end: usize
}