obj/raw/
object.rs

1//! Parses `.obj` format which stores 3D mesh data
2
3use std::collections::hash_map::Entry;
4use std::collections::HashMap;
5use std::hash::Hash;
6use std::io::BufRead;
7use std::mem;
8
9use crate::error::{make_error, ObjResult};
10use crate::raw::lexer::lex;
11use crate::raw::util::parse_args;
12
13macro_rules! parse_args {
14    {
15        $first:expr, $rest:expr,
16        $($pat:pat => $type:ident::$name:ident[$exp:expr]),*,
17        ! => $error:expr
18    } => (
19        match split_vertex_group($first)[..] {
20            $($pat => $type::$name({
21                let mut points = vec![$exp];
22                for param in $rest {
23                    match split_vertex_group(param)[..] {
24                        $pat => points.push($exp),
25                        _ => $error
26                    }
27                }
28                points
29            }),)*
30            _ => $error
31        }
32    )
33}
34
35// Helper function for handling the indexes.
36//
37// If total size of the collection is 5:
38//
39// - ["1", "2", "3", "4", "5"] → [0, 1, 2, 3, 4]
40// - ["-5", "-4", "-3", "-2", "-1"] → [0, 1, 2, 3, 4]
41// - ["0"] → Error
42// - ["6"] → Error
43// - ["-6"] → Error
44//
45// If the index is < 0, then it represents an offset from the end of
46// the current list. So -1 is the most recently added vertex.
47//
48// If the index is > 0 then it's simply the position in the list such
49// that 1 is the first vertex.
50fn try_index<T>(collection: &[T], input: &str) -> ObjResult<usize> {
51    use crate::error::{LoadError, LoadErrorKind, ObjError};
52
53    let len: isize = collection.len().try_into().map_err(|_| {
54        ObjError::Load(LoadError::new_internal(
55            LoadErrorKind::IndexOutOfRange,
56            "Too many items in collection".to_string(),
57        ))
58    })?;
59
60    // Should be [-len, -1] ∪ [1, len]
61    let index: isize = input.parse()?;
62
63    let ret = if index < -len {
64        // (∞, -len)
65        make_error!(IndexOutOfRange, "Too small index value");
66    } else if index < 0 {
67        // [-len, 0)
68        len + index
69    } else if index == 0 {
70        // {0}
71        make_error!(IndexOutOfRange, "Index value shouldn't be zero");
72    } else if index <= len {
73        // (0, len]
74        index - 1
75    } else {
76        // (len, ∞)
77        make_error!(IndexOutOfRange, "Too big index value");
78    };
79
80    Ok(ret as usize)
81}
82
83/// Parses a wavefront `.obj` format.
84pub fn parse_obj<T: BufRead>(input: T) -> ObjResult<RawObj> {
85    let mut name = None;
86    let mut material_libraries = Vec::new();
87
88    let mut positions = Vec::new();
89    let mut tex_coords = Vec::new();
90    let mut normals = Vec::new();
91    let mut param_vertices = Vec::new();
92
93    let mut points = Vec::new();
94    let mut lines = Vec::new();
95    let mut polygons = Vec::new();
96
97    let counter = Counter::new(&points, &lines, &polygons);
98    let mut group_builder = GroupBuilder::with_default(&counter, String::from("default"));
99    let mut mesh_builder = GroupBuilder::with_default(&counter, String::new());
100    let mut smoothing_builder = GroupBuilder::new(&counter);
101    let mut merging_builder = GroupBuilder::new(&counter);
102
103    lex(input, |stmt, args: &[&str]| {
104        match stmt {
105            // Vertex data
106            "v" => positions.push(match parse_args(args)?[..] {
107                [x, y, z, w] => (x, y, z, w),
108                [x, y, z] => (x, y, z, 1.0),
109                _ => make_error!(WrongNumberOfArguments, "Expected 3 or 4 arguments"),
110            }),
111            "vt" => tex_coords.push(match parse_args(args)?[..] {
112                [u, v, w] => (u, v, w),
113                [u, v] => (u, v, 0.0),
114                [u] => (u, 0.0, 0.0),
115                _ => make_error!(WrongNumberOfArguments, "Expected 1, 2 or 3 arguments"),
116            }),
117            "vn" => normals.push(match parse_args(args)?[..] {
118                [x, y, z] => (x, y, z),
119                _ => make_error!(WrongNumberOfArguments, "Expected 3 arguments"),
120            }),
121            "vp" => param_vertices.push(match parse_args(args)?[..] {
122                [u, v, w] => (u, v, w),
123                [u, v] => (u, v, 1.0),
124                [u] => (u, 0.0, 1.0),
125                _ => make_error!(WrongNumberOfArguments, "Expected 1, 2 or 3 arguments"),
126            }),
127
128            // Free-form curve / surface attributes
129            // TODO: Use rational information
130            "cstype" => {
131                let geometry = match args {
132                    ["rat", ty] => *ty,
133                    [ty] => *ty,
134                    _ => make_error!(WrongTypeOfArguments, "Expected 'rat xxx' or 'xxx' format"),
135                };
136
137                match geometry {
138                    "bmatrix" => unimplemented!(),
139                    "bezier" => unimplemented!(),
140                    "bspline" => unimplemented!(),
141                    "cardinal" => unimplemented!(),
142                    "taylor" => unimplemented!(),
143                    _ => make_error!(
144                        WrongTypeOfArguments,
145                        "Expected one of 'bmatrix', 'bezier', 'bspline', 'cardinal' and 'taylor'"
146                    ),
147                }
148            }
149            "deg" => match parse_args(args)?[..] {
150                [_deg_u, _deg_v] => unimplemented!(),
151                [_deg_u] => unimplemented!(),
152                _ => make_error!(WrongNumberOfArguments, "Expected 1 or 2 arguments"),
153            },
154            "bmat" => unimplemented!(),
155            "step" => unimplemented!(),
156
157            // Elements
158            "p" => {
159                for v in args {
160                    let v = try_index(&positions, v)?;
161                    points.push(v);
162                }
163            }
164            "l" => match args {
165                [] => make_error!(WrongNumberOfArguments, "Expected at least 2 arguments"),
166                [first, rest @ ..] => {
167                    if args.len() < 2 {
168                        make_error!(WrongNumberOfArguments, "Expected at least 2 arguments")
169                    }
170
171                    let line = parse_args! {
172                        first, rest,
173                        [p] => Line::P[try_index(&positions, p)?],
174                        [p, t] => Line::PT[(try_index(&positions, p)?, try_index(&tex_coords, t)?)],
175                        ! => make_error!(WrongTypeOfArguments, "Unexpected vertex format, expected `#`, or `#/#`")
176                    };
177
178                    lines.push(line);
179                }
180            },
181            "fo" | "f" => match args {
182                [] => make_error!(WrongNumberOfArguments, "Expected at least 3 arguments"),
183                [first, rest @ ..] => {
184                    if args.len() < 3 {
185                        make_error!(WrongNumberOfArguments, "Expected at least 3 arguments")
186                    }
187
188                    let polygon = parse_args! {
189                        first, rest,
190                        [p] => Polygon::P[try_index(&positions, p)?],
191                        [p, t] => Polygon::PT[(try_index(&positions, p)?, try_index(&tex_coords, t)?)],
192                        [p, "", n] => Polygon::PN[(try_index(&positions, p)?, try_index(&normals, n)?)],
193                        [p, t, n] => Polygon::PTN[(try_index(&positions, p)?, try_index(&tex_coords, t)?, try_index(&normals, n)?)],
194                        ! => make_error!(WrongTypeOfArguments, "Unexpected vertex format, expected `#`, `#/#`, `#//#`, or `#/#/#`")
195                    };
196
197                    polygons.push(polygon);
198                }
199            },
200            "curv" => unimplemented!(),
201            "curv2" => unimplemented!(),
202            "surf" => unimplemented!(),
203
204            // Free-form curve / surface body statements
205            "parm" => unimplemented!(),
206            "trim" => unimplemented!(),
207            "hole" => unimplemented!(),
208            "scrv" => unimplemented!(),
209            "sp" => unimplemented!(),
210            "end" => unimplemented!(),
211
212            // Connectivity between free-form surfaces
213            "con" => unimplemented!(),
214
215            // Grouping
216            "g" => match args {
217                [name] => group_builder.start((*name).to_string()),
218                _ => make_error!(
219                    WrongNumberOfArguments,
220                    "Expected group name parameter, but nothing has been supplied"
221                ),
222            },
223            "s" => match args {
224                ["off"] | ["0"] => smoothing_builder.end(),
225                [param] => smoothing_builder.start(param.parse()?),
226                _ => make_error!(WrongNumberOfArguments, "Expected only 1 argument"),
227            },
228            "mg" => match args {
229                ["off"] | ["0"] => merging_builder.end(),
230                [param] => merging_builder.start(param.parse()?),
231                _ => make_error!(WrongNumberOfArguments, "Expected only 1 argument"),
232            },
233            "o" => {
234                name = match args {
235                    [] => None,
236                    _ => Some(args.join(" ")),
237                    // TODO: "name a  b" will be parsed as "name a b"
238                }
239            }
240
241            // Display / render attributes
242            "bevel" => unimplemented!(),
243            "c_interp" => unimplemented!(),
244            "d_interp" => unimplemented!(),
245            "lod" => unimplemented!(),
246            "usemtl" => match args {
247                [material] => mesh_builder.start((*material).to_string()),
248                _ => make_error!(WrongNumberOfArguments, "Expected only 1 argument"),
249            },
250            "mtllib" => {
251                material_libraries.reserve(args.len());
252                for &path in args {
253                    material_libraries.push(path.to_string());
254                }
255            }
256            "shadow_obj" => unimplemented!(),
257            "trace_obj" => unimplemented!(),
258            "ctech" => unimplemented!(),
259            "stech" => unimplemented!(),
260
261            // Unexpected statement
262            _ => make_error!(UnexpectedStatement, "Received unknown statement"),
263        }
264
265        Ok(())
266    })?;
267
268    group_builder.end();
269    mesh_builder.end();
270    smoothing_builder.end();
271    merging_builder.end();
272
273    Ok(RawObj {
274        name,
275        material_libraries,
276
277        positions,
278        tex_coords,
279        normals,
280        param_vertices,
281
282        points,
283        lines,
284        polygons,
285
286        groups: group_builder.result,
287        meshes: mesh_builder.result,
288        smoothing_groups: smoothing_builder.result,
289        merging_groups: merging_builder.result,
290    })
291}
292
293/// Splits a string with '/'.
294fn split_vertex_group(input: &str) -> Vec<&str> {
295    input.split('/').collect()
296}
297
298/// Counts current total count of parsed `points`, `lines` and `polygons`.
299struct Counter {
300    points: *const Vec<Point>,
301    lines: *const Vec<Line>,
302    polygons: *const Vec<Polygon>,
303}
304
305impl Counter {
306    /// Constructs a new `Counter`.
307    fn new(
308        points: *const Vec<Point>,
309        lines: *const Vec<Line>,
310        polygons: *const Vec<Polygon>,
311    ) -> Self {
312        Counter {
313            points,
314            lines,
315            polygons,
316        }
317    }
318
319    /// Returns a current count of parsed `(points, lines, polygons)`.
320    fn get(&self) -> (usize, usize, usize) {
321        unsafe {
322            (
323                (*self.points).len(),
324                (*self.lines).len(),
325                (*self.polygons).len(),
326            )
327        }
328    }
329}
330
331/// Helper for creating `groups`, `meshes`, `smoothing_groups` and `merging_groups` member of
332/// `Obj`.
333struct GroupBuilder<'a, K> {
334    counter: &'a Counter,
335    /// `Some(K)` if some group has been started, `None` otherwise.
336    current: Option<K>,
337    result: HashMap<K, Group>,
338}
339
340impl<'a, K> GroupBuilder<'a, K>
341where
342    K: Clone + Eq + Hash,
343{
344    fn new(counter: &'a Counter) -> Self {
345        GroupBuilder {
346            counter,
347            current: None,
348            result: HashMap::new(),
349        }
350    }
351
352    fn with_default(counter: &'a Counter, default: K) -> Self {
353        let mut result = HashMap::with_capacity(1);
354        result.insert(default.clone(), Group::new((0, 0, 0)));
355
356        GroupBuilder {
357            counter,
358            current: Some(default),
359            result,
360        }
361    }
362
363    /// Starts a group whose name is `input`.
364    fn start(&mut self, input: K) {
365        let count = self.counter.get();
366
367        match self.current {
368            // Started same group twice, do nothing
369            Some(ref current) if *current == input => return,
370            // Changing group
371            Some(ref mut current) => {
372                // Close the past group and start a new one
373                let past = mem::replace(current, input.clone());
374                match self.result.entry(past) {
375                    Entry::Vacant(_) => unreachable!(),
376                    Entry::Occupied(mut e) => {
377                        let was_empty_group = e.get_mut().end(count);
378                        // Remove the past group if the past group is empty
379                        if was_empty_group {
380                            e.remove();
381                        }
382                    }
383                }
384            }
385            // Starting a new group
386            None => self.current = Some(input.clone()),
387        }
388
389        self.result
390            .entry(input)
391            .and_modify(|e| e.start(count))
392            .or_insert_with(|| Group::new(count));
393    }
394
395    /// Ends a current group.
396    fn end(&mut self) {
397        match self.current.take() {
398            // Closed group twice, do nothing
399            None => {}
400            // Closing a group
401            Some(current) => {
402                match self.result.entry(current) {
403                    Entry::Vacant(_) => unreachable!(),
404                    Entry::Occupied(mut e) => {
405                        let count = self.counter.get();
406                        let was_empty_group = e.get_mut().end(count);
407                        // Remove the past group if the past group is empty
408                        if was_empty_group {
409                            e.remove();
410                        }
411                    }
412                }
413            }
414        }
415    }
416}
417
418/// Constant which is used to represent undefined bound of range.
419const UNDEFINED: usize = usize::MAX;
420
421impl Group {
422    fn new(count: (usize, usize, usize)) -> Self {
423        let mut ret = Group {
424            points: Vec::with_capacity(1),
425            lines: Vec::with_capacity(1),
426            polygons: Vec::with_capacity(1),
427        };
428        ret.start(count);
429        ret
430    }
431
432    fn start(&mut self, count: (usize, usize, usize)) {
433        self.points.push(Range {
434            start: count.0,
435            end: UNDEFINED,
436        });
437        self.lines.push(Range {
438            start: count.1,
439            end: UNDEFINED,
440        });
441        self.polygons.push(Range {
442            start: count.2,
443            end: UNDEFINED,
444        })
445    }
446
447    /// Closes group, return true if self is empty
448    fn end(&mut self, count: (usize, usize, usize)) -> bool {
449        end(&mut self.points, count.0);
450        end(&mut self.lines, count.1);
451        end(&mut self.polygons, count.2);
452
453        fn end(vec: &mut Vec<Range>, end: usize) {
454            let last = vec.len() - 1;
455            assert_eq!(vec[last].end, UNDEFINED);
456            if vec[last].start != end {
457                vec[last].end = end;
458            } else {
459                vec.pop();
460            }
461        }
462
463        self.points.is_empty() && self.lines.is_empty() && self.polygons.is_empty()
464    }
465}
466
467/// Low-level Rust binding for `.obj` format.
468#[derive(Clone, PartialEq, Debug, Default)]
469pub struct RawObj {
470    /// Name of the object.
471    pub name: Option<String>,
472    /// `.mtl` files which required by this object.
473    pub material_libraries: Vec<String>,
474
475    /// Position vectors of each vertex.
476    pub positions: Vec<(f32, f32, f32, f32)>,
477    /// Texture coordinates of each vertex.
478    pub tex_coords: Vec<(f32, f32, f32)>,
479    /// Normal vectors of each vertex.
480    pub normals: Vec<(f32, f32, f32)>,
481    /// Parametric vertices.
482    pub param_vertices: Vec<(f32, f32, f32)>,
483
484    /// Points which stores the index data of position vectors.
485    pub points: Vec<Point>,
486    /// Lines which store the index data of vectors.
487    pub lines: Vec<Line>,
488    /// Polygons which store the index data of vectors.
489    pub polygons: Vec<Polygon>,
490
491    /// Groups of multiple geometries.
492    pub groups: HashMap<String, Group>,
493    /// Geometries which consist in a same material.
494    pub meshes: HashMap<String, Group>,
495    /// Smoothing groups.
496    pub smoothing_groups: HashMap<usize, Group>,
497    /// Merging groups.
498    pub merging_groups: HashMap<usize, Group>,
499}
500
501/// The `Point` type which stores the index of the position vector.
502pub type Point = usize;
503
504/// The `Line` type.
505#[derive(Clone, Eq, PartialEq, Hash, Debug)]
506pub enum Line {
507    /// A series of line segments which contain only the position data of each vertex
508    P(Vec<usize>),
509    /// A series of line segments which contain both position and texture coordinate
510    /// data of each vertex
511    PT(Vec<(usize, usize)>),
512}
513
514/// The `Polygon` type.
515#[derive(Clone, Eq, PartialEq, Hash, Debug)]
516pub enum Polygon {
517    /// A polygon which contains only the position data of each vertex.
518    P(Vec<usize>),
519    /// A polygon which contains both position and texture coordinate data of each vertex.
520    PT(Vec<(usize, usize)>),
521    /// A polygon which contains both position and normal data of each vertex.
522    PN(Vec<(usize, usize)>),
523    /// A polygon which contains all position, texture coordinate and normal data of each vertex.
524    PTN(Vec<(usize, usize, usize)>),
525}
526
527/// A group which contains ranges of points, lines and polygons
528#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
529pub struct Group {
530    /// Multiple range of points
531    pub points: Vec<Range>,
532    /// Multiple range of lines
533    pub lines: Vec<Range>,
534    /// Multiple range of polygons
535    pub polygons: Vec<Range>,
536}
537
538/// A struct which represent `[start, end)` range.
539#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
540pub struct Range {
541    /// The lower bound of the range (inclusive).
542    pub start: usize,
543    /// The upper bound of the range (exclusive).
544    pub end: usize,
545}