mesh_loader/collada/
geometry.rs

1use super::*;
2
3/// The `<library_geometries>` element.
4///
5/// See the [specification][1.4] for details.
6///
7/// [1.4] https://www.khronos.org/files/collada_spec_1_4.pdf#page=99
8#[derive(Default)]
9pub(super) struct LibraryGeometries<'a> {
10    // /// The unique identifier of this element.
11    // pub(super) id: Option<&'a str>,
12    // /// The name of this element.
13    // pub(super) name: Option<&'a str>,
14    pub(super) geometries: BTreeMap<&'a str, Geometry<'a>>,
15
16    pub(super) accessors: HashMap<&'a str, Accessor<'a>>,
17    pub(super) array_data: HashMap<&'a str, ArrayData<'a>>,
18}
19
20/// The `<geometry>` element.
21///
22/// See the [specification][1.4] for details.
23///
24/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=68
25pub(super) struct Geometry<'a> {
26    /// The unique identifier of this element.
27    pub(super) id: &'a str,
28    // /// The name of this element.
29    // pub(super) name: Option<&'a str>,
30    pub(super) mesh: Mesh<'a>,
31}
32
33/// The `<mesh>` element.
34///
35/// See the [specification][1.4] for details.
36///
37/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=112
38pub(super) struct Mesh<'a> {
39    pub(super) vertices: Vertices<'a>,
40    pub(super) primitives: Vec<Primitive<'a>>,
41}
42
43pub(super) struct Vertices<'a> {
44    /// The unique identifier of this element.
45    pub(super) id: &'a str,
46    // /// The name of this element.
47    // pub(super) name: Option<&'a str>,
48    pub(super) input: VerticesInputs<'a>,
49}
50
51pub(super) struct VerticesInputs<'a> {
52    pub(super) position: UnsharedInput<'a>,
53    pub(super) normal: Option<UnsharedInput<'a>>,
54    pub(super) texcoord: Option<UnsharedInput<'a>>,
55    pub(super) color: Option<UnsharedInput<'a>>,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
59pub(super) enum PrimitiveType {
60    /// The `<lines>` element.
61    Lines,
62    /// The `<linestrips>` element.
63    LineStrips,
64    /// The `<polygons>` element.
65    Polygons,
66    /// The `<polylist>` element.
67    Polylist,
68    /// The `<triangles>` element.
69    Triangles,
70    /// The `<trifans>` element.
71    TriFans,
72    /// The `<tristrips>` element.
73    TriStrips,
74}
75
76impl PrimitiveType {
77    pub(super) fn face_size(self) -> Option<u32> {
78        match self {
79            PrimitiveType::Lines | PrimitiveType::LineStrips => Some(2),
80            PrimitiveType::Triangles | PrimitiveType::TriFans | PrimitiveType::TriStrips => Some(3),
81            PrimitiveType::Polygons | PrimitiveType::Polylist => None,
82        }
83    }
84
85    pub(super) fn min_face_size(self) -> u32 {
86        self.face_size().unwrap_or(1)
87    }
88}
89
90pub(super) struct PrimitiveInputs<'a> {
91    pub(super) vertex: SharedInput<'a, Vertices<'a>>,
92    pub(super) normal: Option<SharedInput<'a>>,
93    pub(super) color: Option<SharedInput<'a>>,
94    pub(super) texcoord: Vec<SharedInput<'a>>,
95}
96
97pub(super) struct Primitive<'a> {
98    /// The type of this element.
99    pub(super) ty: PrimitiveType,
100
101    // /// The name of this element.
102    // pub(super) name: Option<&'a str>,
103    /// The number of primitives.
104    pub(super) count: u32,
105    /// A symbol for a material.
106    pub(super) material: Option<&'a str>,
107
108    /// Declares the input semantics of a data source and connects a consumer to that source.
109    pub(super) input: Option<PrimitiveInputs<'a>>,
110    /// The number of vertices for one polygon.
111    ///
112    /// Only [polylist] actually have a vcount element, but we use this field to
113    /// represent the number of primitives other than [lines] and [triangles].
114    ///
115    /// The values included in this list are:
116    ///
117    /// - For [polylist] and [polygons]: `1 <= n`, and contains one polygon.
118    /// - For [linestrips]: `2 <= n`, and contains `n - 1` lines.
119    /// - For [tristrips] and [trifans]: `3 <= n`, and contains `n - 2` triangles.
120    ///
121    /// For [lines] and [triangles]: Since we know vcount of [lines] is always `vec![2; count]` and vcount of
122    /// [triangles] is always `vec![3; count]`, this field is not used and is empty.
123    ///
124    /// [lines]: PrimitiveType::Lines
125    /// [linestrips]: PrimitiveType::LineStrips
126    /// [polylist]: PrimitiveType::Polylist
127    /// [polygons]: PrimitiveType::Polygons
128    /// [triangles]: PrimitiveType::Triangles
129    /// [trifans]: PrimitiveType::TriFans
130    /// [tristrips]: PrimitiveType::TriStrips
131    pub(super) vcount: Vec<u32>,
132    /// The vertex attributes (indices) for an individual primitive.
133    pub(super) p: Vec<u32>,
134
135    pub(super) stride: u32,
136}
137
138// -----------------------------------------------------------------------------
139// Parsing
140
141pub(super) fn parse_library_geometries<'a>(
142    cx: &mut Context<'a>,
143    node: xml::Node<'a, '_>,
144) -> io::Result<()> {
145    debug_assert_eq!(node.tag_name().name(), "library_geometries");
146    // cx.library_geometries.id = node.attribute("id");
147    // cx.library_geometries.name = node.attribute("name");
148
149    for node in node.element_children() {
150        match node.tag_name().name() {
151            "geometry" => {
152                if let Some(geometry) = parse_geometry(cx, node)? {
153                    cx.library_geometries
154                        .geometries
155                        .insert(geometry.id, geometry);
156                }
157            }
158            "asset" | "extra" => { /* skip */ }
159            _ => return Err(error::unexpected_child_elem(node)),
160        }
161    }
162
163    if cx.library_geometries.geometries.is_empty() {
164        return Err(error::one_or_more_elems(node, "geometry"));
165    }
166
167    Ok(())
168}
169
170fn parse_geometry<'a>(
171    cx: &mut Context<'a>,
172    node: xml::Node<'a, '_>,
173) -> io::Result<Option<Geometry<'a>>> {
174    debug_assert_eq!(node.tag_name().name(), "geometry");
175    // The specification say it is optional, but it is actually required.
176    let id = node.required_attribute("id")?;
177    let mut mesh = None;
178
179    for node in node.element_children() {
180        match node.tag_name().name() {
181            "mesh" => {
182                mesh = Some(parse_mesh(cx, node)?);
183            }
184            "convex_mesh" | "spline" | "brep" => {
185                // warn!(
186                //     "<{}> child element in <{}> element is unsupported ({})",
187                //     child.tag_name().name(),
188                //     child.parent_element().unwrap().tag_name().name(),
189                //     child.node_location()
190                // );
191                return Ok(None);
192            }
193            "asset" | "extra" => { /* skip */ }
194            _ => return Err(error::unexpected_child_elem(node)),
195        }
196    }
197
198    let mesh = match mesh {
199        Some(mesh) => mesh,
200        None => return Err(error::one_or_more_elems(node, "mesh")),
201    };
202
203    Ok(Some(Geometry {
204        id,
205        // name: node.attribute("name"),
206        mesh,
207    }))
208}
209
210fn parse_mesh<'a>(cx: &mut Context<'a>, node: xml::Node<'a, '_>) -> io::Result<Mesh<'a>> {
211    debug_assert_eq!(node.tag_name().name(), "mesh");
212    let mut primitives = vec![];
213    let mut has_source = false;
214    let mut vertices = None;
215
216    for node in node.element_children() {
217        let name = node.tag_name().name();
218        match name {
219            "source" => {
220                has_source = true;
221                let s = Source::parse(node)?;
222                if let Some(acc) = s.accessor {
223                    cx.library_geometries.accessors.insert(s.id, acc);
224                }
225                if let Some(data) = s.array_element {
226                    cx.library_geometries.array_data.insert(data.id, data.data);
227                }
228            }
229            "vertices" => {
230                vertices = Some(parse_vertices(node)?);
231            }
232            "lines" | "linestrips" | "polygons" | "polylist" | "triangles" | "trifans"
233            | "tristrips" => {
234                primitives.push(parse_primitive(node, name.parse().unwrap())?);
235            }
236            "extra" => { /* skip */ }
237            _ => return Err(error::unexpected_child_elem(node)),
238        }
239    }
240
241    if !has_source {
242        return Err(error::one_or_more_elems(node, "source"));
243    }
244    let vertices = match vertices {
245        Some(vertices) => vertices,
246        None => return Err(error::exactly_one_elem(node, "vertices")),
247    };
248
249    Ok(Mesh {
250        vertices,
251        primitives,
252    })
253}
254
255fn parse_vertices<'a>(node: xml::Node<'a, '_>) -> io::Result<Vertices<'a>> {
256    debug_assert_eq!(node.tag_name().name(), "vertices");
257    let id = node.required_attribute("id")?;
258
259    let mut input_position = None;
260    let mut input_normal = None;
261    let mut input_texcoord = None;
262    let mut input_color = None;
263
264    for node in node.element_children() {
265        match node.tag_name().name() {
266            "input" => {
267                let i = UnsharedInput::parse(node)?;
268                match i.semantic {
269                    InputSemantic::POSITION => input_position = Some(i),
270                    InputSemantic::NORMAL => input_normal = Some(i),
271                    InputSemantic::TEXCOORD => input_texcoord = Some(i),
272                    InputSemantic::COLOR => input_color = Some(i),
273                    _semantic => {
274                        // warn!(
275                        //     "unsupported semantic {:?} in <input> ({})",
276                        //     semantic,
277                        //     node.node_location(),
278                        // );
279                    }
280                }
281            }
282            "extra" => { /* skip */ }
283            _ => return Err(error::unexpected_child_elem(node)),
284        }
285    }
286
287    // One input must specify semantic="POSITION".
288    let input_position = match input_position {
289        Some(input_position) => input_position,
290        None => return Err(error::one_or_more_elems(node, "input")),
291    };
292
293    Ok(Vertices {
294        id,
295        // name: node.attribute("name"),
296        input: VerticesInputs {
297            position: input_position,
298            normal: input_normal,
299            texcoord: input_texcoord,
300            color: input_color,
301        },
302    })
303}
304
305impl FromStr for PrimitiveType {
306    type Err = io::Error;
307
308    fn from_str(s: &str) -> Result<Self, Self::Err> {
309        Ok(match s {
310            "lines" => Self::Lines,
311            "linestrips" => Self::LineStrips,
312            "polygons" => Self::Polygons,
313            "polylist" => Self::Polylist,
314            "triangles" => Self::Triangles,
315            "trifans" => Self::TriFans,
316            "tristrips" => Self::TriStrips,
317            _ => bail!("unknown primitive type {:?}", s),
318        })
319    }
320}
321
322fn parse_primitive<'a>(node: xml::Node<'a, '_>, ty: PrimitiveType) -> io::Result<Primitive<'a>> {
323    debug_assert_eq!(node.tag_name().name().parse::<PrimitiveType>().unwrap(), ty);
324    let count: u32 = node.parse_required_attribute("count")?;
325    let mut vcount = vec![];
326    let mut p = vec![];
327    let mut stride = 0;
328
329    let mut input_vertex = None;
330    let mut input_normal = None;
331    let mut input_color = None;
332    let mut input_texcoord = vec![];
333
334    for node in node.element_children() {
335        match node.tag_name().name() {
336            "input" => {
337                let i = SharedInput::parse(node)?;
338                stride = cmp::max(stride, i.offset + 1);
339                match i.semantic {
340                    InputSemantic::VERTEX => {
341                        // ignore all position streams except 0 - there can be only one position
342                        if i.set == 0 {
343                            input_vertex = Some(i);
344                        }
345                    }
346                    InputSemantic::NORMAL => {
347                        // ignore all position streams except 0 - there can be only one position
348                        if i.set == 0 {
349                            input_normal = Some(i);
350                        }
351                    }
352                    InputSemantic::COLOR => input_color = Some(i),
353                    InputSemantic::TEXCOORD => input_texcoord.push(i),
354                    _semantic => {
355                        // warn!(
356                        //     "unsupported semantic {:?} in <input> ({})",
357                        //     semantic,
358                        //     node.node_location(),
359                        // );
360                    }
361                }
362            }
363            "vcount" => {
364                // Only <polylist> has <vcount>.
365                if ty != PrimitiveType::Polylist {
366                    return Err(error::unexpected_child_elem(node));
367                }
368                if !vcount.is_empty() {
369                    return Err(error::multiple_elems(node));
370                }
371                // It is possible to not contain any indices.
372                if count == 0 {
373                    continue;
374                }
375
376                vcount.reserve(count as usize);
377
378                // TODO: use parse_int_array_exact?
379                let mut iter = xml::parse_int_array::<u32>(node.trimmed_text());
380                for _ in 0..count {
381                    let value = iter.next().ok_or_else(|| {
382                        format_err!(
383                            "expected more values while reading <{}> contents at {}",
384                            node.tag_name().name(),
385                            node.node_location()
386                        )
387                    })??;
388                    if value >= 1 {
389                        vcount.push(value);
390                    } else {
391                        bail!(
392                            "incorrect number of indices in <p> element ({})",
393                            node.node_location()
394                        );
395                    }
396                }
397            }
398            "p" => {
399                // It is possible to not contain any indices.
400                if count == 0 {
401                    continue;
402                }
403
404                if matches!(
405                    ty,
406                    PrimitiveType::Lines | PrimitiveType::Polylist | PrimitiveType::Triangles
407                ) {
408                    // For primitives with at most one <p> element,
409                    // the length of indices can be pre-calculated.
410
411                    if !p.is_empty() {
412                        return Err(error::multiple_elems(node));
413                    }
414
415                    let mut expected_count = 0;
416                    match ty {
417                        PrimitiveType::Polylist => {
418                            for &i in &vcount {
419                                expected_count += i as usize;
420                            }
421                        }
422                        PrimitiveType::Lines => {
423                            expected_count = count as usize * 2;
424                        }
425                        PrimitiveType::Triangles => {
426                            expected_count = count as usize * 3;
427                        }
428                        _ => unreachable!(),
429                    }
430
431                    p.reserve(expected_count * stride as usize);
432
433                    // TODO: It seems some exporters put negative indices sometimes.
434                    // TODO: use parse_int_array_exact?
435                    for value in xml::parse_int_array(node.trimmed_text()) {
436                        p.push(value.map_err(|e| {
437                            format_err!(
438                                "{e} in <{}> element ({})",
439                                node.tag_name().name(),
440                                node.text_location(),
441                            )
442                        })?);
443                    }
444
445                    if p.len() != expected_count * stride as usize {
446                        // TODO: It seems SketchUp 15.3.331 writes the wrong 'count' for 'lines'.
447                        bail!(
448                            "incorrect index count in <p> element, expected {} but found {} ({})",
449                            expected_count * stride as usize,
450                            p.len(),
451                            node.node_location()
452                        );
453                    }
454                } else {
455                    // For primitives that can have multiple <p> elements,
456                    // One <p> element corresponds to one polygon.
457                    // Therefore, we represent them in the same way as polylist.
458                    // See the description of the `Primitive::vcount` field for more information.
459
460                    if vcount.capacity() == 0 {
461                        vcount.reserve(count as usize);
462                    }
463
464                    let prev_len = p.len();
465
466                    // TODO: It seems some exporters put negative indices sometimes.
467                    // TODO: use parse_int_array_exact?
468                    for value in xml::parse_int_array(node.trimmed_text()) {
469                        p.push(value.map_err(|e| {
470                            format_err!(
471                                "{e} in <{}> element ({})",
472                                node.tag_name().name(),
473                                node.text_location(),
474                            )
475                        })?);
476                    }
477
478                    #[allow(clippy::cast_possible_truncation)]
479                    let added = (p.len() - prev_len) as u32;
480                    if added % stride != 0 {
481                        bail!(
482                            "incorrect index count in <p> element, expected multiple of {}, but found {} ({})",
483                            stride,
484                            p.len(),
485                            node.node_location()
486                        );
487                    }
488                    let vc = added / stride;
489                    if vc >= ty.min_face_size() {
490                        vcount.push(vc);
491                    } else {
492                        bail!(
493                            "incorrect number of indices in <p> element ({})",
494                            node.node_location()
495                        );
496                    }
497                }
498            }
499            "ph" => {
500                // warn!(
501                //     "<{}> child element in <{}> element is unsupported ({})",
502                //     child.tag_name().name(),
503                //     child.parent_element().unwrap().tag_name().name(),
504                //     child.node_location()
505                // );
506            }
507            "extra" => { /* skip */ }
508            _ => return Err(error::unexpected_child_elem(node)),
509        }
510    }
511
512    // When at least one input is present, one input must specify semantic="VERTEX".
513    if input_vertex.is_none()
514        && (input_normal.is_some() || input_color.is_some() || !input_texcoord.is_empty())
515    {
516        bail!(
517            "one <input> in <{}> element must specify semantic=\"VERTEX\" ({})",
518            node.tag_name().name(),
519            node.node_location()
520        );
521    }
522    // Attempt to respect the specified set.
523    if !input_texcoord.is_empty() {
524        input_texcoord.sort_by_key(|i| i.set);
525    }
526
527    Ok(Primitive {
528        ty,
529        // name: node.attribute("name"),
530        count,
531        material: node.attribute("material"),
532        input: input_vertex.map(|vertex| PrimitiveInputs {
533            vertex: vertex.cast(),
534            normal: input_normal,
535            color: input_color,
536            texcoord: input_texcoord,
537        }),
538        vcount,
539        p,
540        stride,
541    })
542}