cranpose_ui_graphics/space/
obj.rs1use std::fmt;
2
3use super::mesh::{Mesh, MeshError, smooth_normals};
4use crate::FxHashMap;
5
6#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct ObjError {
9 pub line: usize,
12 pub kind: ObjErrorKind,
14}
15
16#[derive(Clone, Debug, PartialEq, Eq)]
18pub enum ObjErrorKind {
19 BadNumber,
21 TooFewValues,
23 IndexOutOfRange,
26 Mesh(MeshError),
28}
29
30impl fmt::Display for ObjError {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let what = match &self.kind {
33 ObjErrorKind::BadNumber => "a number does not parse",
34 ObjErrorKind::TooFewValues => "too few values",
35 ObjErrorKind::IndexOutOfRange => "a face names a vertex that is not given before it",
36 ObjErrorKind::Mesh(error) => return write!(f, "OBJ file: {error}"),
37 };
38 write!(f, "OBJ line {}: {what}", self.line)
39 }
40}
41
42impl std::error::Error for ObjError {}
43
44#[derive(Default)]
45struct Builder {
46 positions: Vec<[f32; 3]>,
47 uvs: Vec<[f32; 2]>,
48 normals: Vec<[f32; 3]>,
49 corners: FxHashMap<(usize, Option<usize>, Option<usize>), u32>,
50 out_positions: Vec<[f32; 3]>,
51 out_uvs: Vec<[f32; 2]>,
52 out_normals: Vec<Option<[f32; 3]>>,
53 indices: Vec<u32>,
54}
55
56#[expect(clippy::multiple_inherent_impl, reason = "constructor by its parser")]
57impl Mesh {
58 pub fn from_obj(source: &str) -> Result<Self, ObjError> {
68 let mut builder = Builder::default();
69 for (number, line) in source.lines().enumerate() {
70 let line = line.split('#').next().unwrap_or_default();
71 let mut words = line.split_whitespace();
72 let fail = |kind| ObjError {
73 line: number + 1,
74 kind,
75 };
76 match words.next() {
77 Some("v") => {
78 let [x, y, z] = numbers(&mut words).map_err(fail)?;
79 builder.positions.push([x, y, z]);
80 }
81 Some("vt") => {
82 let u = number_of(words.next()).map_err(fail)?;
83 let v = words
84 .next()
85 .map_or(Ok(0.0), |word| number_of(Some(word)))
86 .map_err(fail)?;
87 builder.uvs.push([u, 1.0 - v]);
88 }
89 Some("vn") => {
90 let [x, y, z] = numbers(&mut words).map_err(fail)?;
91 builder.normals.push([x, y, z]);
92 }
93 Some("f") => builder.face(words).map_err(fail)?,
94 _ => {}
95 }
96 }
97 builder.finish()
98 }
99}
100
101impl Builder {
102 fn face<'a>(&mut self, words: impl Iterator<Item = &'a str>) -> Result<(), ObjErrorKind> {
104 let mut corners = 0usize;
105 let (mut first, mut previous) = (0, 0);
106 for word in words {
107 let corner = self.corner(word)?;
108 match corners {
109 0 => first = corner,
110 1 => {}
111 _ => self.indices.extend([first, previous, corner]),
112 }
113 previous = corner;
114 corners += 1;
115 }
116 if corners < 3 {
117 return Err(ObjErrorKind::TooFewValues);
118 }
119 Ok(())
120 }
121
122 fn corner(&mut self, word: &str) -> Result<u32, ObjErrorKind> {
123 let mut parts = word.split('/');
124 let position =
125 resolve(parts.next(), self.positions.len())?.ok_or(ObjErrorKind::TooFewValues)?;
126 let uv = resolve(parts.next(), self.uvs.len())?;
127 let normal = resolve(parts.next(), self.normals.len())?;
128 if let Some(&index) = self.corners.get(&(position, uv, normal)) {
129 return Ok(index);
130 }
131 let index = self.out_positions.len() as u32;
132 self.out_positions.push(self.positions[position]);
133 self.out_uvs.push(uv.map_or([0.0, 0.0], |uv| self.uvs[uv]));
134 self.out_normals
135 .push(normal.map(|normal| self.normals[normal]));
136 self.corners.insert((position, uv, normal), index);
137 Ok(index)
138 }
139
140 fn finish(self) -> Result<Mesh, ObjError> {
141 let fail = |error| ObjError {
142 line: 0,
143 kind: ObjErrorKind::Mesh(error),
144 };
145 if self.indices.is_empty() {
146 return Err(fail(MeshError::Empty));
147 }
148 let smooth = self
149 .out_normals
150 .iter()
151 .any(Option::is_none)
152 .then(|| smooth_normals(&self.out_positions, &self.indices));
153 let normals = self
154 .out_normals
155 .iter()
156 .enumerate()
157 .map(|(index, normal)| match (normal, &smooth) {
158 (Some(normal), _) => unit(*normal),
159 (None, Some(smooth)) => smooth[index],
160 (None, None) => [0.0, 0.0, -1.0],
161 })
162 .collect();
163 Mesh::new(
164 self.out_positions,
165 self.out_uvs,
166 Some(normals),
167 self.indices,
168 )
169 .map_err(fail)
170 }
171}
172
173fn unit(normal: [f32; 3]) -> [f32; 3] {
174 super::math::Vec3::from_array(normal)
175 .normalized()
176 .map_or([0.0, 0.0, -1.0], super::math::Vec3::to_array)
177}
178
179fn resolve(part: Option<&str>, count: usize) -> Result<Option<usize>, ObjErrorKind> {
183 let Some(part) = part.filter(|part| !part.is_empty()) else {
184 return Ok(None);
185 };
186 let index: i64 = part.parse().map_err(|_| ObjErrorKind::BadNumber)?;
187 let resolved = match index {
188 1.. => index - 1,
189 ..0 => count as i64 + index,
190 0 => return Err(ObjErrorKind::IndexOutOfRange),
191 };
192 usize::try_from(resolved)
193 .ok()
194 .filter(|&resolved| resolved < count)
195 .map(Some)
196 .ok_or(ObjErrorKind::IndexOutOfRange)
197}
198
199fn number_of(word: Option<&str>) -> Result<f32, ObjErrorKind> {
200 word.ok_or(ObjErrorKind::TooFewValues)?
201 .parse()
202 .map_err(|_| ObjErrorKind::BadNumber)
203}
204
205fn numbers<'a>(words: &mut impl Iterator<Item = &'a str>) -> Result<[f32; 3], ObjErrorKind> {
206 Ok([
207 number_of(words.next())?,
208 number_of(words.next())?,
209 number_of(words.next())?,
210 ])
211}