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 }
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#[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#[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
77pub struct ColladaDocument {
84 pub root_element: xml::Element, }
87
88impl ColladaDocument {
89 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 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 shininess,
257 specular,
258 }
259 }
260
261 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 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 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 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 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 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 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 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 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 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 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 let joint_weights = match self.get_skeletons() {
660 Some(skeletons) => {
661 let skeleton = &skeletons[0];
662 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 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 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 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}