use std::collections::HashMap;
use std::io::prelude::*;
use std::mem::replace;
use error::ObjResult;
use raw::lexer::lex;
macro_rules! f {
($args:expr) => (
&{
let mut ret = Vec::<f32>::new();
ret.reserve($args.len());
for arg in $args {
ret.push(arg.parse()?)
}
ret
}[..]
)
}
pub fn parse_mtl<T: BufRead>(input: T) -> ObjResult<RawMtl> {
let mut materials = HashMap::new();
let mut name: Option<String> = None;
let mut mat: Material = Material::default();
lex(input, |stmt, args| {
match stmt {
"newmtl" => {
if let Some(name) = name.take() {
materials.insert(name, replace(&mut mat, Material::default()));
}
match args.len() {
1 => name = Some(args[0].to_owned()),
_ => error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
}
}
"Ka" => mat.ambient = Some(parse_color(args)?),
"Kd" => mat.diffuse = Some(parse_color(args)?),
"Ks" => mat.specular = Some(parse_color(args)?),
"Ke" => mat.emissive = Some(parse_color(args)?),
"Km" => unimplemented!(),
"Tf" => mat.transmission_filter = Some(parse_color(args)?),
"Ns" => match args.len() {
1 => mat.specular_exponent = Some(args[0].parse()?),
_ => error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"Ni" => match args.len() {
1 => mat.optical_density = Some(args[0].parse()?),
_ => error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"illum" => match args.len() {
1 => mat.illumination_model = Some(args[0].parse()?),
_ => error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"d" => match args.len() {
1 => mat.dissolve = Some(args[0].parse()?),
_ => error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"Tr" => match args.len() {
1 => mat.dissolve = Some(1.0 - args[0].parse::<f32>()?),
_ => error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"map_Ka" => mat.ambient_map = Some(parse_texture_map(args)?),
"map_Kd" => mat.diffuse_map = Some(parse_texture_map(args)?),
"map_Ks" => mat.specular_map = Some(parse_texture_map(args)?),
"map_Ke" => mat.emissive_map = Some(parse_texture_map(args)?),
"map_d" => mat.dissolve_map = Some(parse_texture_map(args)?),
"map_aat" => unimplemented!(),
"map_refl" => unimplemented!(),
"map_bump" | "map_Bump" | "bump" => mat.bump_map = Some(parse_texture_map(args)?),
"disp" => unimplemented!(),
"refl" => unimplemented!(),
_ => error!(UnexpectedStatement, "Received unknown statement"),
}
Ok(())
})?;
if let Some(name) = name {
materials.insert(name, mat);
}
Ok(RawMtl { materials })
}
fn parse_color(args: &[&str]) -> ObjResult<MtlColor> {
if args.is_empty() {
error!(WrongNumberOfArguments, "Expected at least 1 argument");
}
Ok(match args[0] {
"xyz" => {
let args = f!(&args[1..]);
match args.len() {
1 => MtlColor::Xyz(args[0], args[0], args[0]),
3 => MtlColor::Xyz(args[0], args[1], args[2]),
_ => error!(WrongNumberOfArguments, "Expected 1 or 3 color values"),
}
}
"spectral" => match args.len() {
2 => MtlColor::Spectral(args[1].to_owned(), 1.0),
3 => MtlColor::Spectral(args[1].to_owned(), args[2].parse()?),
_ => error!(WrongNumberOfArguments, "Expected 2 or 3 arguments"),
},
_ => {
let args = f!(args);
match args.len() {
1 => MtlColor::Rgb(args[0], args[0], args[0]),
3 => MtlColor::Rgb(args[0], args[1], args[2]),
_ => error!(WrongNumberOfArguments, "Expected 1 or 3 color values"),
}
}
})
}
fn parse_texture_map(args: &[&str]) -> ObjResult<MtlTextureMap> {
if args.len() == 1 {
Ok(MtlTextureMap {
file: args[0].to_owned(),
})
} else {
error!(WrongNumberOfArguments, "Expected exactly 1 argument")
}
}
#[derive(Clone, Debug)]
pub struct RawMtl {
pub materials: HashMap<String, Material>,
}
#[derive(Clone, PartialEq, Debug, Default)]
pub struct Material {
pub ambient: Option<MtlColor>,
pub diffuse: Option<MtlColor>,
pub specular: Option<MtlColor>,
pub emissive: Option<MtlColor>,
pub transmission_filter: Option<MtlColor>,
pub illumination_model: Option<u32>,
pub dissolve: Option<f32>,
pub specular_exponent: Option<f32>,
pub optical_density: Option<f32>,
pub ambient_map: Option<MtlTextureMap>,
pub diffuse_map: Option<MtlTextureMap>,
pub specular_map: Option<MtlTextureMap>,
pub emissive_map: Option<MtlTextureMap>,
pub dissolve_map: Option<MtlTextureMap>,
pub bump_map: Option<MtlTextureMap>,
}
#[derive(Clone, PartialEq, Debug)]
pub enum MtlColor {
Rgb(f32, f32, f32),
Xyz(f32, f32, f32),
Spectral(String, f32),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct MtlTextureMap {
pub file: String,
}