1use std::collections::HashMap;
4use std::io::BufRead;
5use std::mem::take;
6
7use crate::error::{make_error, ObjResult};
8use crate::raw::lexer::lex;
9use crate::raw::util::parse_args;
10
11pub fn parse_mtl<T: BufRead>(input: T) -> ObjResult<RawMtl> {
13 let mut materials = HashMap::new();
14
15 let mut name: Option<String> = None;
17 let mut mat: Material = Material::default();
18
19 lex(input, |stmt, args| {
20 match stmt {
21 "newmtl" => {
23 if let Some(name) = name.take() {
25 materials.insert(name, take(&mut mat));
26 }
27
28 match args {
29 [arg] => name = Some((*arg).to_string()),
30 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
31 }
32 }
33
34 "Ka" => mat.ambient = Some(parse_color(args)?),
36 "Kd" => mat.diffuse = Some(parse_color(args)?),
37 "Ks" => mat.specular = Some(parse_color(args)?),
38 "Ke" => mat.emissive = Some(parse_color(args)?),
39 "Km" => unimplemented!(),
40 "Tf" => mat.transmission_filter = Some(parse_color(args)?),
41 "Ns" => match args {
42 [arg] => mat.specular_exponent = Some(arg.parse()?),
43 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
44 },
45 "Ni" => match args {
46 [arg] => mat.optical_density = Some(arg.parse()?),
47 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
48 },
49 "illum" => match args {
50 [arg] => mat.illumination_model = Some(arg.parse()?),
51 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
52 },
53 "d" => match args {
54 [arg] => mat.dissolve = Some(arg.parse()?),
55 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
56 },
57 "Tr" => match args {
58 [arg] => mat.dissolve = Some(1.0 - arg.parse::<f32>()?),
59 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
60 },
61
62 "map_Ka" => mat.ambient_map = Some(parse_texture_map(args)?),
64 "map_Kd" => mat.diffuse_map = Some(parse_texture_map(args)?),
65 "map_Ks" => mat.specular_map = Some(parse_texture_map(args)?),
66 "map_Ke" => mat.emissive_map = Some(parse_texture_map(args)?),
67 "map_d" => mat.dissolve_map = Some(parse_texture_map(args)?),
68 "map_aat" => unimplemented!(),
69 "map_refl" => unimplemented!(),
70 "map_bump" | "map_Bump" | "bump" => mat.bump_map = Some(parse_texture_map(args)?),
71 "disp" => unimplemented!(),
72
73 "refl" => unimplemented!(),
75
76 _ => make_error!(UnexpectedStatement, "Received unknown statement"),
78 }
79
80 Ok(())
81 })?;
82
83 if let Some(name) = name {
85 materials.insert(name, mat);
86 }
87
88 Ok(RawMtl { materials })
89}
90
91fn parse_color(args: &[&str]) -> ObjResult<MtlColor> {
93 if args.is_empty() {
94 make_error!(WrongNumberOfArguments, "Expected at least 1 argument");
95 }
96
97 Ok(match args[0] {
98 "xyz" => match parse_args(&args[1..])?[..] {
99 [x] => MtlColor::Xyz(x, x, x),
100 [x, y, z] => MtlColor::Xyz(x, y, z),
101 _ => make_error!(WrongNumberOfArguments, "Expected 1 or 3 color values"),
102 },
103
104 "spectral" => match args[1..] {
105 [name] => MtlColor::Spectral(name.to_string(), 1.0),
106 [name, multiplier] => MtlColor::Spectral(name.to_string(), multiplier.parse()?),
107 _ => make_error!(WrongNumberOfArguments, "Expected 1 or 2 arguments"),
108 },
109
110 _ => match parse_args(args)?[..] {
111 [r] => MtlColor::Rgb(r, r, r),
112 [r, g, b] => MtlColor::Rgb(r, g, b),
113 _ => make_error!(WrongNumberOfArguments, "Expected 1 or 3 color values"),
114 },
115 })
116}
117
118fn parse_texture_map(args: &[&str]) -> ObjResult<MtlTextureMap> {
120 match args {
121 [file] => Ok(MtlTextureMap {
122 file: (*file).to_string(),
123 }),
124 _ => make_error!(WrongNumberOfArguments, "Expected exactly 1 argument"),
125 }
126}
127
128#[derive(Clone, PartialEq, Debug, Default)]
130pub struct RawMtl {
131 pub materials: HashMap<String, Material>,
133}
134
135#[derive(Clone, PartialEq, Debug, Default)]
137pub struct Material {
138 pub ambient: Option<MtlColor>,
140 pub diffuse: Option<MtlColor>,
142 pub specular: Option<MtlColor>,
144 pub emissive: Option<MtlColor>,
146 pub transmission_filter: Option<MtlColor>,
148 pub illumination_model: Option<u32>,
150 pub dissolve: Option<f32>,
152 pub specular_exponent: Option<f32>,
154 pub optical_density: Option<f32>,
156 pub ambient_map: Option<MtlTextureMap>,
158 pub diffuse_map: Option<MtlTextureMap>,
160 pub specular_map: Option<MtlTextureMap>,
162 pub emissive_map: Option<MtlTextureMap>,
164 pub dissolve_map: Option<MtlTextureMap>,
166 pub bump_map: Option<MtlTextureMap>,
168}
169
170#[derive(Clone, PartialEq, Debug)]
172pub enum MtlColor {
173 Rgb(f32, f32, f32),
175 Xyz(f32, f32, f32),
177 Spectral(String, f32),
182}
183
184#[derive(Clone, PartialEq, Eq, Debug)]
186pub struct MtlTextureMap {
187 pub file: String,
189 }