collada/
document.rs

1use crate::obj::*;
2use crate::utils::*;
3use std::collections::HashMap;
4use std::fs::File;
5use std::io::Read;
6use std::path::Path;
7use std::str::FromStr;
8use xml::Element;
9use xml::Xml::CharacterNode;
10
11use vecmath;
12use xml;
13
14use crate::{
15    Animation, BindData, BindDataSet, Joint, JointIndex, Skeleton, VertexWeight,
16    ROOT_JOINT_PARENT_INDEX,
17};
18
19enum GeometryBindingType {
20    Polylist,
21    Triangles,
22    // TODO types below are not implemented
23    // Lines,
24    // Linestrips,
25    // Polygons,
26    // Trifans,
27    // Tristrips,
28}
29
30impl GeometryBindingType {
31    fn name(&self) -> &'static str {
32        match *self {
33            GeometryBindingType::Polylist => "polylist",
34            GeometryBindingType::Triangles => "triangles",
35        }
36    }
37}
38
39///
40/// Commonly used shading techniques
41/// Holds a description of the textures, samplers, shaders, parameters, and
42/// passes necessary for rendering this effect using one method.
43///
44#[derive(Clone, Debug, PartialEq)]
45pub struct PhongEffect {
46    pub emission: [f32; 4],
47    pub ambient: [f32; 4],
48    pub diffuse: Diffuse,
49    pub specular: Specular,
50    pub shininess: f32,
51}
52
53///
54/// Can be a plain color or point to a texture in the images library
55///
56#[derive(Clone, Debug, PartialEq)]
57pub enum Diffuse {
58    Color([f32; 4]),
59    Texture(String),
60}
61
62type Specular = Diffuse;
63
64#[derive(Clone, Debug, PartialEq)]
65pub struct LambertEffect {
66    pub emission: [f32; 4],
67    pub diffuse: Diffuse,
68    pub index_of_refraction: f32,
69}
70
71#[derive(Clone, Debug, PartialEq)]
72pub enum MaterialEffect {
73    Phong(PhongEffect),
74    Lambert(LambertEffect),
75}
76
77// TODO: Add more effect types and then unify them under a technique enum.
78// Lambert
79// Blinn
80// Constant
81//
82
83pub struct ColladaDocument {
84    pub root_element: xml::Element, // TODO figure out how to cache skeletal and skinning data, as we need to
85                                    // access them multiple times
86}
87
88impl ColladaDocument {
89    ///
90    /// Construct a ColladaDocument for the XML document at the given path
91    ///
92    pub fn from_path(path: &Path) -> Result<ColladaDocument, &'static str> {
93        let file_result = File::open(path);
94
95        let mut file = match file_result {
96            Ok(file) => file,
97            Err(_) => return Err("Failed to open COLLADA file at path."),
98        };
99
100        let mut xml_string = String::new();
101        match file.read_to_string(&mut xml_string) {
102            Ok(_) => {}
103            Err(_) => return Err("Failed to read COLLADA file."),
104        };
105
106        ColladaDocument::from_str(&xml_string)
107    }
108
109    ///
110    /// Construct a ColladaDocument from an XML string
111    ///
112    pub fn from_str(xml_string: &str) -> Result<ColladaDocument, &'static str> {
113        match xml_string.parse() {
114            Ok(root_element) => Ok(ColladaDocument { root_element }),
115            Err(_) => Err("Error while parsing COLLADA document."),
116        }
117    }
118
119    fn get_color(el: &Element) -> Option<[f32; 4]> {
120        let v: Vec<f32> = parse_string_to_vector(el.content_str().as_str());
121        if v.len() == 4 {
122            Some([v[0], v[1], v[2], v[3]])
123        } else {
124            None
125        }
126    }
127
128    fn get_lambert(&self, lamb: &Element, ns: Option<&str>) -> LambertEffect {
129        let emission_color = lamb
130            .get_child("emission", ns)
131            .expect("lambert is missing emission")
132            .get_child("color", ns)
133            .expect("emission is missing color");
134        let emission =
135            ColladaDocument::get_color(emission_color).expect("could not get emission color.");
136        let diffuse_element = lamb
137            .get_child("diffuse", ns)
138            .expect("lambert is missing diffuse");
139
140        let diffuse_texture = diffuse_element.get_child("texture", ns);
141
142        let diffuse;
143        if let Some(texture) = diffuse_texture {
144            diffuse = Diffuse::Texture(
145                texture
146                    .get_attribute("texture", None)
147                    .expect("No texture attribute on texture")
148                    .to_string(),
149            );
150        } else {
151            let diffuse_element_color = diffuse_element
152                .get_child("color", ns)
153                .expect("diffuse is missing color");
154            let diffuse_color = ColladaDocument::get_color(diffuse_element_color)
155                .expect("could not get diffuse color.");
156            diffuse = Diffuse::Color(diffuse_color);
157        }
158
159        let index_of_refraction: f32 = lamb
160            .get_child("index_of_refraction", ns)
161            .expect("lambert is missing index_of_refraction")
162            .get_child("float", ns)
163            .expect("index_of_refraction is missing float")
164            .content_str()
165            .as_str()
166            .parse()
167            .ok()
168            .expect("could not parse index_of_refraction");
169
170        LambertEffect {
171            diffuse,
172            emission,
173            index_of_refraction,
174        }
175    }
176
177    fn get_phong(&self, phong: &Element, ns: Option<&str>) -> PhongEffect {
178        let emission_color = phong
179            .get_child("emission", ns)
180            .expect("phong is missing emission")
181            .get_child("color", ns)
182            .expect("emission is missing color");
183        let emission =
184            ColladaDocument::get_color(emission_color).expect("could not get emission color.");
185        let ambient_color = phong
186            .get_child("ambient", ns)
187            .expect("phong is missing ambient")
188            .get_child("color", ns)
189            .expect("ambient is missing color");
190        let ambient =
191            ColladaDocument::get_color(ambient_color).expect("could not get ambient color.");
192        
193        let diffuse_element = phong
194            .get_child("diffuse", ns)
195            .expect("phong is missing diffuse");
196
197        let diffuse_texture = diffuse_element.get_child("texture", ns);
198
199        let diffuse;
200        if let Some(texture) = diffuse_texture {
201            diffuse = Diffuse::Texture(
202                texture
203                    .get_attribute("texture", None)
204                    .expect("No texture attribute on texture")
205                    .to_string(),
206            );
207        } else {
208            let diffuse_element_color = diffuse_element
209                .get_child("color", ns)
210                .expect("diffuse is missing color");
211            let diffuse_color = ColladaDocument::get_color(diffuse_element_color)
212                .expect("could not get diffuse color.");
213            diffuse = Diffuse::Color(diffuse_color);
214        }
215
216
217    let specular_element = phong
218        .get_child("specular", ns)
219        .expect("phong is missing specular");
220
221    let specular_texture = specular_element.get_child("texture", ns);
222
223    let specular;
224    if let Some(texture) = specular_texture {
225        specular = Specular::Texture(
226            texture
227                .get_attribute("texture", None)
228                .expect("No texture attribute on texture")
229                .to_string(),
230        );
231    } else {
232        let specular_element_color = specular_element
233            .get_child("color", ns)
234            .expect("specular is missing color");
235        let specular_color = ColladaDocument::get_color(specular_element_color)
236            .expect("could not get specular color.");
237        specular = Specular::Color(specular_color);
238    }
239
240        let shininess: f32 = phong
241            .get_child("shininess", ns)
242            .expect("phong is missing shininess")
243            .get_child("float", ns)
244            .expect("shininess is missing float")
245            .content_str()
246            .as_str()
247            .parse()
248            .ok()
249            .expect("could not parse shininess");
250        
251        PhongEffect {
252            ambient,
253            diffuse,
254            emission,
255            // index_of_refraction,
256            shininess,
257            specular,
258        }
259    }
260
261    ///
262    /// Returns the library of effects.
263    /// Current only supports Phong shading.
264    ///
265    pub fn get_effect_library(&self) -> HashMap<String, MaterialEffect> {
266        let ns = self.get_ns();
267        let lib_effs = self
268            .root_element
269            .get_child("library_effects", ns)
270            .expect("Could not get library_effects from the document.");
271        lib_effs
272            .get_children("effect", ns)
273            .flat_map(|el: &Element| -> Option<(String, MaterialEffect)> {
274                let id = el
275                    .get_attribute("id", None)
276                    .unwrap_or_else(|| panic!("effect is missing its id. {:#?}", el));
277                let prof = el.get_child("profile_COMMON", ns)?;
278                let tech = prof.get_child("technique", ns)?;
279                let phong = tech.get_child("phong", ns);
280                if let Some(p) = phong {
281                    let phong_effect = self.get_phong(p, ns);
282                    return Some((id.to_string(), MaterialEffect::Phong(phong_effect)));
283                };
284                let lambert = tech.get_child("lambert", ns);
285                if let Some(lam) = lambert {
286                    let lambert_effect = self.get_lambert(lam, ns);
287                    return Some((id.to_string(), MaterialEffect::Lambert(lambert_effect)));
288                };
289
290                None
291            })
292            .collect()
293    }
294
295    pub fn get_material_to_effect(&self) -> HashMap<String, String> {
296        let ns = self.get_ns();
297        let lib_mats = self
298            .root_element
299            .get_child("library_materials", ns)
300            .expect("Could not get library_materials from the document");
301        lib_mats
302            .get_children("material", ns)
303            .flat_map(|el| {
304                let id = el
305                    .get_attribute("id", None)
306                    .unwrap_or_else(|| panic!("material is missing its id. {:#?}", el));
307                let mut url: String = el
308                    .get_child("instance_effect", ns)
309                    .expect("could not get material instance_effect")
310                    .get_attribute("url", None)
311                    .expect("could not get material instance_effect url attribute")
312                    .to_string();
313                if url.remove(0) == '#' {
314                    Some((id.to_string(), url))
315                } else {
316                    None
317                }
318            })
319            .collect()
320    }
321
322    ///
323    /// Returns a hashmap of <imageid, filename>
324    ///
325    pub fn get_images(&self) -> HashMap<String, String> {
326        let ns = self.get_ns();
327        let lib_images = self
328            .root_element
329            .get_child("library_images", ns)
330            .expect("Could not get library_images from the document");
331        lib_images
332            .get_children("image", ns)
333            .flat_map(|el| {
334                let id = el
335                    .get_attribute("id", None)
336                    .expect(&format!("image is missing its id. {:#?}", el))
337                    .to_string();
338                let file_name = el
339                    .get_child("init_from", ns)
340                    .expect("Could not get image from the element")
341                    .content_str();
342                Some((id, file_name))
343            })
344            .collect()
345    }
346
347    ///
348    /// Return a vector of all Animations in the Collada document
349    ///
350    pub fn get_animations(&self) -> Option<Vec<Animation>> {
351        match self
352            .root_element
353            .get_child("library_animations", self.get_ns())
354        {
355            Some(library_animations) => {
356                let animations = library_animations.get_children("animation", self.get_ns());
357                Some(animations.filter_map(|a| self.get_animation(a)).collect())
358            }
359            None => None,
360        }
361    }
362
363    ///
364    /// Construct an Animation struct for the given <animation> element if possible
365    ///
366    fn get_animation(&self, animation_element: &Element) -> Option<Animation> {
367        let channel_element = animation_element
368            .get_child("channel", self.get_ns())
369            .expect("Missing channel element in animation element");
370
371        let target = channel_element
372            .get_attribute("target", None)
373            .expect("Missing target attribute in animation channel element");
374
375        let sampler_element = animation_element
376            .get_child("sampler", self.get_ns())
377            .expect("Missing sampler element in animation element");
378
379        // Note: Assuming INPUT for animation is 'time'
380        let time_input = self
381            .get_input(sampler_element, "INPUT")
382            .expect("Missing input element for animation INPUT (sample time)");
383
384        let sample_times = self
385            .get_array_for_input(animation_element, time_input)
386            .expect("Missing / invalid source for animation INPUT");
387
388        // Note: Assuming OUTPUT for animation is a pose matrix
389        let pose_input = self
390            .get_input(sampler_element, "OUTPUT")
391            .expect("Missing input element for animation OUTPUT (pose transform)");
392
393        let sample_poses_flat = self
394            .get_array_for_input(animation_element, pose_input)
395            .expect("Missing / invalid source for animation OUTPUT");
396
397        // Convert flat array of floats into array of matrices
398        let sample_poses = to_matrix_array(sample_poses_flat);
399
400        Some(Animation {
401            target: target.to_string(),
402            sample_times,
403            sample_poses,
404        })
405    }
406
407    ///
408    /// Populate and return an ObjSet for the meshes in the Collada document
409    ///
410    pub fn get_obj_set(&self) -> Option<ObjSet> {
411        let library_geometries = (self
412            .root_element
413            .get_child("library_geometries", self.get_ns()))?;
414        let geometries = library_geometries.get_children("geometry", self.get_ns());
415        let objects = geometries.filter_map(|g| self.get_object(g)).collect();
416
417        Some(ObjSet {
418            material_library: None,
419            objects,
420        })
421    }
422
423    ///
424    /// Populate and return a BindDataSet from the Collada document
425    ///
426    pub fn get_bind_data_set(&self) -> Option<BindDataSet> {
427        let library_controllers = (self
428            .root_element
429            .get_child("library_controllers", self.get_ns()))?;
430        let controllers = library_controllers.get_children("controller", self.get_ns());
431        let bind_data = controllers.filter_map(|c| self.get_bind_data(c)).collect();
432        Some(BindDataSet { bind_data })
433    }
434
435    ///
436    ///
437    ///
438    pub fn get_skeletons(&self) -> Option<Vec<Skeleton>> {
439        let library_visual_scenes = (self
440            .root_element
441            .get_child("library_visual_scenes", self.get_ns()))?;
442        let visual_scene = (library_visual_scenes.get_child("visual_scene", self.get_ns()))?;
443
444        let bind_data_set = (self.get_bind_data_set())?;
445
446        let skeleton_ids: Vec<&str> = pre_order_iter(visual_scene)
447            .filter(|e| e.name == "skeleton")
448            .filter_map(|s| {
449                if let CharacterNode(ref id) = s.children[0] {
450                    Some(&id[..])
451                } else {
452                    None
453                }
454            })
455            .map(|id| id.trim_start_matches('#'))
456            .collect();
457
458        if skeleton_ids.is_empty() {
459            return None;
460        }
461
462        let skeletons = pre_order_iter(visual_scene)
463            .filter(|e| e.name == "node")
464            .filter(|e| has_attribute_with_value(e, "id", skeleton_ids[0]))
465            .filter_map(|e| self.get_skeleton(e, &bind_data_set.bind_data[0]))
466            .collect();
467
468        Some(skeletons)
469    }
470
471    fn get_skeleton(&self, root_element: &Element, bind_data: &BindData) -> Option<Skeleton> {
472        let mut parent_index_stack: Vec<JointIndex> = vec![ROOT_JOINT_PARENT_INDEX];
473        let mut joints = Vec::new();
474        let mut bind_poses = Vec::new();
475        for (joint_index, (joint_element, depth)) in pre_order_with_depth_iter(root_element)
476            .filter(|&(e, _)| e.name == "node" && has_attribute_with_value(e, "type", "JOINT"))
477            .enumerate()
478        {
479            // If our depth decreases after visiting a leaf node, pop indices off the stack
480            // until it matches our depth
481            while depth < parent_index_stack.len() - 1 {
482                parent_index_stack.pop();
483            }
484
485            let joint_name = joint_element.get_attribute("id", None).unwrap().to_string();
486
487            let mut joint_names_with_bind_pose = bind_data
488                .joint_names
489                .iter()
490                .zip(bind_data.inverse_bind_poses.iter());
491            let inverse_bind_pose =
492                match joint_names_with_bind_pose.find(|&(name, _)| *name == joint_name) {
493                    Some((_, pose)) => *pose,
494                    _ => vecmath::mat4_id(),
495                };
496
497            joints.push(Joint {
498                inverse_bind_pose,
499                name: joint_name,
500                parent_index: *parent_index_stack.last().unwrap(),
501            });
502
503            let pose_matrix_element = (joint_element.get_child("matrix", self.get_ns()))?;
504            let pose_matrix_array = (get_array_content(pose_matrix_element))?;
505            let mut pose_matrix = vecmath::mat4_id();
506            for (&array_value, matrix_value) in pose_matrix_array
507                .iter()
508                .zip(pose_matrix.iter_mut().flat_map(|n| n.iter_mut()))
509            {
510                *matrix_value = array_value;
511            }
512
513            bind_poses.push(pose_matrix);
514
515            parent_index_stack.push(joint_index as JointIndex);
516        }
517
518        Some(Skeleton { joints, bind_poses })
519    }
520
521    fn get_bind_data(&self, controller_element: &xml::Element) -> Option<BindData> {
522        let skeleton_name = controller_element.get_attribute("name", None);
523        let skin_element = controller_element.get_child("skin", self.get_ns())?;
524        let object_name = skin_element
525            .get_attribute("source", None)?
526            .trim_start_matches('#');
527
528        let vertex_weights_element = (skin_element.get_child("vertex_weights", self.get_ns()))?;
529        let vertex_weights = (self.get_vertex_weights(vertex_weights_element))?;
530
531        let joints_element = (skin_element.get_child("joints", self.get_ns()))?;
532
533        let joint_input = (self.get_input(joints_element, "JOINT"))?;
534        let joint_names = (self.get_array_for_input(skin_element, joint_input))?;
535
536        let weights_input = (self.get_input(vertex_weights_element, "WEIGHT"))?;
537        let weights = (self.get_array_for_input(skin_element, weights_input))?;
538
539        let inv_bind_matrix_input = (self.get_input(joints_element, "INV_BIND_MATRIX"))?;
540
541        let inverse_bind_poses =
542            to_matrix_array((self.get_array_for_input(skin_element, inv_bind_matrix_input))?);
543
544        Some(BindData {
545            object_name: object_name.to_string(),
546            skeleton_name: skeleton_name.map(|s| s.to_string()),
547            joint_names,
548            inverse_bind_poses,
549            weights,
550            vertex_weights,
551        })
552    }
553
554    fn get_vertex_weights(
555        &self,
556        vertex_weights_element: &xml::Element,
557    ) -> Option<Vec<VertexWeight>> {
558        let joint_index_offset = (self.get_input_offset(vertex_weights_element, "JOINT"))?;
559        let weight_index_offset = (self.get_input_offset(vertex_weights_element, "WEIGHT"))?;
560
561        let vcount_element = (vertex_weights_element.get_child("vcount", self.get_ns()))?;
562        let weights_per_vertex: Vec<usize> = (get_array_content(vcount_element))?;
563        let input_count = vertex_weights_element
564            .get_children("input", self.get_ns())
565            .count();
566
567        let v_element = (vertex_weights_element.get_child("v", self.get_ns()))?;
568        let joint_weight_indices: Vec<usize> = (get_array_content(v_element))?;
569        let mut joint_weight_iter = joint_weight_indices.chunks(input_count);
570
571        let mut vertex_indices: Vec<usize> = Vec::new();
572        for (index, n) in weights_per_vertex.iter().enumerate() {
573            for _ in 0..*n {
574                vertex_indices.push(index);
575            }
576        }
577
578        let vertex_weights = vertex_indices
579            .iter()
580            .filter_map(|vertex_index| {
581                joint_weight_iter.next().map(|joint_weight| VertexWeight {
582                    vertex: *vertex_index,
583                    joint: joint_weight[joint_index_offset] as JointIndex,
584                    weight: joint_weight[weight_index_offset],
585                })
586            })
587            .collect();
588
589        Some(vertex_weights)
590    }
591
592    fn get_object(&self, geometry_element: &xml::Element) -> Option<Object> {
593        let id = (geometry_element.get_attribute("id", None))?;
594        let name = (geometry_element.get_attribute("name", None))?;
595        let mesh_element = (geometry_element.get_child("mesh", self.get_ns()))?;
596        let mesh = (self.get_mesh_elements(mesh_element))?;
597
598        let mut first_primitive_element = None;
599        'find_primitive: for t in [
600            GeometryBindingType::Polylist,
601            GeometryBindingType::Triangles,
602        ]
603        .iter()
604        {
605            first_primitive_element = mesh_element.get_child(t.name(), self.get_ns());
606            if first_primitive_element.is_some() {
607                break 'find_primitive;
608            }
609        }
610        let first_primitive_element = first_primitive_element?;
611
612        let positions_input = (self.get_input(first_primitive_element, "VERTEX"))?;
613        let positions_array = (self.get_array_for_input(mesh_element, positions_input))?;
614        let positions: Vec<_> = positions_array
615            .chunks(3)
616            .map(|coords| Vertex {
617                x: coords[0],
618                y: coords[1],
619                z: coords[2],
620            })
621            .collect();
622
623        let normals = {
624            match self.get_input(first_primitive_element, "NORMAL") {
625                Some(normals_input) => {
626                    let normals_array = (self.get_array_for_input(mesh_element, normals_input))?;
627                    normals_array
628                        .chunks(3)
629                        .map(|coords| Normal {
630                            x: coords[0],
631                            y: coords[1],
632                            z: coords[2],
633                        })
634                        .collect()
635                }
636                None => Vec::new(),
637            }
638        };
639
640        let texcoords = {
641            match self.get_input(first_primitive_element, "TEXCOORD") {
642                Some(texcoords_input) => {
643                    let texcoords_array =
644                        (self.get_array_for_input(mesh_element, texcoords_input))?;
645                    texcoords_array
646                        .chunks(2)
647                        .map(|coords| TVertex {
648                            x: coords[0],
649                            y: coords[1],
650                        })
651                        .collect()
652                }
653                None => Vec::new(),
654            }
655        };
656
657        // TODO cache! also only if any skeleton
658
659        let joint_weights = match self.get_skeletons() {
660            Some(skeletons) => {
661                let skeleton = &skeletons[0];
662                // TODO cache bind_data_set
663                let bind_data_set = (self.get_bind_data_set())?;
664                let bind_data_opt = bind_data_set
665                    .bind_data
666                    .iter()
667                    .find(|bind_data| bind_data.object_name == id);
668
669                if let Some(bind_data) = bind_data_opt {
670                    // Build an array of joint weights for each vertex
671                    // Initialize joint weights array with no weights for any vertex
672                    let mut joint_weights = vec![
673                        JointWeights {
674                            joints: [0; 4],
675                            weights: [0.0; 4]
676                        };
677                        positions.len()
678                    ];
679
680                    for vertex_weight in bind_data.vertex_weights.iter() {
681                        let joint_name = &bind_data.joint_names[vertex_weight.joint as usize];
682                        let vertex_joint_weights: &mut JointWeights =
683                            &mut joint_weights[vertex_weight.vertex];
684
685                        if let Some((next_index, _)) = vertex_joint_weights
686                            .weights
687                            .iter()
688                            .enumerate()
689                            .find(|&(_, weight)| *weight == 0.0)
690                        {
691                            if let Some((joint_index, _)) = skeleton
692                                .joints
693                                .iter()
694                                .enumerate()
695                                .find(|&(_, j)| &j.name == joint_name)
696                            {
697                                vertex_joint_weights.joints[next_index] = joint_index;
698                                vertex_joint_weights.weights[next_index] =
699                                    bind_data.weights[vertex_weight.weight];
700                            } else {
701                                error!("Couldn't find joint: {}", joint_name);
702                            }
703                        } else {
704                            error!("Too many joint influences for vertex");
705                        }
706                    }
707                    joint_weights
708                } else {
709                    Vec::new()
710                }
711            }
712            None => Vec::new(),
713        };
714
715        Some(Object {
716            id: id.to_string(),
717            name: name.to_string(),
718            vertices: positions,
719            tex_vertices: texcoords,
720            normals,
721            joint_weights,
722            geometry: vec![Geometry {
723                smooth_shading_group: 0,
724                mesh,
725            }],
726        })
727    }
728
729    fn get_ns(&self) -> Option<&str> {
730        match self.root_element.ns {
731            Some(ref ns) => Some(&ns[..]),
732            None => None,
733        }
734    }
735
736    fn get_input_offset(&self, parent_element: &xml::Element, semantic: &str) -> Option<usize> {
737        let mut inputs = parent_element.get_children("input", self.get_ns());
738        let input: &Element = inputs.find(|i| {
739            if let Some(s) = i.get_attribute("semantic", None) {
740                s == semantic
741            } else {
742                false
743            }
744        })?;
745        input
746            .get_attribute("offset", None)
747            .expect("input is missing offest")
748            .parse::<usize>()
749            .ok()
750    }
751
752    ///
753    fn get_input<'a>(&'a self, parent: &'a Element, semantic: &str) -> Option<&'a Element> {
754        let mut inputs = parent.get_children("input", self.get_ns());
755        match inputs.find(|i| {
756            if let Some(s) = i.get_attribute("semantic", None) {
757                s == semantic
758            } else {
759                false
760            }
761        }) {
762            Some(e) => Some(e),
763            None => None,
764        }
765    }
766
767    fn get_input_source<'a>(
768        &'a self,
769        parent_element: &'a xml::Element,
770        input_element: &'a xml::Element,
771    ) -> Option<&'a xml::Element> {
772        let source_id = (input_element.get_attribute("source", None))?;
773
774        if let Some(element) = parent_element
775            .children
776            .iter()
777            .filter_map(|node| {
778                if let xml::Xml::ElementNode(ref e) = node {
779                    Some(e)
780                } else {
781                    None
782                }
783            })
784            .find(|e| {
785                if let Some(id) = e.get_attribute("id", None) {
786                    let id = "#".to_string() + id;
787                    id == source_id
788                } else {
789                    false
790                }
791            })
792        {
793            if element.name == "source" {
794                return Some(element);
795            } else {
796                let input = (element.get_child("input", self.get_ns()))?;
797                return self.get_input_source(parent_element, input);
798            }
799        }
800        None
801    }
802
803    fn get_array_for_input<T: FromStr>(&self, parent: &Element, input: &Element) -> Option<Vec<T>> {
804        let source = (self.get_input_source(parent, input))?;
805        let array_element =
806            (if let Some(float_array) = source.get_child("float_array", self.get_ns()) {
807                Some(float_array)
808            } else {
809                source.get_child("Name_array", self.get_ns())
810            })?;
811        get_array_content(array_element)
812    }
813
814    fn get_vertex_indices(&self, prim_element: &xml::Element) -> Option<Vec<VertexIndex>> {
815        let p_element = (prim_element.get_child("p", self.get_ns()))?;
816        let indices: Vec<usize> = (get_array_content(p_element))?;
817
818        let input_count = prim_element
819            .get_children("input", self.get_ns())
820            .filter(|c| {
821                if let Some(set) = c.get_attribute("set", None) {
822                    set == "0"
823                } else {
824                    true
825                }
826            })
827            .count();
828
829        let vertex_offset = (self.get_input_offset(prim_element, "VERTEX"))?;
830        let vertex_indices = indices
831            .chunks(input_count)
832            .map(|indices| indices[vertex_offset])
833            .collect();
834
835        Some(vertex_indices)
836    }
837
838    fn get_normal_indices(&self, prim_element: &xml::Element) -> Option<Vec<NormalIndex>> {
839        let p_element = (prim_element.get_child("p", self.get_ns()))?;
840        let indices: Vec<usize> = (get_array_content(p_element))?;
841
842        let input_count = prim_element
843            .get_children("input", self.get_ns())
844            .filter(|c| {
845                if let Some(set) = c.get_attribute("set", None) {
846                    set == "0"
847                } else {
848                    true
849                }
850            })
851            .count();
852
853        self.get_input_offset(prim_element, "NORMAL")
854            .map(|normal_offset| {
855                indices
856                    .chunks(input_count)
857                    .map(|indices| indices[normal_offset])
858                    .collect()
859            })
860    }
861
862    fn get_texcoord_indices(&self, prim_element: &xml::Element) -> Option<Vec<TextureIndex>> {
863        let p_element = (prim_element.get_child("p", self.get_ns()))?;
864        let indices: Vec<usize> = (get_array_content(p_element))?;
865
866        let input_count = prim_element
867            .get_children("input", self.get_ns())
868            .filter(|c| {
869                if let Some(set) = c.get_attribute("set", None) {
870                    set == "0"
871                } else {
872                    true
873                }
874            })
875            .count();
876
877        self.get_input_offset(prim_element, "TEXCOORD")
878            .map(|texcoord_offset| {
879                indices
880                    .chunks(input_count)
881                    .map(|indices| indices[texcoord_offset])
882                    .collect()
883            })
884    }
885
886    fn get_vtn_indices(&self, prim_element: &xml::Element) -> Option<Vec<VTNIndex>> {
887        let p_element = (prim_element.get_child("p", self.get_ns()))?;
888        let indices: Vec<usize> = (get_array_content(p_element))?;
889
890        let input_count = prim_element
891            .get_children("input", self.get_ns())
892            .filter(|c| {
893                if let Some(set) = c.get_attribute("set", None) {
894                    set == "0"
895                } else {
896                    true
897                }
898            })
899            .count();
900
901        let position_offset = (self.get_input_offset(prim_element, "VERTEX"))?;
902
903        let normal_offset_opt = self.get_input_offset(prim_element, "NORMAL");
904        let texcoord_offset_opt = self.get_input_offset(prim_element, "TEXCOORD");
905
906        let vtn_indices: Vec<VTNIndex> = indices
907            .chunks(input_count)
908            .map(|indices| {
909                let position_index = indices[position_offset];
910
911                let normal_index_opt =
912                    normal_offset_opt.map(|normal_offset| indices[normal_offset]);
913
914                let texcoord_index_opt =
915                    texcoord_offset_opt.map(|texcoord_offset| indices[texcoord_offset]);
916
917                (position_index, texcoord_index_opt, normal_index_opt)
918            })
919            .collect();
920
921        Some(vtn_indices)
922    }
923
924    fn get_material(&self, primitive_el: &xml::Element) -> Option<String> {
925        primitive_el
926            .get_attribute("material", None)
927            .map(|s| s.to_string())
928    }
929
930    fn get_mesh_elements(&self, mesh_element: &xml::Element) -> Option<Vec<PrimitiveElement>> {
931        let mut prims = vec![];
932        for child in &mesh_element.children {
933            match child {
934                xml::Xml::ElementNode(el) => {
935                    if el.name == GeometryBindingType::Polylist.name() {
936                        let shapes = self
937                            .get_polylist_shape(el)
938                            .expect("Polylist had no shapes.");
939                        let material = self.get_material(el);
940                        let polylist = Polylist { shapes, material };
941                        prims.push(PrimitiveElement::Polylist(polylist))
942                    } else if el.name == GeometryBindingType::Triangles.name() {
943                        let material = self.get_material(el);
944                        let triangles = self
945                            .get_triangles(el, material)
946                            .expect("Triangles had no indices.");
947                        prims.push(PrimitiveElement::Triangles(triangles))
948                    }
949                }
950                _ => {}
951            }
952        }
953
954        if prims.is_empty() {
955            None
956        } else {
957            Some(prims)
958        }
959    }
960
961    fn get_triangles(
962        &self,
963        triangles: &xml::Element,
964        material: Option<String>,
965    ) -> Option<Triangles> {
966        let count_str: &str = triangles.get_attribute("count", None)?;
967        let count = count_str.parse::<usize>().ok().unwrap();
968
969        let vertices = self.get_vertex_indices(triangles).map(|vertex_indices| {
970            let mut vertex_iter = vertex_indices.iter();
971            (0..count)
972                .map(|_| {
973                    (
974                        *vertex_iter.next().unwrap(),
975                        *vertex_iter.next().unwrap(),
976                        *vertex_iter.next().unwrap(),
977                    )
978                })
979                .collect()
980        })?;
981
982        let tex_vertices = self
983            .get_texcoord_indices(triangles)
984            .map(|texcoord_indices| {
985                let mut texcoord_iter = texcoord_indices.iter();
986                (0..count)
987                    .map(|_| {
988                        (
989                            *texcoord_iter.next().unwrap(),
990                            *texcoord_iter.next().unwrap(),
991                            *texcoord_iter.next().unwrap(),
992                        )
993                    })
994                    .collect()
995            });
996
997        let normals = self.get_normal_indices(triangles).map(|normal_indices| {
998            let mut normal_iter = normal_indices.iter();
999            (0..count)
1000                .map(|_| {
1001                    (
1002                        *normal_iter.next().unwrap(),
1003                        *normal_iter.next().unwrap(),
1004                        *normal_iter.next().unwrap(),
1005                    )
1006                })
1007                .collect()
1008        });
1009
1010        Some(Triangles {
1011            vertices,
1012            tex_vertices,
1013            normals,
1014            material,
1015        })
1016    }
1017
1018    fn get_polylist_shape(&self, polylist_element: &xml::Element) -> Option<Vec<Shape>> {
1019        let vtn_indices = (self.get_vtn_indices(polylist_element))?;
1020
1021        let vcount_element = (polylist_element.get_child("vcount", self.get_ns()))?;
1022        let vertex_counts: Vec<usize> = (get_array_content(vcount_element))?;
1023
1024        let mut vtn_iter = vtn_indices.iter();
1025        let shapes = vertex_counts
1026            .iter()
1027            .map(|vertex_count| {
1028                match *vertex_count {
1029                    1 => Shape::Point(*vtn_iter.next().unwrap()),
1030                    2 => Shape::Line(*vtn_iter.next().unwrap(), *vtn_iter.next().unwrap()),
1031                    3 => Shape::Triangle(
1032                        *vtn_iter.next().unwrap(),
1033                        *vtn_iter.next().unwrap(),
1034                        *vtn_iter.next().unwrap(),
1035                    ),
1036                    n => {
1037                        // Polys with more than 3 vertices not supported - try to advance and continue
1038                        // TODO attempt to triangle-fy? (take a look at wavefront_obj)
1039                        for _ in 0..n {
1040                            vtn_iter.next();
1041                        }
1042                        Shape::Point((0, None, None))
1043                    }
1044                }
1045            })
1046            .collect();
1047
1048        Some(shapes)
1049    }
1050}
1051
1052#[test]
1053fn test_get_obj_set() {
1054    let collada_document = ColladaDocument::from_path(Path::new("test_assets/test.dae")).unwrap();
1055    let obj_set = collada_document.get_obj_set().unwrap();
1056    assert_eq!(obj_set.objects.len(), 1);
1057
1058    let object = &obj_set.objects[0];
1059    assert_eq!(object.id, "BoxyWorm-mesh");
1060    assert_eq!(object.name, "BoxyWorm");
1061    assert_eq!(object.vertices.len(), 16);
1062    assert_eq!(object.tex_vertices.len(), 84);
1063    assert_eq!(object.normals.len(), 28);
1064    assert_eq!(object.geometry.len(), 1);
1065
1066    let geometry = &object.geometry[0];
1067
1068    let prim = &geometry.mesh[0];
1069    if let PrimitiveElement::Polylist(polylist) = prim {
1070        assert_eq!(polylist.shapes.len(), 28);
1071        let shape = polylist.shapes[1];
1072        if let Shape::Triangle((position_index, Some(texture_index), Some(normal_index)), _, _) =
1073            shape
1074        {
1075            assert_eq!(position_index, 7);
1076            assert_eq!(texture_index, 3);
1077            assert_eq!(normal_index, 1);
1078        } else {
1079            assert!(false);
1080        }
1081    }
1082}
1083
1084#[test]
1085fn test_get_obj_set_triangles_geometry() {
1086    let collada_document =
1087        ColladaDocument::from_path(Path::new("test_assets/test_cube_triangles_geometry.dae"))
1088            .unwrap();
1089    let obj_set = collada_document.get_obj_set().unwrap();
1090    assert_eq!(obj_set.objects.len(), 1);
1091
1092    let object = &obj_set.objects[0];
1093    assert_eq!(object.id, "Cube-mesh");
1094    assert_eq!(object.name, "Cube");
1095    assert_eq!(object.vertices.len(), 8);
1096    assert_eq!(object.normals.len(), 12);
1097    assert_eq!(object.geometry.len(), 1);
1098
1099    let geometry = &object.geometry[0];
1100
1101    let prim = &geometry.mesh[0];
1102    match prim {
1103        PrimitiveElement::Triangles(triangles) => {
1104            assert_eq!(triangles.vertices.len(), 12);
1105
1106            let position_index = triangles.vertices[1].0;
1107            assert_eq!(position_index, 7);
1108
1109            if let Some(ref normals) = triangles.normals {
1110                assert_eq!(normals.len(), 12);
1111
1112                let normal_index = normals[1].0;
1113                assert_eq!(normal_index, 1);
1114            } else {
1115                assert!(false, "Triangle is missing a normal.");
1116            }
1117        }
1118        x => {
1119            assert!(false, "Not a polylist: {:#?}", x);
1120        }
1121    }
1122}
1123
1124#[test]
1125fn test_get_bind_data_set() {
1126    let collada_document = ColladaDocument::from_path(Path::new("test_assets/test.dae")).unwrap();
1127    let bind_data_set = collada_document.get_bind_data_set().unwrap();
1128    let bind_data = &bind_data_set.bind_data[0];
1129
1130    assert_eq!(bind_data.object_name, "BoxyWorm-mesh");
1131    assert!(bind_data.skeleton_name.is_some());
1132    assert_eq!(bind_data.skeleton_name.as_ref().unwrap(), "BoxWormRoot");
1133    assert_eq!(bind_data.joint_names, ["Root", "UpperArm", "LowerArm"]);
1134    assert_eq!(bind_data.vertex_weights.len(), 29);
1135    assert_eq!(bind_data.weights.len(), 29);
1136    assert_eq!(bind_data.inverse_bind_poses.len(), 3);
1137}
1138
1139#[test]
1140fn test_get_skeletons() {
1141    let collada_document = ColladaDocument::from_path(Path::new("test_assets/test.dae")).unwrap();
1142    let skeletons = collada_document.get_skeletons().unwrap();
1143    assert_eq!(skeletons.len(), 1);
1144
1145    let skeleton = &skeletons[0];
1146    assert_eq!(skeleton.joints.len(), 4);
1147    assert_eq!(skeleton.bind_poses.len(), 4);
1148
1149    assert_eq!(skeleton.joints[0].name, "Root");
1150    assert_eq!(skeleton.joints[0].parent_index, ROOT_JOINT_PARENT_INDEX);
1151    assert!(skeleton.joints[0].inverse_bind_pose != vecmath::mat4_id());
1152
1153    assert_eq!(skeleton.joints[1].name, "UpperArm");
1154    assert_eq!(skeleton.joints[1].parent_index, 0);
1155    assert!(skeleton.joints[1].inverse_bind_pose != vecmath::mat4_id());
1156
1157    assert_eq!(skeleton.joints[2].name, "UpperArmDanglyBit");
1158    assert_eq!(skeleton.joints[2].parent_index, 1);
1159    assert_eq!(skeleton.joints[2].inverse_bind_pose, vecmath::mat4_id());
1160
1161    assert_eq!(skeleton.joints[3].name, "LowerArm");
1162    assert_eq!(skeleton.joints[3].parent_index, 0);
1163    assert!(skeleton.joints[3].inverse_bind_pose != vecmath::mat4_id());
1164}
1165
1166#[test]
1167fn test_get_animations() {
1168    let collada_document = ColladaDocument::from_path(Path::new("test_assets/test.dae")).unwrap();
1169    let animations = collada_document.get_animations().unwrap();
1170    assert_eq!(animations.len(), 4);
1171
1172    let animation = &animations[1];
1173    assert_eq!(animation.target, "UpperArm/transform");
1174    assert_eq!(animation.sample_times.len(), 4);
1175    assert_eq!(animation.sample_poses.len(), 4);
1176
1177    let animation = &animations[3];
1178    assert_eq!(animation.target, "LowerArm/transform");
1179    assert_eq!(animation.sample_times.len(), 4);
1180    assert_eq!(animation.sample_poses.len(), 4);
1181}
1182
1183#[test]
1184fn test_get_obj_set_noskeleton() {
1185    let collada_document =
1186        ColladaDocument::from_path(Path::new("test_assets/test_noskeleton.dae")).unwrap();
1187    collada_document.get_obj_set().unwrap();
1188}