mesh_loader/collada/
scene.rs

1use super::*;
2
3/// The `<scene>` element.
4///
5/// See [specification][spec] for details.
6///
7/// [spec]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=141
8#[derive(Default)]
9pub(super) struct Scene<'a> {
10    pub(super) instance_visual_scene: Option<InstanceVisualScene<'a>>,
11}
12
13/// The `<instance_visual_scene>` element.
14///
15/// See [specification][spec] for details.
16///
17/// [spec]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=91
18pub(super) struct InstanceVisualScene<'a> {
19    // /// The scoped identifier of this element.
20    // pub(super) sid: Option<&'a str>,
21    // /// The name of this element.
22    // pub(super) name: Option<&'a str>,
23    /// The URI of the location of the [`VisualScene`] to instantiate.
24    pub(super) url: Uri<'a, Node<'a>>,
25}
26
27/// The `<library_visual_scenes>` element.
28///
29/// See the [specification][1.4] for details.
30///
31/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=102
32#[derive(Default)]
33pub(super) struct LibraryVisualScenes<'a> {
34    // /// The unique identifier of this element.
35    // pub(super) id: Option<&'a str>,
36    // /// The name of this element.
37    // pub(super) name: Option<&'a str>,
38    pub(super) nodes: Vec<Node<'a>>,
39}
40
41/// The `<node>` element.
42///
43/// See the [specification][1.4] for details.
44///
45/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=119
46#[derive(Default)]
47pub(super) struct Node<'a> {
48    /// The unique identifier of this element.
49    pub(super) id: Option<&'a str>,
50    // /// The name of this element.
51    // pub(super) name: Option<&'a str>,
52    // /// The scoped identifier of this element.
53    // pub(super) sid: Option<&'a str>,
54    // /// The type of this element.
55    // pub(super) ty: NodeType,
56    pub(super) parent: Option<usize>,
57    // pub(super) children: Vec<usize>,
58    // pub(super) transforms: Vec<Transform>,
59    pub(super) transform: Matrix4x4,
60    // pub(super) instance_camera: Vec<InstanceCamera>,
61    // pub(super) instance_controller: Vec<InstanceController>,
62    pub(super) instance_geometry: Vec<InstanceGeometry<'a>>,
63    // pub(super) instance_light: Vec<InstanceLight>,
64    // pub(super) instance_node: Vec<InstanceNode>,
65}
66
67/// The type of the [`Node`].
68#[derive(Debug)]
69pub(super) enum NodeType {
70    Joint,
71    Node,
72}
73
74impl Default for NodeType {
75    fn default() -> Self {
76        Self::Node
77    }
78}
79
80pub(super) enum Transform {
81    Lookat([f32; 9]),
82    Rotate([f32; 4]),
83    Translate([f32; 3]),
84    Scale([f32; 3]),
85    Skew(#[allow(dead_code)] [f32; 7]),
86    Matrix([f32; 16]),
87}
88
89impl Transform {
90    // Based on https://github.com/assimp/assimp/blob/v5.3.1/code/AssetLib/Collada/ColladaParser.cpp#L2318
91    fn calculate_transform(transforms: &[Self]) -> Matrix4x4 {
92        // Based on https://github.com/assimp/assimp/blob/v5.3.1/include/assimp/vector3.inl
93        fn sub(mut a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
94            a[0] -= b[0];
95            a[1] -= b[1];
96            a[2] -= b[2];
97            a
98        }
99        fn cross_product(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
100            let mut r = [0.; 3];
101            r[0] = a[1] * b[2] - a[2] * b[1];
102            r[1] = a[2] * b[0] - a[0] * b[2];
103            r[2] = a[0] * b[1] - a[1] * b[0];
104            r
105        }
106        fn normalize(mut v: [f32; 3]) -> [f32; 3] {
107            let square_len = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
108            let len = square_len.sqrt();
109            if len == 0. {
110                return v;
111            }
112            let inv = 1. / len;
113            v[0] *= inv;
114            v[1] *= inv;
115            v[2] *= inv;
116            v
117        }
118
119        let mut out = None;
120        for transform in transforms {
121            match transform {
122                Self::Lookat(f) => {
123                    let pos = [f[0], f[1], f[2]];
124                    let dst_pos = [f[3], f[4], f[5]];
125                    let up = normalize([f[6], f[7], f[8]]);
126                    let dir = normalize(sub(dst_pos, pos));
127                    let right = normalize(cross_product(dir, up));
128                    let m = Matrix4x4::new(
129                        right[0], up[0], -dir[0], pos[0], right[1], up[1], -dir[1], pos[1],
130                        right[2], up[2], -dir[2], pos[2], 0., 0., 0., 1.,
131                    );
132                    match &mut out {
133                        Some(out) => *out *= m,
134                        _ => out = Some(m),
135                    }
136                }
137                Self::Rotate(f) => {
138                    let angle = f[3].to_radians();
139                    let axis = [f[0], f[1], f[2]];
140                    let m = Matrix4x4::rotation(angle, axis);
141                    match &mut out {
142                        Some(out) => *out *= m,
143                        _ => out = Some(m),
144                    }
145                }
146                Self::Translate(f) => {
147                    let m = Matrix4x4::translation(*f);
148                    match &mut out {
149                        Some(out) => *out *= m,
150                        _ => out = Some(m),
151                    }
152                }
153                Self::Scale(f) => {
154                    let m = Matrix4x4::new(
155                        f[0], 0., 0., 0., 0., f[1], 0., 0., 0., 0., f[2], 0., 0., 0., 0., 1.,
156                    );
157                    match &mut out {
158                        Some(out) => *out *= m,
159                        _ => out = Some(m),
160                    }
161                }
162                Self::Skew(_f) => {
163                    // TODO
164                }
165                Self::Matrix(f) => {
166                    let m = Matrix4x4::new(
167                        f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10], f[11],
168                        f[12], f[13], f[14], f[15],
169                    );
170                    match &mut out {
171                        Some(out) => *out *= m,
172                        _ => out = Some(m),
173                    }
174                }
175            }
176        }
177        out.unwrap_or_default()
178    }
179}
180
181// Based on https://github.com/assimp/assimp/blob/v5.3.1/include/assimp/matrix4x4.inl
182#[derive(Clone, Copy)]
183pub(super) struct Matrix4x4 {
184    a1: f32,
185    a2: f32,
186    a3: f32,
187    a4: f32,
188    b1: f32,
189    b2: f32,
190    b3: f32,
191    b4: f32,
192    c1: f32,
193    c2: f32,
194    c3: f32,
195    c4: f32,
196    d1: f32,
197    d2: f32,
198    d3: f32,
199    d4: f32,
200}
201impl Matrix4x4 {
202    pub(super) const fn new(
203        a1: f32,
204        a2: f32,
205        a3: f32,
206        a4: f32,
207        b1: f32,
208        b2: f32,
209        b3: f32,
210        b4: f32,
211        c1: f32,
212        c2: f32,
213        c3: f32,
214        c4: f32,
215        d1: f32,
216        d2: f32,
217        d3: f32,
218        d4: f32,
219    ) -> Self {
220        Self {
221            a1,
222            a2,
223            a3,
224            a4,
225            b1,
226            b2,
227            b3,
228            b4,
229            c1,
230            c2,
231            c3,
232            c4,
233            d1,
234            d2,
235            d3,
236            d4,
237        }
238    }
239    fn rotation(a: f32, axis: [f32; 3]) -> Self {
240        let c = a.cos();
241        let s = a.sin();
242        let t = 1. - c;
243        let [x, y, z] = axis;
244        Self::new(
245            t * x * x + c,
246            t * x * y - s * z,
247            t * x * z + s * y,
248            0.,
249            t * x * y + s * z,
250            t * y * y + c,
251            t * y * z - s * x,
252            0.,
253            t * x * z - s * y,
254            t * y * z + s * x,
255            t * z * z + c,
256            0.,
257            0.,
258            0.,
259            0.,
260            1.,
261        )
262    }
263    fn translation(v: [f32; 3]) -> Self {
264        Self {
265            a4: v[0],
266            b4: v[1],
267            c4: v[2],
268            ..Default::default()
269        }
270    }
271    pub(super) fn is_identity(&self) -> bool {
272        // TODO: use f32::EPSILON?
273        const EPSILON: f32 = 10e-3;
274        self.a2 <= EPSILON
275            && self.a2 >= -EPSILON
276            && self.a3 <= EPSILON
277            && self.a3 >= -EPSILON
278            && self.a4 <= EPSILON
279            && self.a4 >= -EPSILON
280            && self.b1 <= EPSILON
281            && self.b1 >= -EPSILON
282            && self.b3 <= EPSILON
283            && self.b3 >= -EPSILON
284            && self.b4 <= EPSILON
285            && self.b4 >= -EPSILON
286            && self.c1 <= EPSILON
287            && self.c1 >= -EPSILON
288            && self.c2 <= EPSILON
289            && self.c2 >= -EPSILON
290            && self.c4 <= EPSILON
291            && self.c4 >= -EPSILON
292            && self.d1 <= EPSILON
293            && self.d1 >= -EPSILON
294            && self.d2 <= EPSILON
295            && self.d2 >= -EPSILON
296            && self.d3 <= EPSILON
297            && self.d3 >= -EPSILON
298            && self.a1 <= 1. + EPSILON
299            && self.a1 >= 1. - EPSILON
300            && self.b2 <= 1. + EPSILON
301            && self.b2 >= 1. - EPSILON
302            && self.c3 <= 1. + EPSILON
303            && self.c3 >= 1. - EPSILON
304            && self.d4 <= 1. + EPSILON
305            && self.d4 >= 1. - EPSILON
306    }
307}
308impl Default for Matrix4x4 {
309    fn default() -> Self {
310        Self::new(
311            1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.,
312        )
313    }
314}
315impl ops::MulAssign<Matrix4x4> for [f32; 4] {
316    fn mul_assign(&mut self, m: Matrix4x4) {
317        let [x, y, z, w] = *self;
318        self[0] = m.a1 * x + m.a2 * y + m.a3 * z + m.a4 * w;
319        self[1] = m.b1 * x + m.b2 * y + m.b3 * z + m.b4 * w;
320        self[2] = m.c1 * x + m.c2 * y + m.c3 * z + m.c4 * w;
321        self[3] = m.d1 * x + m.d2 * y + m.d3 * z + m.d4 * w;
322    }
323}
324impl ops::MulAssign<Matrix4x4> for [f32; 3] {
325    fn mul_assign(&mut self, m: Matrix4x4) {
326        let mut result = [self[0], self[1], self[2], 1.];
327        result *= m;
328        *self = [result[0], result[1], result[2]];
329    }
330}
331impl ops::MulAssign for Matrix4x4 {
332    fn mul_assign(&mut self, m: Self) {
333        let t = *self;
334        self.a1 = m.a1 * t.a1 + m.b1 * t.a2 + m.c1 * t.a3 + m.d1 * t.a4;
335        self.a2 = m.a2 * t.a1 + m.b2 * t.a2 + m.c2 * t.a3 + m.d2 * t.a4;
336        self.a3 = m.a3 * t.a1 + m.b3 * t.a2 + m.c3 * t.a3 + m.d3 * t.a4;
337        self.a4 = m.a4 * t.a1 + m.b4 * t.a2 + m.c4 * t.a3 + m.d4 * t.a4;
338        self.b1 = m.a1 * t.b1 + m.b1 * t.b2 + m.c1 * t.b3 + m.d1 * t.b4;
339        self.b2 = m.a2 * t.b1 + m.b2 * t.b2 + m.c2 * t.b3 + m.d2 * t.b4;
340        self.b3 = m.a3 * t.b1 + m.b3 * t.b2 + m.c3 * t.b3 + m.d3 * t.b4;
341        self.b4 = m.a4 * t.b1 + m.b4 * t.b2 + m.c4 * t.b3 + m.d4 * t.b4;
342        self.c1 = m.a1 * t.c1 + m.b1 * t.c2 + m.c1 * t.c3 + m.d1 * t.c4;
343        self.c2 = m.a2 * t.c1 + m.b2 * t.c2 + m.c2 * t.c3 + m.d2 * t.c4;
344        self.c3 = m.a3 * t.c1 + m.b3 * t.c2 + m.c3 * t.c3 + m.d3 * t.c4;
345        self.c4 = m.a4 * t.c1 + m.b4 * t.c2 + m.c4 * t.c3 + m.d4 * t.c4;
346        self.d1 = m.a1 * t.d1 + m.b1 * t.d2 + m.c1 * t.d3 + m.d1 * t.d4;
347        self.d2 = m.a2 * t.d1 + m.b2 * t.d2 + m.c2 * t.d3 + m.d2 * t.d4;
348        self.d3 = m.a3 * t.d1 + m.b3 * t.d2 + m.c3 * t.d3 + m.d3 * t.d4;
349        self.d4 = m.a4 * t.d1 + m.b4 * t.d2 + m.c4 * t.d3 + m.d4 * t.d4;
350    }
351}
352
353/// The `<instance_geometry>` element.
354///
355/// See the [specification][1.4] for details.
356///
357/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=85
358pub(super) struct InstanceGeometry<'a> {
359    // /// The scoped identifier of this element.
360    // pub(super) sid: Option<&'a str>,
361    // /// The name of this element.
362    // pub(super) name: Option<&'a str>,
363    /// The URI of the location of the [`Geometry`] to instantiate.
364    pub(super) url: Uri<'a, Geometry<'a>>,
365
366    pub(super) materials: BTreeMap<&'a str, SemanticMappingTable<'a>>,
367}
368
369/*
370/// The `<instance_controller>` element.
371///
372/// See the [specification][1.4] for details.
373///
374/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=82
375pub(super) struct InstanceController<'a> {
376    /// The scoped identifier of this element.
377    pub(super) sid: Option<&'a str>,
378    /// The name of this element.
379    pub(super) name: Option<&'a str>,
380    /// The URI of the location of the [`Controller`] to instantiate.
381    pub(super) url: Uri<Controller>,
382
383    pub(super) materials: IndexMap<&'a str, SemanticMappingTable>,
384}
385*/
386
387pub(super) struct SemanticMappingTable<'a> {
388    // Required
389    pub(super) target: Uri<'a, Material<'a>>,
390    // Required
391    pub(super) symbol: &'a str,
392    // pub(super) map: HashMap<&'a str, InputSemanticMapEntry>,
393}
394
395// pub(super) struct InputSemanticMapEntry {
396//     pub(super) input_semantic: InputSemantic,
397//     pub(super) input_set: u32,
398// }
399
400/*
401/// The `<instance_camera>` element.
402///
403/// See the [specification][1.4] for details.
404///
405/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=80
406#[derive(Debug)]
407#[non_exhaustive]
408pub struct InstanceCamera<'a> {
409    /// The scoped identifier of this element.
410    pub sid: Option<&'a str>,
411    /// The name of this element.
412    pub name: Option<&'a str>,
413    /// The URI of the location of the [`Camera`] to instantiate.
414    pub url: Uri<Camera<'a>>,
415}
416
417/// The `<instance_light>` element.
418///
419/// See the [specification][1.4] for details.
420///
421/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=87
422#[derive(Debug)]
423#[non_exhaustive]
424pub struct InstanceLight<'a> {
425    /// The scoped identifier of this element.
426    pub sid: Option<&'a str>,
427    /// The name of this element.
428    pub name: Option<&'a str>,
429    /// The URI of the location of the [`Light`] to instantiate.
430    pub url: Uri<Light<'a>>,
431}
432
433/// The `<instance_node>` element.
434///
435/// See the [specification][1.4] for details.
436///
437/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=89
438pub(super) struct InstanceNode<'a> {
439    /// The scoped identifier of this element.
440    pub(super) sid: Option<&'a str>,
441    /// The name of this element.
442    pub(super) name: Option<&'a str>,
443    /// The URI of the location of the [`Node`] to instantiate.
444    pub(super) url: Uri<'a, Node<'a>>,
445}
446*/
447
448// -----------------------------------------------------------------------------
449// Parsing
450
451pub(super) fn parse_scene<'a>(
452    _cx: &mut Context<'a>,
453    node: xml::Node<'a, '_>,
454) -> io::Result<Scene<'a>> {
455    debug_assert_eq!(node.tag_name().name(), "scene");
456    let mut instance_visual_scene = None;
457
458    for child in node.element_children() {
459        match child.tag_name().name() {
460            "instance_visual_scene" => {
461                instance_visual_scene = Some(parse_instance_visual_scene(child)?);
462            }
463            "instance_physics_scene" | "instance_kinematics_scene" => {
464                // warn!(
465                //     "<{}> child element in <{}> element is unsupported ({})",
466                //     child.tag_name().name(),
467                //     child.parent_element().unwrap().tag_name().name(),
468                //     child.node_location()
469                // );
470            }
471            "extra" => { /* skip */ }
472            _ => return Err(error::unexpected_child_elem(child)),
473        }
474    }
475
476    Ok(Scene {
477        instance_visual_scene,
478    })
479}
480
481fn parse_instance_visual_scene<'a>(node: xml::Node<'a, '_>) -> io::Result<InstanceVisualScene<'a>> {
482    debug_assert_eq!(node.tag_name().name(), "instance_visual_scene");
483    let url = node.parse_url("url")?;
484    Ok(InstanceVisualScene {
485        // sid: node.attribute("sid"),
486        // name: node.attribute("name"),
487        url,
488    })
489}
490
491pub(super) fn parse_library_visual_scenes<'a>(
492    cx: &mut Context<'a>,
493    node: xml::Node<'a, '_>,
494) -> io::Result<()> {
495    debug_assert_eq!(node.tag_name().name(), "library_visual_scenes");
496    // cx.library_visual_scenes.id = node.attribute("id");
497    // cx.library_visual_scenes.name = node.attribute("name");
498
499    for child in node.element_children() {
500        match child.tag_name().name() {
501            "visual_scene" => {
502                parse_visual_scene(child, &mut cx.library_visual_scenes.nodes)?;
503            }
504            "asset" | "extra" => { /* skip */ }
505            _ => return Err(error::unexpected_child_elem(child)),
506        }
507    }
508
509    // if visual_scenes.is_empty() {
510    //     error::one_or_more_elems(node, "visual_scene")?;
511    // }
512
513    Ok(())
514}
515
516fn parse_visual_scene<'a>(node: xml::Node<'a, '_>, nodes: &mut Vec<Node<'a>>) -> io::Result<()> {
517    debug_assert_eq!(node.tag_name().name(), "visual_scene");
518    let id = node.attribute("id");
519    let mut scene_nodes = vec![];
520    let this = Node {
521        id,
522        // name: node.attribute("name"),
523        ..Default::default()
524    };
525    let this_index = nodes.len();
526    nodes.push(this);
527
528    for child in node.element_children() {
529        match child.tag_name().name() {
530            "node" => {
531                scene_nodes.push(parse_node(child, nodes, this_index)?);
532            }
533            "evaluate_scene" => {
534                // warn!(
535                //     "<{}> child element in <{}> element is unsupported ({})",
536                //     child.tag_name().name(),
537                //     child.parent_element().unwrap().tag_name().name(),
538                //     child.node_location()
539                // );
540            }
541            "asset" | "extra" => { /* skip */ }
542            _ => return Err(error::unexpected_child_elem(child)),
543        }
544    }
545
546    Ok(())
547}
548
549/*
550The `<node>` element
551
552Attributes:
553- `id` (xs:ID, Optional)
554- `name` (xs:token, Optional)
555- `sid` (sid_type, Optional)
556- `type` (Enumeration, Optional)
557    The type of the <node> element. Valid values are JOINT or NODE.
558    The default is NODE.
559- `layer` (list_of_names_type, Optional)
560
561Child elements must appear in the following order if present:
562- `<asset>` (0 or 1)
563- transformation_elements (0 or more )
564    Any combination of the following transformation elements:
565    - `<lookat>`
566    - `<matrix>`
567    - `<rotate>`
568    - `<scale>`
569    - `<skew>`
570    - `<translate>`
571- `<instance_camera>` (0 or more)
572- `<instance_controller>` (0 or more)
573- `<instance_geometry>` (0 or more)
574- `<instance_light>` (0 or more)
575- `<instance_node>` (0 or more)
576- `<node>` (0 or more)
577- `<extra>` (0 or more)
578*/
579fn parse_node<'a>(
580    node: xml::Node<'a, '_>,
581    nodes: &mut Vec<Node<'a>>,
582    parent: usize,
583) -> io::Result<usize> {
584    debug_assert_eq!(node.tag_name().name(), "node");
585    let _ty: NodeType = node.parse_attribute("type")?.unwrap_or_default();
586    let this = Node {
587        // id: node.attribute("id"),
588        // name: node.attribute("name"),
589        // sid: node.attribute("sid"),
590        // ty,
591        parent: Some(parent),
592        ..Default::default()
593    };
594    let this_index = nodes.len();
595    nodes.push(this);
596    let mut transforms = vec![];
597
598    for child in node.element_children() {
599        match child.tag_name().name() {
600            "node" => {
601                let _c = parse_node(child, nodes, this_index)?;
602                // nodes[this_index].children.push(c);
603            }
604
605            // transformation
606            "lookat" => {
607                let content = xml::comma_to_period(child.trimmed_text());
608                let mut iter = xml::parse_float_array_exact(&content, 9);
609                // TODO: include in parse_float_array_exact?
610                let map_err = |e| {
611                    format_err!(
612                        "{e} in <{}> element ({})",
613                        child.tag_name().name(),
614                        child.text_location(),
615                    )
616                };
617                let t = [
618                    iter.next().unwrap().map_err(map_err)?,
619                    iter.next().unwrap().map_err(map_err)?,
620                    iter.next().unwrap().map_err(map_err)?,
621                    iter.next().unwrap().map_err(map_err)?,
622                    iter.next().unwrap().map_err(map_err)?,
623                    iter.next().unwrap().map_err(map_err)?,
624                    iter.next().unwrap().map_err(map_err)?,
625                    iter.next().unwrap().map_err(map_err)?,
626                    iter.next().unwrap().map_err(map_err)?,
627                ];
628                transforms.push(Transform::Lookat(t));
629            }
630            "matrix" => {
631                let content = xml::comma_to_period(child.trimmed_text());
632                let mut iter = xml::parse_float_array_exact(&content, 16);
633                // TODO: include in parse_float_array_exact?
634                let map_err = |e| {
635                    format_err!(
636                        "{e} in <{}> element ({})",
637                        child.tag_name().name(),
638                        child.text_location(),
639                    )
640                };
641                let t = [
642                    iter.next().unwrap().map_err(map_err)?,
643                    iter.next().unwrap().map_err(map_err)?,
644                    iter.next().unwrap().map_err(map_err)?,
645                    iter.next().unwrap().map_err(map_err)?,
646                    iter.next().unwrap().map_err(map_err)?,
647                    iter.next().unwrap().map_err(map_err)?,
648                    iter.next().unwrap().map_err(map_err)?,
649                    iter.next().unwrap().map_err(map_err)?,
650                    iter.next().unwrap().map_err(map_err)?,
651                    iter.next().unwrap().map_err(map_err)?,
652                    iter.next().unwrap().map_err(map_err)?,
653                    iter.next().unwrap().map_err(map_err)?,
654                    iter.next().unwrap().map_err(map_err)?,
655                    iter.next().unwrap().map_err(map_err)?,
656                    iter.next().unwrap().map_err(map_err)?,
657                    iter.next().unwrap().map_err(map_err)?,
658                ];
659                transforms.push(Transform::Matrix(t));
660            }
661            "rotate" => {
662                let content = xml::comma_to_period(child.trimmed_text());
663                let mut iter = xml::parse_float_array_exact(&content, 4);
664                // TODO: include in parse_float_array_exact?
665                let map_err = |e| {
666                    format_err!(
667                        "{e} in <{}> element ({})",
668                        child.tag_name().name(),
669                        child.text_location(),
670                    )
671                };
672                let t = [
673                    iter.next().unwrap().map_err(map_err)?,
674                    iter.next().unwrap().map_err(map_err)?,
675                    iter.next().unwrap().map_err(map_err)?,
676                    iter.next().unwrap().map_err(map_err)?,
677                ];
678                transforms.push(Transform::Rotate(t));
679            }
680            "scale" => {
681                let content = xml::comma_to_period(child.trimmed_text());
682                let mut iter = xml::parse_float_array_exact(&content, 3);
683                // TODO: include in parse_float_array_exact?
684                let map_err = |e| {
685                    format_err!(
686                        "{e} in <{}> element ({})",
687                        child.tag_name().name(),
688                        child.text_location(),
689                    )
690                };
691                let t = [
692                    iter.next().unwrap().map_err(map_err)?,
693                    iter.next().unwrap().map_err(map_err)?,
694                    iter.next().unwrap().map_err(map_err)?,
695                ];
696                transforms.push(Transform::Scale(t));
697            }
698            "skew" => {
699                let content = xml::comma_to_period(child.trimmed_text());
700                let mut iter = xml::parse_float_array_exact(&content, 7);
701                // TODO: include in parse_float_array_exact?
702                let map_err = |e| {
703                    format_err!(
704                        "{e} in <{}> element ({})",
705                        child.tag_name().name(),
706                        child.text_location(),
707                    )
708                };
709                let t = [
710                    iter.next().unwrap().map_err(map_err)?,
711                    iter.next().unwrap().map_err(map_err)?,
712                    iter.next().unwrap().map_err(map_err)?,
713                    iter.next().unwrap().map_err(map_err)?,
714                    iter.next().unwrap().map_err(map_err)?,
715                    iter.next().unwrap().map_err(map_err)?,
716                    iter.next().unwrap().map_err(map_err)?,
717                ];
718                transforms.push(Transform::Skew(t));
719            }
720            "translate" => {
721                let content = xml::comma_to_period(child.trimmed_text());
722                let mut iter = xml::parse_float_array_exact(&content, 3);
723                // TODO: include in parse_float_array_exact?
724                let map_err = |e| {
725                    format_err!(
726                        "{e} in <{}> element ({})",
727                        child.tag_name().name(),
728                        child.text_location(),
729                    )
730                };
731                let t = [
732                    iter.next().unwrap().map_err(map_err)?,
733                    iter.next().unwrap().map_err(map_err)?,
734                    iter.next().unwrap().map_err(map_err)?,
735                ];
736                transforms.push(Transform::Translate(t));
737            }
738
739            // instances
740            "instance_camera" => {}
741            "instance_controller" => {}
742            "instance_geometry" => {
743                nodes[this_index]
744                    .instance_geometry
745                    .push(parse_instance_geometry(child)?);
746            }
747            "instance_light" => {}
748            "instance_node" => {}
749
750            _ => {}
751        }
752    }
753
754    if !transforms.is_empty() {
755        nodes[this_index].transform = Transform::calculate_transform(&transforms);
756    }
757
758    Ok(this_index)
759}
760
761impl FromStr for NodeType {
762    type Err = io::Error;
763
764    fn from_str(s: &str) -> Result<Self, Self::Err> {
765        Ok(match s {
766            "NODE" => Self::Node,
767            "JOINT" => Self::Joint,
768            _ => bail!("unknown note type {:?}", s),
769        })
770    }
771}
772
773fn parse_instance_geometry<'a>(node: xml::Node<'a, '_>) -> io::Result<InstanceGeometry<'a>> {
774    debug_assert_eq!(node.tag_name().name(), "instance_geometry");
775    let url = node.parse_url("url")?;
776    let mut materials = BTreeMap::new();
777
778    for child in node.element_children() {
779        match child.tag_name().name() {
780            "bind_material" => {
781                parse_bind_material(child, &mut materials)?;
782            }
783            "extra" => { /* skip */ }
784            _ => return Err(error::unexpected_child_elem(child)),
785        }
786    }
787
788    Ok(InstanceGeometry {
789        // sid: node.attribute("sid"),
790        // name: node.attribute("name"),
791        url,
792        materials,
793    })
794}
795
796/*
797The <bind_material> element
798
799Child elements must appear in the following order if present:
800- `<param>` (core) (0 or more)
801- `<technique_common>` (1)
802- `<technique>` (core) (0 or more)
803- `<extra>` (0 or more)
804
805Child Elements for <bind_material> / <technique_common>
806- `<instance_material>` (geometry) (1 or more)
807*/
808fn parse_bind_material<'a>(
809    node: xml::Node<'a, '_>,
810    materials: &mut BTreeMap<&'a str, SemanticMappingTable<'a>>,
811) -> io::Result<()> {
812    debug_assert_eq!(node.tag_name().name(), "bind_material");
813    for child in node.element_children() {
814        match child.tag_name().name() {
815            "technique_common" => {
816                for instance_mat_node in child.element_children() {
817                    match instance_mat_node.tag_name().name() {
818                        "instance_material" => {
819                            let table = parse_instance_material(instance_mat_node)?;
820                            materials.insert(table.symbol, table);
821                        }
822                        _ => return Err(error::unexpected_child_elem(instance_mat_node)),
823                    }
824                }
825            }
826            "param" | "technique" | "extra" => { /* skip */ }
827            _ => return Err(error::unexpected_child_elem(child)),
828        }
829        // TODO
830    }
831    Ok(())
832}
833
834/*
835The <instance_material> element (geometry)
836
837Attributes:
838- `sid` (sid_type, Optional)
839- `name` (xs:token, Optional)
840- `target` (xs:anyURI, Required)
841- `symbol` (xs:NCName, Required)
842
843Child elements must appear in the following order if present:
844- `<bind>` (FX) (0 or more)
845- `<bind_vertex_input>` (0 or more)
846- `<extra>` (0 or more)
847*/
848fn parse_instance_material<'a>(node: xml::Node<'a, '_>) -> io::Result<SemanticMappingTable<'a>> {
849    debug_assert_eq!(node.tag_name().name(), "instance_material");
850    let target = node.parse_url("target")?;
851    let symbol = node.required_attribute("symbol")?;
852    // let mut map = HashMap::default();
853
854    for child in node.element_children() {
855        match child.tag_name().name() {
856            "bind_vertex_input" => {
857                /*
858                The <bind_vertex_input> element
859
860                Attributes:
861                - `semantic` (xs:NCName, Required)
862                - `input_semantic` (xs:NCName, Required)
863                - `input_set` (uint_type, Optional)
864                */
865
866                let _semantic = child.required_attribute("semantic")?;
867                let _input_semantic: InputSemantic =
868                    child.parse_required_attribute("input_semantic")?;
869                let _input_set: u32 = child.parse_attribute("input_set")?.unwrap_or(0);
870
871                // map.insert(
872                //     semantic,
873                //     InputSemanticMapEntry {
874                //         input_semantic,
875                //         input_set,
876                //     },
877                // );
878            }
879            "bind" => {
880                // warn!(
881                //     "<{}> child element in <{}> element is unsupported ({})",
882                //     child.tag_name().name(),
883                //     child.parent_element().unwrap().tag_name().name(),
884                //     child.node_location()
885                // );
886            }
887            "extra" => { /* skip */ }
888            _ => return Err(error::unexpected_child_elem(child)),
889        }
890    }
891
892    Ok(SemanticMappingTable {
893        target,
894        symbol,
895        // map,
896    })
897}