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