use std::{collections::HashMap, result::Result};
use derive_more::{Constructor, From, Into};
use crate::{
get_on_off_from_str, get_token_float, get_token_int, get_token_string,
tokenizer::{Token, TokenSet},
};
use nom::{
branch::alt,
combinator::{map, opt},
error,
multi::{fold_many0, fold_many1, many1},
sequence::preceded,
IResult, Parser,
};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ModelError {
#[error("Parse Error: `{0}`")]
Parse(String),
}
#[derive(Copy, Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Vertex {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: Option<f32>,
}
#[derive(Copy, Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Normal {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Copy, Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Texture {
pub u: f32,
pub v: Option<f32>,
pub w: Option<f32>,
}
#[derive(Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Group {
pub material_name: String,
pub bevel: bool,
pub c_interp: bool,
pub d_interp: bool,
pub lod: u8,
pub texture_map: Option<String>,
}
#[derive(Copy, Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct FaceElement {
pub vertex_index: i32,
pub texture_index: Option<i32>,
pub normal_index: Option<i32>,
}
#[derive(Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Face {
pub elements: Vec<FaceElement>,
pub smoothing_group: i32,
}
#[derive(Copy, Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct LineElement {
pub vertex_index: i32,
pub texture_index: Option<i32>,
}
#[derive(Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Line {
pub elements: Vec<LineElement>,
}
#[derive(Clone, Constructor, Debug, Default, From, Into, PartialEq)]
pub struct Point {
pub elements: Vec<i32>,
}
#[derive(Clone, Debug, From, Into)]
pub struct Model {
pub vertices: Vec<Vertex>,
pub normals: Vec<Normal>,
pub textures: Vec<Texture>,
pub faces: HashMap<String, Vec<Face>>,
pub lines: HashMap<String, Vec<Line>>,
pub points: HashMap<String, Vec<Point>>,
pub groups: HashMap<String, Group>,
pub material_libs: Vec<String>,
pub texture_libs: Vec<String>,
pub shadow_obj: Option<String>,
pub trace_obj: Option<String>,
current_group: Vec<String>,
current_smoothing_group: i32,
}
impl Default for Model {
fn default() -> Self {
Self {
vertices: Default::default(),
normals: Default::default(),
textures: Default::default(),
faces: Default::default(),
lines: Default::default(),
points: Default::default(),
groups: {
let mut res = HashMap::new();
res.insert("default".into(), Default::default());
res
},
material_libs: Default::default(),
texture_libs: Default::default(),
shadow_obj: Default::default(),
trace_obj: Default::default(),
current_group: vec!["default".into()],
current_smoothing_group: 0,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum ModelElement {
Vertex(Vertex),
Normal(Normal),
Texture(Texture),
Face(Face),
Line(Line),
Point(Point),
Group(Vec<String>),
MaterialLib(Vec<String>),
Material(String),
ObjName(String),
Smoothing(i32),
Bevel(bool),
CInterp(bool),
DInterp(bool),
Lod(i32),
ShadowObj(String),
TraceObj(String),
TextureLib(Vec<String>),
TextureMap(String),
}
pub(crate) fn parse(input: TokenSet) -> Result<Model, ModelError> {
match fold_many0(
alt((
map(parse_vertex(), ModelElement::Vertex),
map(parse_vertex_normal(), ModelElement::Normal),
map(parse_vertex_texture(), ModelElement::Texture),
map(parse_face(), ModelElement::Face),
map(parse_line(), ModelElement::Line),
map(parse_point(), ModelElement::Point),
parse_mat_lib(),
parse_material(),
parse_obj_name(),
parse_smoothing(),
parse_bevel(),
parse_c_interp(),
parse_d_interp(),
parse_lod(),
parse_shadow_obj(),
parse_trace_obj(),
parse_texture_lib(),
parse_texture_map(),
parse_group(),
)),
Model::default,
|mut model: Model, item: ModelElement| {
match item {
ModelElement::Vertex(x) => model.vertices.push(x),
ModelElement::Normal(n) => model.normals.push(n),
ModelElement::Texture(t) => model.textures.push(t),
ModelElement::Face(mut f) => {
f.smoothing_group = model.current_smoothing_group;
for g in &model.current_group {
let set = model.faces.entry(g.clone()).or_default();
set.push(f.clone());
}
},
ModelElement::Line(l) => {
for g in &model.current_group {
let set = model.lines.entry(g.clone()).or_default();
set.push(l.clone());
}
},
ModelElement::Point(p) => {
for g in &model.current_group {
let set = model.points.entry(g.clone()).or_default();
set.push(p.clone());
}
},
ModelElement::Group(groups) => {
model.current_group.clear();
for g in groups {
model.groups.insert(g.clone(), Default::default());
model.current_group.push(g);
}
},
ModelElement::MaterialLib(libs) => model.material_libs.extend(libs),
ModelElement::Material(name) => {
for g in &model.current_group {
let group = model.groups.entry(g.clone()).or_default();
group.material_name = name.clone();
}
},
ModelElement::ObjName(_name) => {},
ModelElement::Smoothing(group_id) => {
model.current_smoothing_group = group_id;
},
ModelElement::Bevel(_flag) => {},
ModelElement::CInterp(_flag) => {},
ModelElement::DInterp(_flag) => {},
ModelElement::Lod(_level) => {},
ModelElement::ShadowObj(_name) => {},
ModelElement::TraceObj(_name) => {},
ModelElement::TextureLib(libs) => {
model.texture_libs.extend(libs);
},
ModelElement::TextureMap(name) => {
for g in &model.current_group {
let group = model.groups.entry(g.clone()).or_default();
group.texture_map = Some(name.clone());
}
},
}
model
},
)
.parse_complete(input)
{
Ok((_, acc)) => Ok(acc),
Err(e) => Err(ModelError::Parse(e.to_string())),
}
}
pub(crate) fn parse_vertex<'a>(
) -> impl Parser<TokenSet<'a>, Output = Vertex, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::Vertex),
(
token_match!(Token::Float(_) | Token::Int(_)),
token_match!(Token::Float(_) | Token::Int(_)),
token_match!(Token::Float(_) | Token::Int(_)),
opt(token_match!(Token::Float(_) | Token::Int(_))),
),
),
|(x, y, z, w)| {
let (x, y, z) = (
match get_token_float(&x) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
match get_token_float(&y) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
match get_token_float(&z) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
);
let w = w.map(|val| match get_token_float(&val) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
});
(x, y, z, w).into()
},
)
}
pub(crate) fn parse_vertex_normal<'a>(
) -> impl Parser<TokenSet<'a>, Output = Normal, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::VertexNormal),
(
token_match!(Token::Float(_) | Token::Int(_)),
token_match!(Token::Float(_) | Token::Int(_)),
token_match!(Token::Float(_) | Token::Int(_)),
),
),
|(x, y, z)| {
let (x, y, z) = (
match get_token_float(&x) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
match get_token_float(&y) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
match get_token_float(&z) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
);
(x, y, z).into()
},
)
}
pub(crate) fn parse_vertex_texture<'a>(
) -> impl Parser<TokenSet<'a>, Output = Texture, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::VertexTexture),
(
token_match!(Token::Float(_) | Token::Int(_)),
opt(token_match!(Token::Float(_) | Token::Int(_))),
opt(token_match!(Token::Float(_) | Token::Int(_))),
),
),
|(u, v, w)| {
let u = match get_token_float(&u) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
let v = v.map(|val| match get_token_float(&val) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
});
let w = w.map(|val| match get_token_float(&val) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
});
(u, v, w).into()
},
)
}
pub(crate) fn parse_face<'a>(
) -> impl Parser<TokenSet<'a>, Output = Face, Error = error::Error<TokenSet<'a>>> {
preceded(
token_match!(Token::Face),
fold_many1(
map(
(
token_match!(Token::Int(_)),
opt(preceded(
token_match!(Token::Slash),
opt(token_match!(Token::Int(_))),
)),
opt(preceded(
token_match!(Token::Slash),
opt(token_match!(Token::Int(_))),
)),
),
|(v, t, n)| {
let v = match get_token_int(&v) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
let t = match t {
Some(t) => t.map(|tex| match get_token_int(&tex) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
}),
None => None,
};
let n = match n {
Some(n) => n.map(|norm| match get_token_int(&norm) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
}),
None => None,
};
(v, t, n).into()
},
),
Face::default,
|mut f: Face, item: FaceElement| {
f.elements.push(item);
f
},
),
)
}
pub(crate) fn parse_line<'a>(
) -> impl Parser<TokenSet<'a>, Output = Line, Error = error::Error<TokenSet<'a>>> {
preceded(
token_match!(Token::Line),
fold_many1(
map(
(
token_match!(Token::Int(_)),
opt(preceded(
token_match!(Token::Slash),
opt(token_match!(Token::Int(_))),
)),
),
|(v, t)| {
let v = match get_token_int(&v) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
let t = t.flatten().map(|tex| match get_token_int(&tex) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
});
(v, t).into()
},
),
Line::default,
|mut f: Line, item: LineElement| {
f.elements.push(item);
f
},
),
)
}
pub(crate) fn parse_point<'a>(
) -> impl Parser<TokenSet<'a>, Output = Point, Error = error::Error<TokenSet<'a>>> {
preceded(
token_match!(Token::Point),
fold_many1(
map(token_match!(Token::Int(_)), |v| match get_token_int(&v) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
}),
Point::default,
|mut f: Point, item: i32| {
f.elements.push(item);
f
},
),
)
}
pub(crate) fn parse_group<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::Group),
many1(map(
token_match!(Token::String(_)),
|s| match get_token_string(&s) {
Ok(s) => s.into(),
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
)),
),
ModelElement::Group,
)
}
pub(crate) fn parse_mat_lib<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::MaterialLib),
many1(map(
token_match!(Token::String(_)),
|s| match get_token_string(&s) {
Ok(s) => s.into(),
Err(e) => {
log::error!("{}", e);
Default::default()
},
},
)),
),
ModelElement::MaterialLib,
)
}
pub(crate) fn parse_material<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::UseMaterial),
token_match!(Token::String(_)),
),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::Material(res.into())
},
)
}
pub(crate) fn parse_obj_name<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::Object),
token_match!(Token::String(_) | Token::Int(_)),
),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::ObjName(res.into())
},
)
}
pub(crate) fn parse_smoothing<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::Smoothing),
alt((
token_match!(Token::Int(_)),
map(token_match!(Token::String(_)), |s| {
let val = match get_on_off_from_str(&s) {
Ok(v) => v,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
if !val {
Token::Int(0)
} else {
log::error!("Invalid smoothing value encountered. Setting default to 1.");
Token::Int(1)
}
}),
)),
),
|s| {
let res = match get_token_int(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::Smoothing(res)
},
)
}
pub(crate) fn parse_bevel<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(token_match!(Token::Bevel), token_match!(Token::String(_))),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
if let Ok(flag) = res.parse::<bool>() {
ModelElement::Bevel(flag)
} else {
ModelElement::Bevel(false)
}
},
)
}
pub(crate) fn parse_c_interp<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(token_match!(Token::CInterp), token_match!(Token::String(_))),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
if let Ok(flag) = res.parse::<bool>() {
ModelElement::CInterp(flag)
} else {
ModelElement::CInterp(false)
}
},
)
}
pub(crate) fn parse_d_interp<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(token_match!(Token::DInterp), token_match!(Token::String(_))),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
if let Ok(flag) = res.parse::<bool>() {
ModelElement::DInterp(flag)
} else {
ModelElement::DInterp(false)
}
},
)
}
pub(crate) fn parse_lod<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(token_match!(Token::Lod), token_match!(Token::Int(_))),
|s| {
let res = match get_token_int(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::Lod(res)
},
)
}
pub(crate) fn parse_shadow_obj<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::ShadowObj),
token_match!(Token::String(_)),
),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::ShadowObj(res.into())
},
)
}
pub(crate) fn parse_trace_obj<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::TraceObj),
token_match!(Token::String(_)),
),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::TraceObj(res.into())
},
)
}
pub(crate) fn parse_texture_lib<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::TextureMapLib),
many1(map(token_match!(Token::String(_)), |s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
res.into()
})),
),
ModelElement::TextureLib,
)
}
pub(crate) fn parse_texture_map<'a>(
) -> impl Parser<TokenSet<'a>, Output = ModelElement, Error = error::Error<TokenSet<'a>>> {
map(
preceded(
token_match!(Token::UseTextureMap),
token_match!(Token::String(_)),
),
|s| {
let res = match get_token_string(&s) {
Ok(s) => s,
Err(e) => {
log::error!("{}", e);
Default::default()
},
};
ModelElement::TextureMap(res.into())
},
)
}