use std::collections::HashMap;
use std::io::BufRead;
use std::mem::take;
use crate::error::ObjResult;
use crate::raw::lexer::lex;
use crate::raw::util::parse_args;
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, take(&mut mat));
}
match args {
[arg] => name = Some((*arg).to_string()),
_ => make_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 {
[arg] => mat.specular_exponent = Some(arg.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"Ni" => match args {
[arg] => mat.optical_density = Some(arg.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"illum" => match args {
[arg] => mat.illumination_model = Some(arg.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"d" => match args {
[arg] => mat.dissolve = Some(arg.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
},
"Tr" => match args {
[arg] => mat.dissolve = Some(1.0 - arg.parse::<f32>()?),
_ => make_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!(),
_ => make_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() {
make_error!(WrongNumberOfArguments, "Expected at least 1 argument");
}
Ok(match args[0] {
"xyz" => match parse_args(&args[1..])?[..] {
[x] => MtlColor::Xyz(x, x, x),
[x, y, z] => MtlColor::Xyz(x, y, z),
_ => make_error!(WrongNumberOfArguments, "Expected 1 or 3 color values"),
},
"spectral" => match args[1..] {
[name] => MtlColor::Spectral(name.to_string(), 1.0),
[name, multiplier] => MtlColor::Spectral(name.to_string(), multiplier.parse()?),
_ => make_error!(WrongNumberOfArguments, "Expected 1 or 2 arguments"),
},
_ => match parse_args(args)?[..] {
[r] => MtlColor::Rgb(r, r, r),
[r, g, b] => MtlColor::Rgb(r, g, b),
_ => make_error!(WrongNumberOfArguments, "Expected 1 or 3 color values"),
},
})
}
fn parse_texture_map(args: &[&str]) -> ObjResult<MtlTextureMap> {
match args {
[file] => Ok(MtlTextureMap {
file: (*file).to_string(),
}),
_ => make_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,
}