use std::collections::HashMap;
use std::io::BufRead;
use vec_map::VecMap;
use crate::error::ObjResult;
use crate::raw::lexer::lex;
use crate::raw::util::parse_args;
macro_rules! parse_args {
{
$first:expr, $rest:expr,
$($pat:pat => $type:ident::$name:ident[$exp:expr]),*,
! => $error:expr
} => (
match split_vertex_group($first)[..] {
$($pat => $type::$name({
let mut points = vec![$exp];
for param in $rest {
match split_vertex_group(param)[..] {
$pat => points.push($exp),
_ => $error
}
}
points
}),)*
_ => $error
}
)
}
fn try_index<T>(collection: &[T], input: &str) -> ObjResult<usize> {
use crate::error::{LoadError, LoadErrorKind, ObjError};
use std::convert::TryInto;
let len: isize = collection.len().try_into().map_err(|_| {
ObjError::Load(LoadError::new(
LoadErrorKind::IndexOutOfRange,
"Too many items in collection",
))
})?;
let index: isize = input.parse()?;
let ret = if index < -len {
make_error!(IndexOutOfRange, "Too small index value");
} else if index < 0 {
len + index
} else if index == 0 {
make_error!(IndexOutOfRange, "Index value shouldn't be zero");
} else if index <= len {
index - 1
} else {
make_error!(IndexOutOfRange, "Too big index value");
};
Ok(ret as usize)
}
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 mut points = Vec::new();
let mut 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();
lex(input, |stmt, args: &[&str]| {
match stmt {
"v" => positions.push(match parse_args(args)?[..] {
[x, y, z, w] => (x, y, z, w),
[x, y, z] => (x, y, z, 1.0),
_ => make_error!(WrongNumberOfArguments, "Expected 3 or 4 arguments"),
}),
"vt" => tex_coords.push(match parse_args(args)?[..] {
[u, v, w] => (u, v, w),
[u, v] => (u, v, 0.0),
[u] => (u, 0.0, 0.0),
_ => make_error!(WrongNumberOfArguments, "Expected 1, 2 or 3 arguments"),
}),
"vn" => normals.push(match parse_args(args)?[..] {
[x, y, z] => (x, y, z),
_ => make_error!(WrongNumberOfArguments, "Expected 3 arguments"),
}),
"vp" => param_vertices.push(match parse_args(args)?[..] {
[u, v, w] => (u, v, w),
[u, v] => (u, v, 1.0),
[u] => (u, 0.0, 1.0),
_ => make_error!(WrongNumberOfArguments, "Expected 1, 2 or 3 arguments"),
}),
"cstype" => {
let geometry = match args {
["rat", ty] => *ty,
[ty] => *ty,
_ => make_error!(WrongTypeOfArguments, "Expected 'rat xxx' or 'xxx' format"),
};
match geometry {
"bmatrix" => unimplemented!(),
"bezier" => unimplemented!(),
"bspline" => unimplemented!(),
"cardinal" => unimplemented!(),
"taylor" => unimplemented!(),
_ => make_error!(
WrongTypeOfArguments,
"Expected one of 'bmatrix', 'bezier', 'bspline', 'cardinal' and 'taylor'"
),
}
}
"deg" => match parse_args(args)?[..] {
[_deg_u, _deg_v] => unimplemented!(),
[_deg_u] => unimplemented!(),
_ => make_error!(WrongNumberOfArguments, "Expected 1 or 2 arguments"),
},
"bmat" => unimplemented!(),
"step" => unimplemented!(),
"p" => {
for v in args {
let v = try_index(&positions, v)?;
points.push(v);
}
}
"l" => match args {
[] => make_error!(WrongNumberOfArguments, "Expected at least 2 arguments"),
[first, rest @ ..] => {
if args.len() < 2 {
make_error!(WrongNumberOfArguments, "Expected at least 2 arguments")
}
let line = parse_args! {
first, rest,
[p] => Line::P[try_index(&positions, p)?],
[p, t] => Line::PT[(try_index(&positions, p)?, try_index(&tex_coords, t)?)],
! => make_error!(WrongTypeOfArguments, "Unexpected vertex format, expected `#`, or `#/#`")
};
lines.push(line);
}
},
"fo" | "f" => match args {
[] => make_error!(WrongNumberOfArguments, "Expected at least 3 arguments"),
[first, rest @ ..] => {
if args.len() < 3 {
make_error!(WrongNumberOfArguments, "Expected at least 3 arguments")
}
let polygon = parse_args! {
first, rest,
[p] => Polygon::P[try_index(&positions, p)?],
[p, t] => Polygon::PT[(try_index(&positions, p)?, try_index(&tex_coords, t)?)],
[p, "", n] => Polygon::PN[(try_index(&positions, p)?, try_index(&normals, n)?)],
[p, t, n] => Polygon::PTN[(try_index(&positions, p)?, try_index(&tex_coords, t)?, try_index(&normals, n)?)],
! => make_error!(WrongTypeOfArguments, "Unexpected vertex format, expected `#`, `#/#`, `#//#`, or `#/#/#`")
};
polygons.push(polygon);
}
},
"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()),
_ => make_error!(
WrongNumberOfArguments,
"Expected group name parameter, but nothing has been supplied"
),
},
"s" => match args {
["off"] | ["0"] => smoothing_builder.end(),
[param] => smoothing_builder.start(param.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected only 1 argument"),
},
"mg" => match args {
["off"] | ["0"] => merging_builder.end(),
[param] => merging_builder.start(param.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected only 1 argument"),
},
"o" => {
name = match args {
[] => None,
_ => Some(args.join(" ")),
}
}
"bevel" => unimplemented!(),
"c_interp" => unimplemented!(),
"d_interp" => unimplemented!(),
"lod" => unimplemented!(),
"usemtl" => match args {
[material] => mesh_builder.start((*material).to_string()),
_ => make_error!(WrongNumberOfArguments, "Expected only 1 argument"),
},
"mtllib" => {
material_libraries.reserve(args.len());
for &path in args {
material_libraries.push(path.to_string());
}
}
"shadow_obj" => unimplemented!(),
"trace_obj" => unimplemented!(),
"ctech" => unimplemented!(),
"stech" => unimplemented!(),
_ => make_error!(UnexpectedStatement, "Received unknown statement"),
}
Ok(())
})?;
group_builder.end();
mesh_builder.end();
smoothing_builder.end();
merging_builder.end();
Ok(RawObj {
name,
material_libraries,
positions,
tex_coords,
normals,
param_vertices,
points,
lines,
polygons,
groups: group_builder.result,
meshes: mesh_builder.result,
smoothing_groups: smoothing_builder.result,
merging_groups: merging_builder.result,
})
}
fn split_vertex_group(input: &str) -> Vec<&str> {
input.split('/').collect()
}
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,
lines,
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,
}
}
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> {
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<(f32, f32, f32, f32)>,
pub tex_coords: Vec<(f32, f32, f32)>,
pub normals: Vec<(f32, f32, f32)>,
pub param_vertices: Vec<(f32, f32, f32)>,
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(PartialEq, Eq, Clone, Debug)]
pub enum Line {
P(Vec<usize>),
PT(Vec<(usize, usize)>),
}
#[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,
}