wavefront_loader/
lib.rs

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        // Only positions are specified.
567        if inner_tokens.len() == 1
568        {
569            continue;
570        }
571
572        // Only positions and texture coordinates are specified.
573        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    // Prevent creating empty faces.
621    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}