1#![allow(clippy::collapsible_if, clippy::many_single_char_names)]
6
7mod error;
8
9use std::{
10 collections::HashMap,
11 io, mem,
12 path::{Path, PathBuf},
13 str,
14};
15
16use self::error::ErrorKind;
17use crate::{
18 common,
19 utils::{
20 bytes::{from_utf8_lossy, memchr_naive, memchr_naive_table, path_from_bytes, starts_with},
21 float, int,
22 utf16::decode_bytes,
23 },
24 Color4, Mesh, Scene, ShadingModel, Vec2, Vec3,
25};
26
27pub fn from_slice<B: AsRef<[u8]>, F: FnMut(&Path) -> io::Result<B>>(
29 bytes: &[u8],
30 path: Option<&Path>,
31 mut reader: F,
32) -> io::Result<Scene> {
33 let bytes = &decode_bytes(bytes)?;
37 match read_obj(bytes, path, &mut |path, materials, material_map| {
38 match reader(path) {
39 Ok(bytes) => read_mtl(bytes.as_ref(), Some(path), materials, material_map),
40 Err(_e) => Ok(()),
43 }
44 }) {
45 Ok((meshes, materials)) => {
46 let materials = meshes
47 .iter()
48 .map(|m| {
49 materials
50 .get(m.material_index as usize)
51 .cloned()
52 .unwrap_or_default()
53 })
54 .collect();
55 Ok(Scene { materials, meshes })
56 }
57 Err(e) => Err(e.into_io_error(bytes, path)),
58 }
59}
60
61fn read_obj(
65 mut s: &[u8],
66 obj_path: Option<&Path>,
67 reader: &mut dyn FnMut(
68 &Path,
69 &mut Vec<common::Material>,
70 &mut HashMap<Vec<u8>, u32>,
71 ) -> io::Result<()>,
72) -> Result<(Vec<Mesh>, Vec<common::Material>), ErrorKind> {
73 let mut meshes = Vec::with_capacity(1); let mut vertices = vec![];
77 let mut normals = vec![];
78 let mut texcoords = vec![];
79 let mut colors = vec![];
80 let mut face = Vec::with_capacity(3);
81 let mut faces: Vec<Face> = vec![];
82 let mut current_group: &[u8] = b"default";
83 let mut current_material: &[u8] = &[];
84 let mut materials = vec![];
85 let mut material_map = HashMap::default();
86
87 while let Some((&c, s_next)) = s.split_first() {
88 match c {
89 b'v' => {
90 s = s_next;
91 match s.first() {
92 Some(b' ' | b'\t') => {
93 skip_spaces(&mut s);
94 read_v(&mut s, &mut vertices, &mut colors)?;
95 if !colors.is_empty() && colors.len() < vertices.len() {
96 colors.resize(vertices.len(), [0.; 3]);
97 }
98 continue;
99 }
100 Some(b'n') => {
101 s = &s[1..];
102 if skip_spaces(&mut s) {
103 read_vn(&mut s, &mut normals)?;
104 continue;
105 }
106 }
107 Some(b't') => {
108 s = &s[1..];
109 if skip_spaces(&mut s) {
110 read_vt(&mut s, &mut texcoords)?;
111 continue;
112 }
113 }
114 _ => {}
116 }
117 }
118 b'f' => {
119 s = s_next;
120 if skip_spaces(&mut s) {
121 read_f(
122 &mut s, &mut faces, &mut face, &vertices, &texcoords, &normals,
123 )?;
124 continue;
125 }
126 }
127 b'u' => {
128 s = s_next;
129 if token(&mut s, &b"usemtl"[1..]) {
130 if skip_spaces(&mut s) {
131 let (name, s_next) = name(s);
132 if name != current_material {
133 let material_index = material_map.get(current_material).copied();
134 push_mesh(
135 &mut meshes,
136 &mut faces,
137 &vertices,
138 &texcoords,
139 &normals,
140 &colors,
141 current_group,
142 material_index,
143 )?;
144 current_material = name;
145 }
146 s = s_next;
147 continue;
148 }
149 }
150 }
151 b'm' => {
152 s = s_next;
153 if token(&mut s, &b"mtllib"[1..]) {
154 if skip_spaces(&mut s) {
155 let (path, s_next) = name(s);
156 let path = if path.is_empty() {
157 None
158 } else {
159 path_from_bytes(path).ok()
160 };
161 if let Some(path) = path {
162 match obj_path.and_then(Path::parent) {
163 Some(parent) => {
164 reader(&parent.join(path), &mut materials, &mut material_map)
165 .map_err(ErrorKind::Io)?;
166 }
167 None => {} }
169 }
170 s = s_next;
171 continue;
172 }
173 }
174 }
176 b'g' => {
177 s = s_next;
178 if skip_spaces(&mut s) {
179 let (mut name, s_next) = name(s);
180 if name.is_empty() {
181 name = b"default";
182 }
183 if name != current_group {
184 let material_index = material_map.get(current_material).copied();
185 push_mesh(
186 &mut meshes,
187 &mut faces,
188 &vertices,
189 &texcoords,
190 &normals,
191 &colors,
192 current_group,
193 material_index,
194 )?;
195 current_material = &[];
196 current_group = name;
197 }
198 s = s_next;
199 continue;
200 }
201 }
202 _ => {}
203 }
204 skip_any_until_line(&mut s);
206 }
207
208 let material_index = material_map.get(current_material).copied();
209 push_mesh(
210 &mut meshes,
211 &mut faces,
212 &vertices,
213 &texcoords,
214 &normals,
215 &colors,
216 current_group,
217 material_index,
218 )?;
219
220 Ok((meshes, materials))
221}
222
223#[inline(always)]
224fn read_v(
225 s: &mut &[u8],
226 vertices: &mut Vec<Vec3>,
227 colors: &mut Vec<Vec3>,
228) -> Result<(), ErrorKind> {
229 let vertex = read_float3(s, "v")?;
231 let has_space = skip_spaces(s);
232 match s.first() {
233 Some(b'\n' | b'\r') | None => {
234 vertices.push(vertex);
235 *s = s.get(1..).unwrap_or_default();
236 return Ok(());
237 }
238 _ if !has_space => return Err(ErrorKind::ExpectedSpace("v", s.len())),
239 _ => {}
240 }
241 let w = match float::parse_partial::<f32>(s) {
243 Some((f, n)) => {
244 *s = &s[n..];
245 f
246 }
247 None => return Err(ErrorKind::Float(s.len())),
248 };
249 let has_space = skip_spaces(s);
250 match s.first() {
251 Some(b'\n' | b'\r') | None => {
252 if w == 0. {
254 return Err(ErrorKind::InvalidW(s.len()));
255 }
256 vertices.push([vertex[0] / w, vertex[1] / w, vertex[2] / w]);
257 *s = s.get(1..).unwrap_or_default();
258 return Ok(());
259 }
260 _ if !has_space => return Err(ErrorKind::ExpectedSpace("v", s.len())),
261 _ => {}
262 }
263 vertices.push(vertex);
264 let r = w;
266 let g = match float::parse_partial::<f32>(s) {
267 Some((f, n)) => {
268 *s = &s[n..];
269 f
270 }
271 None => return Err(ErrorKind::Float(s.len())),
272 };
273 if !skip_spaces(s) {
274 return Err(ErrorKind::ExpectedSpace("v", s.len()));
275 }
276 let b = match float::parse_partial::<f32>(s) {
277 Some((f, n)) => {
278 *s = &s[n..];
279 f
280 }
281 None => return Err(ErrorKind::Float(s.len())),
282 };
283 colors.push([r, g, b]);
284 if !skip_spaces_until_line(s) {
285 return Err(ErrorKind::ExpectedNewline("v", s.len()));
286 }
287 Ok(())
288}
289
290fn read_vn(s: &mut &[u8], normals: &mut Vec<Vec3>) -> Result<(), ErrorKind> {
291 let normal = read_float3(s, "vn")?;
293 normals.push(normal);
294 if !skip_spaces_until_line(s) {
295 return Err(ErrorKind::ExpectedNewline("vn", s.len()));
296 }
297 Ok(())
298}
299
300fn read_vt(s: &mut &[u8], texcoords: &mut Vec<Vec2>) -> Result<(), ErrorKind> {
301 let mut texcoord = [0.; 2];
303 match float::parse_partial::<f32>(s) {
305 Some((f, n)) => {
306 texcoord[0] = f;
307 *s = &s[n..];
308 }
309 None => return Err(ErrorKind::Float(s.len())),
310 }
311 let has_space = skip_spaces(s);
312 match s.first() {
313 Some(b'\n' | b'\r') | None => {
314 texcoords.push(texcoord);
315 *s = s.get(1..).unwrap_or_default();
316 return Ok(());
317 }
318 _ if !has_space => return Err(ErrorKind::ExpectedSpace("vt", s.len())),
319 _ => {}
320 }
321 match float::parse_partial::<f32>(s) {
323 Some((f, n)) => {
324 texcoord[1] = f;
325 *s = &s[n..];
326 }
327 None => return Err(ErrorKind::Float(s.len())),
328 }
329 texcoords.push(texcoord);
330 let has_space = skip_spaces(s);
331 match s.first() {
332 Some(b'\n' | b'\r') | None => {
333 *s = s.get(1..).unwrap_or_default();
334 return Ok(());
335 }
336 _ if !has_space => return Err(ErrorKind::ExpectedSpace("vt", s.len())),
337 _ => {}
338 }
339 match float::parse_partial::<f32>(s) {
341 Some((_f, n)) => {
342 *s = &s[n..];
344 }
345 None => return Err(ErrorKind::Float(s.len())),
346 }
347 if !skip_spaces_until_line(s) {
348 return Err(ErrorKind::ExpectedNewline("vt", s.len()));
349 }
350 Ok(())
351}
352
353fn read_f(
354 s: &mut &[u8],
355 faces: &mut Vec<Face>,
356 face: &mut Vec<[u32; 3]>,
357 vertices: &[Vec3],
358 texcoords: &[Vec2],
359 normals: &[Vec3],
360) -> Result<(), ErrorKind> {
361 let mut f;
363 match memchr_naive_table(LINE, &TABLE, s) {
364 Some(n) => {
365 f = &s[..n];
366 *s = &s[n + 1..];
367 }
368 None => {
369 f = s;
370 *s = &[];
371 }
372 };
373 while !f.is_empty() {
374 let mut w;
375 let f_next = match memchr_naive_table(SPACE, &TABLE, f) {
376 Some(n) => {
377 w = &f[..n];
378 &f[n + 1..]
379 }
380 None => {
381 w = f;
382 &[]
383 }
384 };
385 let mut idx = [u32::MAX; 3];
386 let mut i;
387 match memchr_naive(b'/', w) {
388 Some(n) => {
389 i = &w[..n];
390 w = &w[n + 1..];
391 }
392 None => {
393 i = w;
394 w = &[];
395 }
396 };
397 match int::parse::<i32>(i) {
398 #[allow(
399 clippy::cast_possible_truncation,
400 clippy::cast_possible_wrap,
401 clippy::cast_sign_loss
402 )]
403 Some(i) => {
404 idx[0] = if i < 0 {
405 (vertices.len() as isize + i as isize) as u32
406 } else {
407 (i - 1) as u32
408 }
409 }
410 None => return Err(ErrorKind::Int(s.len() + !s.is_empty() as usize + f.len())),
411 }
412 match memchr_naive(b'/', w) {
413 Some(n) => {
414 i = &w[..n];
415 w = &w[n + 1..];
416 }
417 None => {
418 i = w;
419 w = &[];
420 }
421 };
422 if !i.is_empty() {
423 match int::parse::<i32>(i) {
424 #[allow(
425 clippy::cast_possible_truncation,
426 clippy::cast_possible_wrap,
427 clippy::cast_sign_loss
428 )]
429 Some(i) => {
430 idx[1] = if i < 0 {
431 (texcoords.len() as isize + i as isize) as u32
432 } else {
433 (i - 1) as u32
434 }
435 }
436 None => return Err(ErrorKind::Int(s.len() + !s.is_empty() as usize + f.len())),
437 }
438 }
439 i = w;
440 if !i.is_empty() {
441 match int::parse::<i32>(i) {
442 #[allow(
443 clippy::cast_possible_truncation,
444 clippy::cast_possible_wrap,
445 clippy::cast_sign_loss
446 )]
447 Some(i) => {
448 idx[2] = if i < 0 {
449 (normals.len() as isize + i as isize) as u32
450 } else {
451 (i - 1) as u32
452 }
453 }
454 None => return Err(ErrorKind::Int(s.len() + !s.is_empty() as usize + f.len())),
455 }
456 }
457 f = f_next;
458 skip_spaces(&mut f);
459 face.push(idx);
460 }
461 match face.len() {
462 1 => {
463 faces.push(Face::Point([face[0]]));
464 face.clear();
465 }
466 2 => {
467 faces.push(Face::Line([face[0], face[1]]));
468 face.clear();
469 }
470 3 => {
471 faces.push(Face::Triangle([face[0], face[1], face[2]]));
472 face.clear();
473 }
474 0 => return Err(ErrorKind::Expected("f", s.len())),
475 _ => faces.push(Face::Polygon(mem::take(face))),
477 }
478 Ok(())
479}
480
481fn read_float3(s: &mut &[u8], expected: &'static str) -> Result<[f32; 3], ErrorKind> {
482 let mut floats = [0.; 3];
483 match float::parse_partial::<f32>(s) {
484 Some((f, n)) => {
485 floats[0] = f;
486 *s = &s[n..];
487 }
488 None => return Err(ErrorKind::Float(s.len())),
489 }
490 if !skip_spaces(s) {
491 return Err(ErrorKind::ExpectedSpace(expected, s.len()));
492 }
493 match float::parse_partial::<f32>(s) {
494 Some((f, n)) => {
495 floats[1] = f;
496 *s = &s[n..];
497 }
498 None => return Err(ErrorKind::Float(s.len())),
499 }
500 if !skip_spaces(s) {
501 return Err(ErrorKind::ExpectedSpace(expected, s.len()));
502 }
503 match float::parse_partial::<f32>(s) {
504 Some((f, n)) => {
505 floats[2] = f;
506 *s = &s[n..];
507 }
508 None => return Err(ErrorKind::Float(s.len())),
509 }
510 Ok(floats)
511}
512
513fn read_color(s: &mut &[u8], expected: &'static str) -> Result<[f32; 3], ErrorKind> {
514 let mut floats = [0.; 3];
515 match float::parse_partial::<f32>(s) {
517 Some((f, n)) => {
518 floats[0] = f;
519 *s = &s[n..];
520 }
521 None => return Err(ErrorKind::Float(s.len())),
522 }
523 let has_space = skip_spaces(s);
524 match s.first() {
525 Some(b'\n' | b'\r') | None => {
526 *s = s.get(1..).unwrap_or_default();
527 return Ok(floats);
528 }
529 _ if !has_space => return Err(ErrorKind::ExpectedSpace(expected, s.len())),
530 _ => {}
531 }
532 match float::parse_partial::<f32>(s) {
534 Some((f, n)) => {
535 floats[1] = f;
536 *s = &s[n..];
537 }
538 None => return Err(ErrorKind::Float(s.len())),
539 }
540 if !skip_spaces(s) {
541 return Err(ErrorKind::ExpectedSpace(expected, s.len()));
542 }
543 match float::parse_partial::<f32>(s) {
545 Some((f, n)) => {
546 floats[2] = f;
547 *s = &s[n..];
548 }
549 None => return Err(ErrorKind::Float(s.len())),
550 }
551 if !skip_spaces_until_line(s) {
552 return Err(ErrorKind::ExpectedNewline(expected, s.len()));
553 }
554 Ok(floats)
555}
556
557fn read_float1(s: &mut &[u8], expected: &'static str) -> Result<f32, ErrorKind> {
558 match float::parse_partial::<f32>(s) {
559 Some((f, n)) => {
560 *s = &s[n..];
561 if !skip_spaces_until_line(s) {
562 return Err(ErrorKind::ExpectedNewline(expected, s.len()));
563 }
564 Ok(f)
565 }
566 None => Err(ErrorKind::Float(s.len())),
567 }
568}
569
570#[inline(always)]
571fn push_vertex(
572 mesh: &mut Mesh,
573 vert: [u32; 3],
574 vertices: &[Vec3],
575 colors: &[Vec3],
576 texcoords: &[Vec2],
577 normals: &[Vec3],
578) -> Result<(), ErrorKind> {
579 let v = vert[0] as usize;
580 mesh.vertices
581 .push(*vertices.get(v).ok_or(ErrorKind::Oob(v, 0))?);
582 if !texcoords.is_empty() && vert[1] != u32::MAX {
583 let vt = vert[1] as usize;
584 mesh.texcoords[0].push(*texcoords.get(vt).ok_or(ErrorKind::Oob(vt, 0))?);
585 }
586 if !normals.is_empty() && vert[2] != u32::MAX {
587 let vn = vert[2] as usize;
588 mesh.normals
589 .push(*normals.get(vn).ok_or(ErrorKind::Oob(vn, 0))?);
590 }
591 if !colors.is_empty() {
592 let rgb = colors.get(v).ok_or(ErrorKind::Oob(v, 0))?;
593 mesh.colors[0].push([rgb[0], rgb[1], rgb[2], 1.]);
595 }
596 Ok(())
597}
598
599fn push_mesh(
600 meshes: &mut Vec<Mesh>,
601 faces: &mut Vec<Face>,
602 vertices: &[Vec3],
603 texcoords: &[Vec2],
604 normals: &[Vec3],
605 colors: &[Vec3],
606 current_group: &[u8],
607 material_index: Option<u32>,
608) -> Result<(), ErrorKind> {
609 if !faces.is_empty() {
610 let mut mesh = Mesh {
611 name: from_utf8_lossy(current_group).into_owned(),
612 material_index: material_index.unwrap_or(u32::MAX),
613 ..Default::default()
614 };
615 for face in &*faces {
628 match face {
629 Face::Point(_) | Face::Line(_) => {} Face::Triangle(face) => {
631 #[allow(clippy::cast_possible_truncation)]
632 let vertices_indices = [
633 mesh.vertices.len() as u32,
634 (mesh.vertices.len() + 1) as u32,
635 (mesh.vertices.len() + 2) as u32,
636 ];
637 push_vertex(&mut mesh, face[0], vertices, colors, texcoords, normals)?;
638 push_vertex(&mut mesh, face[1], vertices, colors, texcoords, normals)?;
639 push_vertex(&mut mesh, face[2], vertices, colors, texcoords, normals)?;
640 mesh.faces.push(vertices_indices);
641 }
642 Face::Polygon(face) => {
643 let a = face[0];
644 let mut b = face[1];
645 for &c in &face[2..] {
646 #[allow(clippy::cast_possible_truncation)]
647 let vertices_indices = [
648 mesh.vertices.len() as u32,
649 (mesh.vertices.len() + 1) as u32,
650 (mesh.vertices.len() + 2) as u32,
651 ];
652 push_vertex(&mut mesh, a, vertices, colors, texcoords, normals)?;
653 push_vertex(&mut mesh, b, vertices, colors, texcoords, normals)?;
654 push_vertex(&mut mesh, c, vertices, colors, texcoords, normals)?;
655 mesh.faces.push(vertices_indices);
656 b = c;
657 }
658 }
659 }
660 }
661 if !mesh.colors[0].is_empty() && mesh.vertices.len() != mesh.colors[0].len() {
662 return Err(ErrorKind::InvalidFaceIndex(0));
664 }
665 if !mesh.texcoords[0].is_empty() && mesh.vertices.len() != mesh.texcoords[0].len() {
666 return Err(ErrorKind::InvalidFaceIndex(0));
667 }
668 if !mesh.normals.is_empty() && mesh.vertices.len() != mesh.normals.len() {
669 return Err(ErrorKind::InvalidFaceIndex(0));
670 }
671 meshes.push(mesh);
672 faces.clear();
673 }
674 Ok(())
675}
676
677#[doc(hidden)]
682#[allow(clippy::implicit_hasher)] pub fn read_mtl(
684 bytes: &[u8],
685 path: Option<&Path>,
686 materials: &mut Vec<common::Material>,
687 material_map: &mut HashMap<Vec<u8>, u32>,
688) -> io::Result<()> {
689 let bytes = &decode_bytes(bytes)?;
690 match read_mtl_internal(bytes, path.and_then(Path::parent), materials, material_map) {
691 Ok(()) => Ok(()),
692 Err(e) => Err(e.into_io_error(bytes, path)),
693 }
694}
695
696fn read_mtl_internal(
697 mut s: &[u8],
698 mtl_dir: Option<&Path>,
699 materials: &mut Vec<common::Material>,
700 material_map: &mut HashMap<Vec<u8>, u32>,
701) -> Result<(), ErrorKind> {
702 let mut mat: Option<Material<'_>> = None;
703 let mut current_name: &[u8] = b"";
704
705 while let Some((&c, s_next)) = s.split_first() {
706 match c {
707 b'K' | b'k' => {
708 s = s_next;
709 match s.first() {
710 Some(b'a') => {
711 s = &s[1..];
712 if skip_spaces(&mut s) {
713 let color = read_color(&mut s, "Ka")?;
714 if let Some(mat) = &mut mat {
715 mat.ambient = Some(color);
716 }
717 continue;
718 }
719 }
720 Some(b'd') => {
721 s = &s[1..];
722 if skip_spaces(&mut s) {
723 let color = read_color(&mut s, "Kd")?;
724 if let Some(mat) = &mut mat {
725 mat.diffuse = Some(color);
726 }
727 continue;
728 }
729 }
730 Some(b's') => {
731 s = &s[1..];
732 if skip_spaces(&mut s) {
733 let color = read_color(&mut s, "Ks")?;
734 if let Some(mat) = &mut mat {
735 mat.specular = Some(color);
736 }
737 continue;
738 }
739 }
740 Some(b'e') => {
741 s = &s[1..];
742 if skip_spaces(&mut s) {
743 let color = read_color(&mut s, "Ke")?;
744 if let Some(mat) = &mut mat {
745 mat.emissive = Some(color);
746 }
747 continue;
748 }
749 }
750 _ => {}
751 }
752 }
753 b'T' => {
754 s = s_next;
755 match s.first() {
756 Some(b'f') => {
757 s = &s[1..];
758 if skip_spaces(&mut s) {
759 let color = read_color(&mut s, "Tf")?;
760 if let Some(mat) = &mut mat {
761 mat.transparent = Some(color);
762 }
763 continue;
764 }
765 }
766 Some(b'r') => {
767 s = &s[1..];
768 if skip_spaces(&mut s) {
769 let f = read_float1(&mut s, "Tr")?;
770 if let Some(mat) = &mut mat {
771 mat.alpha = Some(1. - f);
772 }
773 continue;
774 }
775 }
776 _ => {}
777 }
778 }
779 b'd' => {
780 match s.get(1) {
781 Some(b' ' | b'\t') => {
782 s = &s[2..];
783 skip_spaces(&mut s);
784 let f = read_float1(&mut s, "d")?;
785 if let Some(mat) = &mut mat {
786 mat.alpha = Some(f);
787 }
788 continue;
789 }
790 Some(b'i') => {
791 if read_texture(&mut s, &mut mat) {
792 continue;
794 }
795 }
796 _ => {}
797 }
798 s = s_next;
799 }
800 b'N' | b'n' => match s.get(1) {
801 Some(b's') => {
802 s = &s[2..];
803 if skip_spaces(&mut s) {
804 let f = read_float1(&mut s, "Ns")?;
805 if let Some(mat) = &mut mat {
806 mat.shininess = Some(f);
807 }
808 continue;
809 }
810 }
811 Some(b'i') => {
812 s = &s[2..];
813 if skip_spaces(&mut s) {
814 let f = read_float1(&mut s, "Ni")?;
815 if let Some(mat) = &mut mat {
816 mat.index_of_refraction = Some(f);
817 }
818 continue;
819 }
820 }
821 Some(b'e') => {
822 s = &s[2..];
823 if token(&mut s, &b"newmtl"[2..]) {
824 if skip_spaces(&mut s) {
825 let (name, s_next) = name(s);
826 if let Some(mat) = mat.replace(Material::default()) {
827 push_material(materials, material_map, mtl_dir, current_name, &mat);
828 }
829 current_name = name;
830 s = s_next;
831 continue;
832 }
833 }
834 }
835 Some(b'o') => {
836 if read_texture(&mut s, &mut mat) {
837 continue;
839 }
840 }
841 _ => {}
842 },
843 b'P' => {
844 s = s_next;
845 match s.first() {
846 Some(b'r') => {
847 s = &s[1..];
848 if skip_spaces(&mut s) {
849 let f = read_float1(&mut s, "Pr")?;
850 if let Some(mat) = &mut mat {
851 mat.roughness = Some(f);
852 }
853 continue;
854 }
855 }
856 Some(b'm') => {
857 s = &s[1..];
858 if skip_spaces(&mut s) {
859 let f = read_float1(&mut s, "Pm")?;
860 if let Some(mat) = &mut mat {
861 mat.metallic = Some(f);
862 }
863 continue;
864 }
865 }
866 Some(b's') => {
867 s = &s[1..];
868 if skip_spaces(&mut s) {
869 let color = read_color(&mut s, "Ps")?;
870 if let Some(mat) = &mut mat {
871 mat.sheen = Some(color);
872 }
873 continue;
874 }
875 }
876 Some(b'c') => {
877 s = &s[1..];
878 if s.first() == Some(&b'r') {
879 if skip_spaces(&mut s) {
880 let f = read_float1(&mut s, "Pcr")?;
881 if let Some(mat) = &mut mat {
882 mat.clearcoat_roughness = Some(f);
883 }
884 continue;
885 }
886 } else if skip_spaces(&mut s) {
887 let f = read_float1(&mut s, "Pc")?;
888 if let Some(mat) = &mut mat {
889 mat.clearcoat_thickness = Some(f);
890 }
891 continue;
892 }
893 }
894 _ => {}
895 }
896 }
897 b'm' | b'b' | b'r' => {
898 if read_texture(&mut s, &mut mat) {
899 continue;
900 }
901 }
902 b'i' => {
903 s = s_next;
904 if token(&mut s, &b"illum"[1..]) {
905 if skip_spaces(&mut s) {
906 match int::parse_partial::<u8>(s) {
907 Some((i, n)) => {
908 s = &s[n..];
909 if !skip_spaces_until_line(&mut s) {
910 return Err(ErrorKind::ExpectedNewline("illum", s.len()));
911 }
912 if let Some(mat) = &mut mat {
913 mat.illumination_model = Some(i);
914 }
915 }
916 None => return Err(ErrorKind::Int(s.len())),
917 }
918 continue;
919 }
920 }
921 }
922 b'a' => {
923 s = s_next;
924 if skip_spaces(&mut s) {
925 let f = read_float1(&mut s, "a")?;
926 if let Some(mat) = &mut mat {
927 mat.anisotropy = Some(f);
928 }
929 continue;
930 }
931 }
932 _ => {}
933 }
934 skip_any_until_line(&mut s);
936 }
937
938 if let Some(mat) = &mat {
939 push_material(materials, material_map, mtl_dir, current_name, mat);
940 }
941
942 Ok(())
943}
944
945fn read_texture<'a>(s: &mut &'a [u8], mat: &mut Option<Material<'a>>) -> bool {
946 if token(s, b"map_Kd") {
949 if skip_spaces(s) {
950 let (name, s_next) = name(s);
951 if let Some(mat) = mat {
952 mat.diffuse_texture = Some(name);
953 }
954 *s = s_next;
955 return true;
956 }
957 } else if token(s, b"map_Ka") {
958 if skip_spaces(s) {
959 let (name, s_next) = name(s);
960 if let Some(mat) = mat {
961 mat.ambient_texture = Some(name);
962 }
963 *s = s_next;
964 return true;
965 }
966 } else if token(s, b"map_Ks") {
967 if skip_spaces(s) {
968 let (name, s_next) = name(s);
969 if let Some(mat) = mat {
970 mat.specular_texture = Some(name);
971 }
972 *s = s_next;
973 return true;
974 }
975 } else if token(s, b"map_disp") || token(s, b"disp") {
976 if skip_spaces(s) {
977 let (name, s_next) = name(s);
978 if let Some(mat) = mat {
979 mat.displacement_texture = Some(name);
980 }
981 *s = s_next;
982 return true;
983 }
984 } else if token(s, b"map_d") {
985 if skip_spaces(s) {
986 let (name, s_next) = name(s);
987 if let Some(mat) = mat {
988 mat.opacity_texture = Some(name);
989 }
990 *s = s_next;
991 return true;
992 }
993 } else if token(s, b"map_emissive") || token(s, b"map_Ke") {
994 if skip_spaces(s) {
995 let (name, s_next) = name(s);
996 if let Some(mat) = mat {
997 mat.emissive_texture = Some(name);
998 }
999 *s = s_next;
1000 return true;
1001 }
1002 } else if token(s, b"map_Bump") || token(s, b"map_bump") || token(s, b"bump") {
1003 if skip_spaces(s) {
1004 let (name, s_next) = name(s);
1005 if let Some(mat) = mat {
1006 mat.bump_texture = Some(name);
1007 }
1008 *s = s_next;
1009 return true;
1010 }
1011 } else if token(s, b"map_Kn") || token(s, b"norm") {
1012 if skip_spaces(s) {
1013 let (name, s_next) = name(s);
1014 if let Some(mat) = mat {
1015 mat.normal_texture = Some(name);
1016 }
1017 *s = s_next;
1018 return true;
1019 }
1020 } else if token(s, b"refl") {
1021 if skip_spaces(s) {
1022 let (_name, s_next) = name(s);
1023 *s = s_next;
1025 return true;
1026 }
1027 } else if token(s, b"map_Ns") || token(s, b"map_ns") || token(s, b"map_NS") {
1028 if skip_spaces(s) {
1029 let (name, s_next) = name(s);
1030 if let Some(mat) = mat {
1031 mat.specularity_texture = Some(name);
1032 }
1033 *s = s_next;
1034 return true;
1035 }
1036 } else if token(s, b"map_Pr") {
1037 if skip_spaces(s) {
1038 let (name, s_next) = name(s);
1039 if let Some(mat) = mat {
1040 mat.roughness_texture = Some(name);
1041 }
1042 *s = s_next;
1043 return true;
1044 }
1045 } else if token(s, b"map_Pm") {
1046 if skip_spaces(s) {
1047 let (name, s_next) = name(s);
1048 if let Some(mat) = mat {
1049 mat.metallic_texture = Some(name);
1050 }
1051 *s = s_next;
1052 return true;
1053 }
1054 } else if token(s, b"map_Ps") {
1055 if skip_spaces(s) {
1056 let (name, s_next) = name(s);
1057 if let Some(mat) = mat {
1058 mat.sheen_texture = Some(name);
1059 }
1060 *s = s_next;
1061 return true;
1062 }
1063 }
1064 false
1065}
1066
1067fn push_material(
1068 materials: &mut Vec<common::Material>,
1069 material_map: &mut HashMap<Vec<u8>, u32>,
1070 mtl_dir: Option<&Path>,
1071 current_name: &[u8],
1072 mat: &Material<'_>,
1073) {
1074 fn color4(color3: Option<[f32; 3]>) -> Option<Color4> {
1075 let rgb = color3?;
1076 Some([rgb[0], rgb[1], rgb[2], 1.])
1078 }
1079 fn texture_path(texture: Option<&[u8]>, mtl_dir: Option<&Path>) -> Option<PathBuf> {
1080 let mut p = texture?;
1081 if p.is_empty() {
1082 return None;
1083 }
1084 match mtl_dir {
1085 Some(mtl_dir) => {
1086 let tmp: Vec<_>;
1087 if p.contains(&b'\\') {
1088 tmp = p
1089 .iter()
1090 .map(|&b| if b == b'\\' { b'/' } else { b })
1091 .collect();
1092 p = &*tmp;
1093 }
1094 if p.starts_with(b"/..") {
1095 p = p.strip_prefix(b"/").unwrap_or(p);
1096 }
1097 p = p.strip_prefix(b"./").unwrap_or(p);
1098 let p = path_from_bytes(p).ok()?;
1099 let p = mtl_dir.join(p);
1100 if p.to_str().map_or(false, |s| {
1101 s.starts_with("https://") || p.starts_with("http://")
1102 }) || p.exists()
1103 {
1104 Some(p)
1105 } else {
1106 None
1107 }
1108 }
1109 None => {
1110 let p = path_from_bytes(p).ok()?.to_owned();
1111 Some(p)
1112 }
1113 }
1114 }
1115 #[allow(clippy::cast_possible_truncation)]
1116 let material_index = materials.len() as u32;
1117 materials.push(common::Material {
1118 name: from_utf8_lossy(current_name).into_owned(),
1119 shading_model: match mat.illumination_model {
1121 Some(0) => Some(ShadingModel::NoShading),
1122 Some(1) => Some(ShadingModel::Gouraud),
1123 Some(2) => Some(ShadingModel::Phong),
1124 _ => None,
1125 },
1126 shininess: mat.shininess,
1127 opacity: mat.alpha,
1128 reflectivity: None,
1129 index_of_refraction: mat.index_of_refraction,
1130 color: common::Colors {
1137 ambient: color4(mat.ambient),
1138 diffuse: color4(mat.diffuse),
1139 specular: color4(mat.specular),
1140 emissive: color4(mat.emissive),
1141 transparent: color4(mat.transparent),
1142 reflective: None,
1143 },
1144 texture: common::Textures {
1145 diffuse: texture_path(mat.diffuse_texture, mtl_dir),
1146 ambient: texture_path(mat.ambient_texture, mtl_dir),
1147 emissive: texture_path(mat.emissive_texture, mtl_dir),
1148 specular: texture_path(mat.specular_texture, mtl_dir),
1149 height: texture_path(mat.bump_texture, mtl_dir),
1150 normal: texture_path(mat.normal_texture, mtl_dir),
1151 reflection: None, displacement: texture_path(mat.displacement_texture, mtl_dir),
1153 opacity: texture_path(mat.opacity_texture, mtl_dir),
1154 shininess: texture_path(mat.specularity_texture, mtl_dir),
1155 lightmap: None,
1156 },
1157 });
1158 material_map.insert(current_name.to_owned(), material_index);
1159}
1160
1161enum Face {
1165 Point(#[allow(dead_code)] [[u32; 3]; 1]),
1166 Line(#[allow(dead_code)] [[u32; 3]; 2]),
1167 Triangle([[u32; 3]; 3]),
1168 Polygon(Vec<[u32; 3]>),
1169}
1170
1171#[derive(Default)]
1172struct Material<'a> {
1173 diffuse_texture: Option<&'a [u8]>,
1175 specular_texture: Option<&'a [u8]>,
1176 ambient_texture: Option<&'a [u8]>,
1177 emissive_texture: Option<&'a [u8]>,
1178 bump_texture: Option<&'a [u8]>,
1179 normal_texture: Option<&'a [u8]>,
1180 specularity_texture: Option<&'a [u8]>,
1182 opacity_texture: Option<&'a [u8]>,
1183 displacement_texture: Option<&'a [u8]>,
1184 roughness_texture: Option<&'a [u8]>,
1185 metallic_texture: Option<&'a [u8]>,
1186 sheen_texture: Option<&'a [u8]>,
1187 ambient: Option<[f32; 3]>,
1191 diffuse: Option<[f32; 3]>,
1192 specular: Option<[f32; 3]>,
1193 emissive: Option<[f32; 3]>,
1194 alpha: Option<f32>,
1195 shininess: Option<f32>,
1196 illumination_model: Option<u8>,
1197 index_of_refraction: Option<f32>,
1198 transparent: Option<[f32; 3]>,
1199
1200 roughness: Option<f32>,
1201 metallic: Option<f32>,
1202 sheen: Option<[f32; 3]>,
1203 clearcoat_thickness: Option<f32>,
1204 clearcoat_roughness: Option<f32>,
1205 anisotropy: Option<f32>,
1206 }
1208
1209const LINE: u8 = 1 << 0;
1211const SPACE: u8 = 1 << 1;
1213const WHITESPACE: u8 = 1 << 2;
1215
1216static TABLE: [u8; 256] = {
1217 const __: u8 = 0;
1218 const LN: u8 = WHITESPACE | LINE;
1219 const NL: u8 = WHITESPACE | SPACE;
1220 [
1221 __, __, __, __, __, __, __, __, __, NL, LN, __, __, LN, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, NL, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, ]
1239};
1240#[test]
1241fn table() {
1242 for b in u8::MIN..=u8::MAX {
1243 match b {
1244 b' ' | b'\t' => {
1245 assert_eq!(
1246 TABLE[b as usize],
1247 WHITESPACE | SPACE,
1248 "{:?}({b:#X})",
1249 b as char
1250 );
1251 }
1252 b'\n' | b'\r' => {
1253 assert_eq!(
1254 TABLE[b as usize],
1255 WHITESPACE | LINE,
1256 "{:?}({b:#X})",
1257 b as char
1258 );
1259 }
1260 _ => assert_eq!(TABLE[b as usize], 0, "{:?}({b:#X})", b as char),
1261 }
1262 }
1263}
1264
1265#[inline]
1266fn skip_whitespace_until_byte_or_eof(s: &mut &[u8], byte_mask: u8, whitespace_mask: u8) -> bool {
1267 while let Some((&b, s_next)) = s.split_first() {
1268 let t = TABLE[b as usize];
1269 if t & byte_mask != 0 {
1270 *s = s_next;
1271 break;
1272 }
1273 if t & whitespace_mask != 0 {
1274 *s = s_next;
1275 continue;
1276 }
1277 if b == b'\\' && matches!(s_next.first(), Some(b'\n' | b'\r')) {
1278 if s_next.starts_with(b"\r\n") {
1279 *s = &s_next[2..];
1280 } else {
1281 *s = &s_next[1..];
1282 }
1283 continue;
1284 }
1285 return false;
1286 }
1287 true
1288}
1289
1290#[inline]
1291fn skip_spaces_until_line(s: &mut &[u8]) -> bool {
1292 skip_whitespace_until_byte_or_eof(s, LINE, SPACE)
1293}
1294
1295#[inline]
1298fn skip_spaces(s: &mut &[u8]) -> bool {
1299 let start = *s;
1300 while let Some((&b, s_next)) = s.split_first() {
1301 if TABLE[b as usize] & SPACE != 0 {
1302 *s = s_next;
1303 continue;
1304 }
1305 if b == b'\\' && matches!(s_next.first(), Some(b'\n' | b'\r')) {
1306 if s_next.starts_with(b"\r\n") {
1307 *s = &s_next[2..];
1308 } else {
1309 *s = &s_next[1..];
1310 }
1311 continue;
1312 }
1313 break;
1314 }
1315 start.len() != s.len()
1316}
1317
1318#[inline]
1320fn skip_any_until_line(s: &mut &[u8]) {
1321 while let Some((&b, s_next)) = s.split_first() {
1322 if TABLE[b as usize] & LINE != 0 {
1323 *s = s_next;
1324 break;
1325 }
1326 if b == b'\\' && matches!(s_next.first(), Some(b'\n' | b'\r')) {
1327 if s_next.starts_with(b"\r\n") {
1328 *s = &s_next[2..];
1329 } else {
1330 *s = &s_next[1..];
1331 }
1332 continue;
1333 }
1334 *s = s_next;
1335 continue;
1336 }
1337}
1338
1339#[inline]
1340fn token(s: &mut &[u8], token: &'static [u8]) -> bool {
1341 if starts_with(s, token) {
1342 *s = &s[token.len()..];
1343 true
1344 } else {
1345 false
1346 }
1347}
1348
1349fn name(mut s: &[u8]) -> (&[u8], &[u8]) {
1350 let start = s;
1351 skip_any_until_line(&mut s);
1352 let mut name = &start[..start.len() - s.len()];
1353 while let Some((&b, name_next)) = name.split_last() {
1356 if TABLE[b as usize] & WHITESPACE != 0 {
1357 name = name_next;
1358 continue;
1359 }
1360 break;
1361 }
1362 (name, s)
1363}