1#![doc = include_str!("../README.md")]
2
3use std::fmt::{Debug, Display};
4use std::fs::File;
5use std::io::Write;
6use std::io::{BufRead, BufReader};
7use std::ops::AddAssign;
8
9use linear_isomorphic::{RefIterable, VectorSpace};
10use nalgebra;
11
12type Vec2 = nalgebra::Vector2<f32>;
13type Vec3 = nalgebra::Vector3<f32>;
14
15#[derive(Debug)]
16pub struct ObjData
17{
18 pub vertices: Vec<Vec3>,
19 pub point_colors: Vec<Vec3>,
20 pub normals: Vec<Vec3>,
21 pub uvs: Vec<Vec2>,
22 pub vertex_face_indices: Vec<Vec<u64>>,
23 pub normal_face_indices: Vec<Vec<u64>>,
24 pub uv_face_indices: Vec<Vec<u64>>,
25 pub lines: Vec<[u64; 2]>,
26 pub objects: Vec<ObjectRanges>,
27}
28
29#[derive(Debug)]
30pub struct ObjectRanges
31{
32 pub name: String,
33 pub positions: (u64, u64),
34 pub normals: (u64, u64),
35 pub uvs: (u64, u64),
36 pub lines: (u64, u64),
37 pub polygons: [(u64, u64); 3],
38}
39
40impl ObjData
41{
42 pub fn from_disk_file(path: &str) -> Self
43 {
44 let file = File::open(path).expect(&format!("Cannot find {}.", path));
45 let reader = BufReader::new(file);
46
47 let mut vert_topology = Vec::<Vec<u64>>::new();
48 let mut normal_topology = Vec::<Vec<u64>>::new();
49 let mut uv_topology = Vec::<Vec<u64>>::new();
50 let mut lines = Vec::<[u64; 2]>::new();
51
52 let mut vertices = Vec::<Vec3>::new();
53 let mut colors = Vec::<Vec3>::new();
54 let mut normals = Vec::<Vec3>::new();
55 let mut uvs = Vec::<Vec2>::new();
56 let mut object_ranges = Vec::<ObjectRanges>::new();
57 for line in reader.lines()
58 {
59 let line_str = line.unwrap();
60 let line_str = line_str.trim();
61 let tokens = line_str.split_whitespace().collect::<Vec<&str>>();
62 if tokens.is_empty()
63 {
64 continue;
65 }
66 match tokens[0]
67 {
68 "o" =>
69 {
70 object_ranges.push(ObjectRanges {
71 name: tokens[1..].join(" "),
72 positions: (vertices.len() as u64, 0),
73 normals: (normals.len() as u64, 0),
74 uvs: (uvs.len() as u64, 0),
75 lines: (lines.len() as u64, 0),
76 polygons: [
77 (vert_topology.len() as u64, 0),
78 (normal_topology.len() as u64, 0),
79 (uv_topology.len() as u64, 0),
80 ],
81 });
82 }
83 "v" =>
84 {
85 add_vec_3d(&tokens[1..], &mut vertices);
86 if tokens[1..].len() == 6
87 {
88 add_vec_3d(&tokens[4..], &mut colors);
89 }
90 }
91 "vn" =>
92 {
93 add_vec_3d(&tokens[1..], &mut normals);
94 }
95 "vt" =>
96 {
97 add_vec_2d(&tokens[1..], &mut uvs);
98 }
99 "l" =>
100 {
101 add_line(&tokens[1..], &mut lines);
102 }
103 "f" => add_face(
104 &tokens[1..],
105 &mut vert_topology,
106 &mut normal_topology,
107 &mut uv_topology,
108 vertices.len(),
109 uvs.len(),
110 normals.len(),
111 ),
112 _ => continue,
113 }
114
115 if let Some(or) = object_ranges.last_mut()
116 {
117 or.positions.1 = vertices.len() as u64;
118 or.normals.1 = normals.len() as u64;
119 or.uvs.1 = uvs.len() as u64;
120 or.lines.1 = lines.len() as u64;
121
122 or.polygons[0].1 = vert_topology.len() as u64;
123 or.polygons[1].1 = normal_topology.len() as u64;
124 or.polygons[2].1 = uv_topology.len() as u64;
125 }
126 }
127
128 ObjData {
129 vertices,
130 point_colors: colors,
131 normals,
132 uvs,
133 vertex_face_indices: vert_topology,
134 normal_face_indices: normal_topology,
135 uv_face_indices: uv_topology,
136 lines,
137 objects: object_ranges,
138 }
139 }
140
141 pub fn export<'a, T>(mesh: &'a T, path: &str)
142 where
143 T: WaveFrontCompatible<'a>,
144 {
145 std::fs::create_dir_all(std::path::Path::new(path).parent().unwrap()).unwrap();
146 let mut file = File::create(path).unwrap();
147
148 let one = 1;
149 if mesh.point_color_iterator().count() == 0
150 {
151 for point in mesh.pos_iterator()
152 {
153 writeln!(file, "v {} {} {}", point[0], point[1], point[2]).unwrap();
154 }
155 }
156 else
157 {
158 for (point, color) in mesh.pos_iterator().zip(mesh.point_color_iterator())
159 {
160 writeln!(
161 file,
162 "v {} {} {} {} {} {}",
163 point[0], point[1], point[2], color[0], color[1], color[2]
164 )
165 .unwrap();
166 }
167 }
168
169 for norm in mesh.norm_iterator()
170 {
171 writeln!(file, "vn {} {} {}", norm[0], norm[1], norm[2]).unwrap();
172 }
173
174 for uv in mesh.uv_iterator()
175 {
176 writeln!(file, "vt {} {}", uv[0], uv[1]).unwrap();
177 }
178
179 for line in mesh.segment_iterator()
180 {
181 writeln!(file, "l {} {}", line[0] + one, line[1] + one).unwrap();
182 }
183
184 let mut indices = Vec::new();
185 let mut vert_face_count = 0;
186 for face in mesh.pos_index_iterator()
187 {
188 vert_face_count += 1;
189
190 indices.push(Vec::new());
191 let face_ids: &mut Vec<_> = indices.last_mut().unwrap();
192
193 for pos_id in face
194 {
195 face_ids.push([Some(pos_id), None, None]);
196 }
197 }
198
199 let mut uv_face_count = 0;
200 for face in mesh.uv_index_iterator()
201 {
202 for (local_id, uv_id) in face.enumerate()
203 {
204 indices[uv_face_count][local_id][1] = Some(uv_id);
205 }
206 uv_face_count += 1;
207 }
208
209 debug_assert!(uv_face_count == 0 || uv_face_count == vert_face_count);
210
211 let mut norm_face_count = 0;
212 for face in mesh.norm_index_iterator()
213 {
214 for (local_id, norm_id) in face.enumerate()
215 {
216 indices[norm_face_count][local_id][2] = Some(norm_id);
217 }
218 norm_face_count += 1;
219 }
220
221 debug_assert!(norm_face_count == 0 || norm_face_count == vert_face_count);
222
223 for face in indices
224 {
225 let mut face_data = "f ".to_string();
226
227 for [pos_id, uv_id, norm_id] in face
228 {
229 let pos_str = (pos_id.unwrap() + one).to_string();
230 let uv_str = match uv_id
231 {
232 None => "".to_string(),
233 Some(x) => (x + one).to_string(),
234 };
235 let norm_str = match norm_id
236 {
237 None => "".to_string(),
238 Some(x) => (x + one).to_string(),
239 };
240
241 face_data
242 .push_str(format!("{}/{}/{} ", pos_str, uv_str, norm_str).as_str());
243 }
244
245 face_data.push('\n');
246
247 write!(file, "{}", face_data).unwrap();
248 }
249 }
250}
251
252pub trait WaveFrontCompatible<'a>
253{
254 type Scalar: num_traits::Float + Debug + AddAssign + Display;
255
256 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
257 {
258 std::iter::empty()
259 }
260 fn point_color_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
261 {
262 std::iter::empty()
263 }
264 fn uv_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 2]>
265 {
266 std::iter::empty()
267 }
268 fn norm_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
269 {
270 std::iter::empty()
271 }
272
273 fn segment_iterator(&'a self) -> impl Iterator<Item = [usize; 2]>
274 {
275 std::iter::empty()
276 }
277 fn pos_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
278 {
279 let empty_iterator: std::iter::Empty<std::iter::Empty<usize>> =
280 std::iter::empty();
281 empty_iterator
282 }
283 fn uv_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
284 {
285 let empty_iterator: std::iter::Empty<std::iter::Empty<usize>> =
286 std::iter::empty();
287 empty_iterator
288 }
289 fn norm_index_iterator(&'a self)
290 -> impl Iterator<Item = impl Iterator<Item = usize>>
291 {
292 let empty_iterator: std::iter::Empty<std::iter::Empty<usize>> =
293 std::iter::empty();
294 empty_iterator
295 }
296}
297
298impl<'a> WaveFrontCompatible<'a> for ObjData
299{
300 type Scalar = f32;
301
302 fn pos_iterator(&'a self) -> impl Iterator<Item = [f32; 3]>
303 {
304 self.vertices.iter().map(|v| [v.x, v.y, v.z])
305 }
306
307 fn point_color_iterator(&'a self) -> impl Iterator<Item = [f32; 3]>
308 {
309 self.point_colors.iter().map(|v| [v.x, v.y, v.z])
310 }
311
312 fn uv_iterator(&'a self) -> impl Iterator<Item = [f32; 2]>
313 {
314 self.uvs.iter().map(|v| [v.x, v.y])
315 }
316
317 fn norm_iterator(&'a self) -> impl Iterator<Item = [f32; 3]>
318 {
319 self.normals.iter().map(|v| [v.x, v.y, v.z])
320 }
321
322 fn segment_iterator(&'a self) -> impl Iterator<Item = [usize; 2]>
323 {
324 self.lines.iter().map(|[i, j]| [*i as usize, *j as usize])
325 }
326
327 fn pos_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
328 {
329 self.vertex_face_indices
330 .iter()
331 .map(|l| l.iter().map(|i| *i as usize))
332 }
333
334 fn uv_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
335 {
336 self.uv_face_indices
337 .iter()
338 .map(|l| l.iter().map(|i| *i as usize))
339 }
340
341 fn norm_index_iterator(&'a self)
342 -> impl Iterator<Item = impl Iterator<Item = usize>>
343 {
344 self.normal_face_indices
345 .iter()
346 .map(|l| l.iter().map(|i| *i as usize))
347 }
348}
349
350impl<'a, V, S> WaveFrontCompatible<'a> for Vec<V>
351where
352 V: VectorSpace<Scalar = S>,
353 S: num_traits::Float + AddAssign + Display + Debug,
354{
355 type Scalar = S;
356
357 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
358 {
359 self.iter().map(|v| [v[0], v[1], v[2]])
360 }
361}
362
363impl<'a, V, S> WaveFrontCompatible<'a> for (&Vec<V>, &Vec<usize>)
364where
365 V: VectorSpace<Scalar = S>,
366 S: num_traits::Float + AddAssign + Display + Debug,
367{
368 type Scalar = S;
369
370 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
371 {
372 self.0.iter().map(|v| [v[0], v[1], v[2]])
373 }
374
375 fn pos_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
376 {
377 debug_assert!(self.1.len() % 3 == 0);
378 self.1.chunks(3).map(|chunk| chunk.iter().copied())
379 }
380}
381
382impl<'a, V, S, I> WaveFrontCompatible<'a> for (&Vec<V>, &Vec<Vec<I>>)
383where
384 V: VectorSpace<Scalar = S>,
385 S: num_traits::Float + AddAssign + Display + Debug,
386 usize: TryFrom<I>,
387 I: num_traits::PrimInt + std::fmt::Display,
388{
389 type Scalar = S;
390
391 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
392 {
393 self.0.iter().map(|v| [v[0], v[1], v[2]])
394 }
395
396 fn pos_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
397 {
398 self.1.iter().map(|face| {
399 face.iter()
400 .map(|i| usize::try_from(*i).unwrap_or(usize::MAX))
401 })
402 }
403}
404
405impl<'a, V, S, I> WaveFrontCompatible<'a> for (Vec<V>, Vec<[I; 3]>)
406where
407 V: VectorSpace<Scalar = S>,
408 S: num_traits::Float + AddAssign + Display + Debug,
409 usize: TryFrom<I>,
410 I: num_traits::PrimInt + std::fmt::Display,
411{
412 type Scalar = S;
413
414 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
415 {
416 self.0.iter().map(|v| [v[0], v[1], v[2]])
417 }
418
419 fn pos_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
420 {
421 self.1.iter().map(|face| {
422 face.iter()
423 .map(|i| usize::try_from(*i).unwrap_or(usize::MAX))
424 })
425 }
426}
427
428
429impl<'a, V, S, I> WaveFrontCompatible<'a> for (&Vec<V>, &Vec<[I; 3]>)
430where
431 V: VectorSpace<Scalar = S>,
432 S: num_traits::Float + AddAssign + Display + Debug,
433 usize: TryFrom<I>,
434 I: num_traits::PrimInt + std::fmt::Display,
435{
436 type Scalar = S;
437
438 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
439 {
440 self.0.iter().map(|v| [v[0], v[1], v[2]])
441 }
442
443 fn pos_index_iterator(&'a self) -> impl Iterator<Item = impl Iterator<Item = usize>>
444 {
445 self.1.iter().map(|face| {
446 face.iter()
447 .map(|i| usize::try_from(*i).unwrap_or(usize::MAX))
448 })
449 }
450}
451
452impl<'a, V, S, I> WaveFrontCompatible<'a> for (&Vec<V>, &Vec<[I; 2]>)
453where
454 V: VectorSpace<Scalar = S>,
455 S: num_traits::Float + AddAssign + Display + Debug,
456 usize: TryFrom<I>,
457 I: num_traits::PrimInt + Display,
458{
459 type Scalar = S;
460
461 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
462 {
463 self.0.iter().map(|v| [v[0], v[1], v[2]])
464 }
465
466 fn segment_iterator(&'a self) -> impl Iterator<Item = [usize; 2]>
467 {
468 self.1.iter().map(|[i, j]| {
469 [
470 usize::try_from(*i).unwrap_or(usize::MAX),
471 usize::try_from(*j).unwrap_or(usize::MAX),
472 ]
473 })
474 }
475}
476
477pub struct ColoredVerts<'a, V>
478{
479 pub points: &'a Vec<V>,
480 pub colors: &'a Vec<V>,
481}
482
483impl<'a, V, S> WaveFrontCompatible<'a> for ColoredVerts<'a, V>
484where
485 V: VectorSpace<Scalar = S>,
486 S: num_traits::Float + AddAssign + Display + Debug,
487{
488 type Scalar = S;
489
490 fn pos_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
491 {
492 self.points.iter().map(|v| [v[0], v[1], v[2]])
493 }
494
495 fn point_color_iterator(&'a self) -> impl Iterator<Item = [Self::Scalar; 3]>
496 {
497 self.colors.iter().map(|v| [v[0], v[1], v[2]])
498 }
499}
500
501fn add_vec_3d(tokens: &[&str], vecs: &mut Vec<Vec3>)
502{
503 let vec = Vec3::new(
504 tokens[0].parse::<f32>().unwrap(),
505 tokens[1].parse::<f32>().unwrap(),
506 tokens[2].parse::<f32>().unwrap(),
507 );
508 vecs.push(vec);
509}
510
511fn add_vec_2d(tokens: &[&str], vecs: &mut Vec<Vec2>)
512{
513 let vec = Vec2::new(
514 tokens[0].parse::<f32>().unwrap(),
515 tokens[1].parse::<f32>().unwrap(),
516 );
517 vecs.push(vec);
518}
519
520fn add_line(tokens: &[&str], vecs: &mut Vec<[u64; 2]>)
521{
522 assert!(tokens.len() == 2, "have {} expected 2", tokens.len());
523 let index_1 = tokens[0].parse::<u64>().unwrap() - 1;
524 let index_2 = tokens[1].parse::<u64>().unwrap() - 1;
525
526 vecs.push([index_1, index_2])
527}
528
529fn add_face(
530 tokens: &[&str],
531 vert_topology: &mut Vec<Vec<u64>>,
532 normal_topology: &mut Vec<Vec<u64>>,
533 uv_topology: &mut Vec<Vec<u64>>,
534 vert_count: usize,
535 uv_count: usize,
536 normal_count: usize,
537)
538{
539 assert!(
540 tokens.len() >= 3,
541 "have {} out of a minimum of 3",
542 tokens.len()
543 );
544 vert_topology.push(Vec::new());
545 uv_topology.push(Vec::new());
546 normal_topology.push(Vec::new());
547
548 for token in tokens
549 {
550 let inner_tokens = token.split("/").collect::<Vec<&str>>();
551
552 assert!(inner_tokens.len() <= 3 && inner_tokens.len() >= 1);
553
554 let index = inner_tokens[0].parse::<i64>().unwrap();
555 let vert_index = if index < 0
556 {
557 (vert_count as i64 + index) as u64
558 }
559 else
560 {
561 (index - 1) as u64
562 };
563
564 vert_topology.last_mut().unwrap().push(vert_index);
565
566 if inner_tokens.len() == 1
568 {
569 continue;
570 }
571
572 if inner_tokens.len() == 2
574 {
575 let index = inner_tokens[1].parse::<i64>().unwrap();
576 let uv_index = if index < 0
577 {
578 (uv_count as i64 + index) as u64
579 }
580 else
581 {
582 (index - 1) as u64
583 };
584
585 uv_topology.last_mut().unwrap().push(uv_index);
586 continue;
587 }
588
589 if !inner_tokens[1].is_empty()
590 {
591 let index = inner_tokens[1].parse::<i64>().unwrap();
592 let uv_index = if index < 0
593 {
594 (uv_count as i64 + index) as u64
595 }
596 else
597 {
598 (index - 1) as u64
599 };
600
601 uv_topology.last_mut().unwrap().push(uv_index);
602 }
603
604 if !inner_tokens[2].is_empty()
605 {
606 let index = inner_tokens[2].parse::<i64>().unwrap();
607 let normal_index = if index < 0
608 {
609 (normal_count as i64 + index) as u64
610 }
611 else
612 {
613 (index - 1) as u64
614 };
615
616 normal_topology.last_mut().unwrap().push(normal_index);
617 }
618 }
619
620 if uv_topology.last().unwrap().is_empty()
622 {
623 uv_topology.pop();
624 }
625 if normal_topology.last().unwrap().is_empty()
626 {
627 normal_topology.pop();
628 }
629}
630
631#[cfg(test)]
632mod test
633{
634 use super::*;
635
636 #[test]
637 fn test_load_model()
638 {
639 let data = ObjData::from_disk_file("../../../Assets/cube_fs.obj");
640
641 ObjData::export(&data, "wavefront_test.obj");
642 }
643}