1use std::{
19 borrow::Cow,
20 fmt,
21 io::{self, BufRead, BufReader, Error, Read, Write},
22 path::Path,
23 str::FromStr,
24 sync::Arc,
25};
26
27#[derive(Debug, Clone, PartialEq)]
29pub struct Material {
30 pub name: String,
31
32 pub ka: Option<[f32; 3]>,
34 pub kd: Option<[f32; 3]>,
35 pub ks: Option<[f32; 3]>,
36 pub ke: Option<[f32; 3]>,
37 pub km: Option<f32>,
38 pub tf: Option<[f32; 3]>,
39 pub ns: Option<f32>,
40 pub ni: Option<f32>,
41 pub tr: Option<f32>,
42 pub d: Option<f32>,
43 pub illum: Option<i32>,
44
45 pub map_ka: Option<String>,
47 pub map_kd: Option<String>,
48 pub map_ks: Option<String>,
49 pub map_ke: Option<String>,
50 pub map_ns: Option<String>,
51 pub map_d: Option<String>,
52 pub map_bump: Option<String>,
53 pub map_refl: Option<String>,
54}
55
56impl Material {
57 pub fn new(name: String) -> Self {
58 Material {
59 name,
60 ka: None,
61 kd: None,
62 ks: None,
63 ke: None,
64 km: None,
65 ns: None,
66 ni: None,
67 tr: None,
68 tf: None,
69 d: None,
70 map_ka: None,
71 map_kd: None,
72 map_ks: None,
73 map_ke: None,
74 map_ns: None,
75 map_d: None,
76 map_bump: None,
77 map_refl: None,
78 illum: None,
79 }
80 }
81}
82
83#[derive(Debug)]
85pub enum MtlMissingType {
86 I32,
88 F32,
90 String,
92}
93
94impl fmt::Display for MtlMissingType {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 match self {
97 MtlMissingType::I32 => write!(f, "i32"),
98 MtlMissingType::F32 => write!(f, "f32"),
99 MtlMissingType::String => write!(f, "String"),
100 }
101 }
102}
103
104#[derive(Debug)]
106pub enum MtlError {
107 Io(io::Error),
108 InvalidInstruction(String),
110 InvalidValue(String),
112 MissingMaterialName,
114 MissingValue(MtlMissingType),
116}
117
118impl std::error::Error for MtlError {
119 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
120 match self {
121 MtlError::Io(err) => Some(err),
122 _ => None,
123 }
124 }
125}
126
127impl fmt::Display for MtlError {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 match self {
130 MtlError::Io(err) => write!(f, "I/O error loading a .mtl file: {}", err),
131 MtlError::InvalidInstruction(instruction) => write!(f, "Unsupported mtl instruction: {}", instruction),
132 MtlError::InvalidValue(val) => write!(f, "Attempted to parse the value '{}' but failed.", val),
133 MtlError::MissingMaterialName => write!(f, "newmtl issued, but no name provided."),
134 MtlError::MissingValue(ty) => write!(f, "Instruction is missing a value of type '{}'", ty),
135 }
136 }
137}
138
139impl From<io::Error> for MtlError {
140 fn from(e: Error) -> Self {
141 Self::Io(e)
142 }
143}
144
145impl<'a> From<Material> for Cow<'a, Material> {
146 #[inline]
147 fn from(s: Material) -> Cow<'a, Material> {
148 Cow::Owned(s)
149 }
150}
151
152struct Parser<I>(I);
153
154impl<'a, I: Iterator<Item = &'a str>> Parser<I> {
155 fn get_vec(&mut self) -> Result<[f32; 3], MtlError> {
156 let (x, y, z) = match (self.0.next(), self.0.next(), self.0.next()) {
157 (Some(x), Some(y), Some(z)) => (x, y, z),
158 other => {
159 return Err(MtlError::InvalidValue(format!("{:?}", other)));
160 }
161 };
162
163 match (x.parse::<f32>(), y.parse::<f32>(), z.parse::<f32>()) {
164 (Ok(x), Ok(y), Ok(z)) => Ok([x, y, z]),
165 other => Err(MtlError::InvalidValue(format!("{:?}", other))),
166 }
167 }
168
169 fn get_i32(&mut self) -> Result<i32, MtlError> {
170 match self.0.next() {
171 Some(v) => FromStr::from_str(v).map_err(|_| MtlError::InvalidValue(v.to_string())),
172 None => Err(MtlError::MissingValue(MtlMissingType::I32)),
173 }
174 }
175
176 fn get_f32(&mut self) -> Result<f32, MtlError> {
177 match self.0.next() {
178 Some(v) => FromStr::from_str(v).map_err(|_| MtlError::InvalidValue(v.to_string())),
179 None => Err(MtlError::MissingValue(MtlMissingType::F32)),
180 }
181 }
182
183 fn into_string(mut self) -> Result<String, MtlError> {
184 match self.0.next() {
185 Some(v) => {
186 Ok(self.0.fold(v.to_string(), |mut existing, next| {
188 existing.push(' ');
189 existing.push_str(next);
190 existing
191 }))
192 }
193 None => Err(MtlError::MissingValue(MtlMissingType::String)),
194 }
195 }
196}
197
198#[derive(Debug, Clone, PartialEq)]
203pub struct Mtl {
204 pub filename: String,
206 pub materials: Vec<Arc<Material>>,
211}
212
213impl Mtl {
214 pub fn new(filename: String) -> Self {
216 Mtl {
217 filename,
218 materials: Vec::new(),
219 }
220 }
221
222 pub fn reload_with<R, F>(&mut self, obj_dir: impl AsRef<Path>, mut resolve: F) -> Result<&mut Self, MtlError>
226 where
227 R: BufRead,
228 F: FnMut(&Path, &str) -> io::Result<R>,
229 {
230 self.reload(resolve(obj_dir.as_ref(), &self.filename)?)
231 }
232
233 pub fn reload(&mut self, input: impl Read) -> Result<&mut Self, MtlError> {
237 self.materials.clear();
238 let input = BufReader::new(input);
239 let mut material = None;
240 for line in input.lines() {
241 let mut parser = match line {
242 Ok(ref line) => Parser(line.split_whitespace().filter(|s| !s.is_empty())),
243 Err(err) => return Err(MtlError::Io(err)),
244 };
245 match parser.0.next() {
246 Some("newmtl") => {
247 self.materials.extend(material.take().map(Arc::new));
248 material = Some(Material::new(
249 parser
250 .0
251 .next()
252 .ok_or_else(|| MtlError::MissingMaterialName)?
253 .to_string(),
254 ));
255 }
256 Some("Ka") => {
257 if let Some(ref mut m) = material {
258 m.ka = Some(parser.get_vec()?);
259 }
260 }
261 Some("Kd") => {
262 if let Some(ref mut m) = material {
263 m.kd = Some(parser.get_vec()?);
264 }
265 }
266 Some("Ks") => {
267 if let Some(ref mut m) = material {
268 m.ks = Some(parser.get_vec()?);
269 }
270 }
271 Some("Ke") => {
272 if let Some(ref mut m) = material {
273 m.ke = Some(parser.get_vec()?);
274 }
275 }
276 Some("Ns") => {
277 if let Some(ref mut m) = material {
278 m.ns = Some(parser.get_f32()?);
279 }
280 }
281 Some("Ni") => {
282 if let Some(ref mut m) = material {
283 m.ni = Some(parser.get_f32()?);
284 }
285 }
286 Some("Km") => {
287 if let Some(ref mut m) = material {
288 m.km = Some(parser.get_f32()?);
289 }
290 }
291 Some("d") => {
292 if let Some(ref mut m) = material {
293 m.d = Some(parser.get_f32()?);
294 }
295 }
296 Some("Tr") => {
297 if let Some(ref mut m) = material {
298 m.tr = Some(parser.get_f32()?);
299 }
300 }
301 Some("Tf") => {
302 if let Some(ref mut m) = material {
303 m.tf = Some(parser.get_vec()?);
304 }
305 }
306 Some("illum") => {
307 if let Some(ref mut m) = material {
308 m.illum = Some(parser.get_i32()?);
309 }
310 }
311 Some("map_Ka") => {
312 if let Some(ref mut m) = material {
313 m.map_ka = Some(parser.into_string()?);
314 }
315 }
316 Some("map_Kd") => {
317 if let Some(ref mut m) = material {
318 m.map_kd = Some(parser.into_string()?);
319 }
320 }
321 Some("map_Ks") => {
322 if let Some(ref mut m) = material {
323 m.map_ks = Some(parser.into_string()?);
324 }
325 }
326 Some("map_Ns") => {
327 if let Some(ref mut m) = material {
328 m.map_ns = Some(parser.into_string()?);
329 }
330 }
331 Some("map_d") => {
332 if let Some(ref mut m) = material {
333 m.map_d = Some(parser.into_string()?);
334 }
335 }
336 Some("map_refl") | Some("refl") => {
337 if let Some(ref mut m) = material {
338 m.map_refl = Some(parser.into_string()?);
339 }
340 }
341 Some("map_bump") | Some("map_Bump") | Some("bump") => {
342 if let Some(ref mut m) = material {
343 m.map_bump = Some(parser.into_string()?);
344 }
345 }
346 Some(other) => {
347 if !other.starts_with('#') {
348 return Err(MtlError::InvalidInstruction(other.to_string()));
349 }
350 }
351 None => {}
352 }
353 }
354
355 if let Some(material) = material {
356 self.materials.push(Arc::new(material));
357 }
358
359 Ok(self)
360 }
361
362 pub fn write_to_buf(&self, out: &mut impl Write) -> Result<(), io::Error> {
363 for mtl in &self.materials {
364 writeln!(out, "newmtl {}", mtl.name)?;
365 if let Some([ka0, ka1, ka2]) = mtl.ka {
366 writeln!(out, "Ka {} {} {}", ka0, ka1, ka2)?;
367 }
368 if let Some([kd0, kd1, kd2]) = mtl.kd {
369 writeln!(out, "Kd {} {} {}", kd0, kd1, kd2)?;
370 }
371 if let Some([ks0, ks1, ks2]) = mtl.ks {
372 writeln!(out, "Ks {} {} {}", ks0, ks1, ks2)?;
373 }
374 if let Some([ke0, ke1, ke2]) = mtl.ke {
375 writeln!(out, "Ke {} {} {}", ke0, ke1, ke2)?;
376 }
377 if let Some(ns) = mtl.ns {
378 writeln!(out, "Ns {}", ns)?;
379 }
380 if let Some(ns) = mtl.ns {
381 writeln!(out, "Ns {}", ns)?;
382 }
383 if let Some(ni) = mtl.ni {
384 writeln!(out, "Ni {}", ni)?;
385 }
386 if let Some(km) = mtl.km {
387 writeln!(out, "Km {}", km)?;
388 }
389 if let Some(d) = mtl.d {
390 writeln!(out, "d {}", d)?;
391 }
392 if let Some(tr) = mtl.tr {
393 writeln!(out, "Tr {}", tr)?;
394 }
395 if let Some([tf0, tf1, tf2]) = mtl.tf {
396 writeln!(out, "Tf {} {} {}", tf0, tf1, tf2)?;
397 }
398 if let Some(illum) = mtl.illum {
399 writeln!(out, "illum {}", illum)?;
400 }
401 if let Some(map_ka) = &mtl.map_ka {
402 writeln!(out, "map_Ka {}", map_ka)?;
403 }
404 if let Some(map_kd) = &mtl.map_kd {
405 writeln!(out, "map_Kd {}", map_kd)?;
406 }
407 if let Some(map_ks) = &mtl.map_ks {
408 writeln!(out, "map_Ks {}", map_ks)?;
409 }
410 if let Some(map_d) = &mtl.map_d {
411 writeln!(out, "map_d {}", map_d)?;
412 }
413 if let Some(map_refl) = &mtl.map_refl {
414 writeln!(out, "refl {}", map_refl)?;
415 }
416 if let Some(map_bump) = &mtl.map_bump {
417 writeln!(out, "bump {}", map_bump)?;
418 }
419 }
420 Ok(())
421 }
422}