1#![allow(clippy::wildcard_imports)] #![allow(clippy::many_single_char_names)]
7
8mod effect;
9mod error;
10mod geometry;
11mod image;
12mod instance;
13mod iter;
14mod material;
15mod scene;
16
17use std::{
18 cmp,
19 collections::{BTreeMap, HashMap},
20 fmt, io,
21 marker::PhantomData,
22 ops,
23 path::Path,
24 str::{self, FromStr},
25};
26
27use self::{effect::*, geometry::*, image::*, material::*, scene::*};
28use crate::{
29 common,
30 utils::{
31 float, hex,
32 utf16::decode_string,
33 xml::{self, XmlNodeExt},
34 },
35 Color4,
36};
37
38#[inline]
40pub fn from_slice(bytes: &[u8]) -> io::Result<common::Scene> {
41 from_slice_internal(bytes, None)
42}
43
44#[inline]
46pub fn from_str(s: &str) -> io::Result<common::Scene> {
47 from_str_internal(s, None)
48}
49
50#[inline]
51pub(crate) fn from_slice_internal(bytes: &[u8], path: Option<&Path>) -> io::Result<common::Scene> {
52 let bytes = &decode_string(bytes)?;
53 from_str_internal(bytes, path)
54}
55
56#[inline]
57pub(crate) fn from_str_internal(s: &str, path: Option<&Path>) -> io::Result<common::Scene> {
58 let xml = xml::Document::parse(s).map_err(crate::error::invalid_data)?;
59 let mut collada = Document::parse(&xml)?;
60 Ok(instance::build(&mut collada, path.and_then(Path::parent)))
61}
62
63trait Get<T> {
66 type Target;
67
68 fn get(&self, uri: &T) -> Option<&Self::Target>;
69}
70
71macro_rules! impl_get_by_uri {
72 ($ty:ty, $($field:ident).*) => {
73 impl<'a> Get<Uri<'a, $ty>> for Document<'a> {
74 type Target = $ty;
75
76 fn get(&self, index: &Uri<'a, $ty>) -> Option<&Self::Target> {
77 self.$($field).*.get(&*index.0)
78 }
79 }
80 };
81}
82
83impl_get_by_uri!(Accessor<'a>, library_geometries.accessors);
84impl_get_by_uri!(ArrayData<'a>, library_geometries.array_data);
85impl_get_by_uri!(Effect<'a>, library_effects.effects);
86impl_get_by_uri!(Geometry<'a>, library_geometries.geometries);
87impl_get_by_uri!(Image<'a>, library_images.images);
88impl_get_by_uri!(Material<'a>, library_materials.materials);
89
90struct Uri<'a, T>(&'a str, PhantomData<fn() -> T>);
91
92impl<'a, T> Uri<'a, T> {
93 fn parse(url: &'a str) -> io::Result<Self> {
94 if let Some(id) = url.strip_prefix('#') {
96 Ok(Self(id, PhantomData))
97 } else {
98 Err(format_err!("unknown reference format {:?}", url))
99 }
100 }
101
102 fn from_id(id: &'a str) -> Self {
103 Self(id, PhantomData)
104 }
105
106 fn cast<U>(self) -> Uri<'a, U> {
107 Uri(self.0, PhantomData)
108 }
109
110 fn as_str(&self) -> &'a str {
111 self.0
112 }
113}
114
115impl<T> PartialEq for Uri<'_, T> {
116 fn eq(&self, other: &Self) -> bool {
117 self.0 == other.0
118 }
119}
120
121impl<T> Eq for Uri<'_, T> {}
122
123impl<T, S> PartialEq<S> for Uri<'_, T>
124where
125 S: ?Sized + AsRef<str>,
126{
127 fn eq(&self, other: &S) -> bool {
128 self.0 == other.as_ref()
129 }
130}
131
132impl<T> PartialEq<Uri<'_, T>> for str {
133 #[inline]
134 fn eq(&self, other: &Uri<'_, T>) -> bool {
135 self == other.0
136 }
137}
138
139impl<T> PartialEq<Uri<'_, T>> for &str {
140 #[inline]
141 fn eq(&self, other: &Uri<'_, T>) -> bool {
142 *self == other.0
143 }
144}
145
146trait ColladaXmlNodeExt<'a, 'input> {
147 fn parse_url<T>(&self, name: &str) -> io::Result<Uri<'a, T>>;
148 }
150
151impl<'a, 'input> ColladaXmlNodeExt<'a, 'input> for xml::Node<'a, 'input> {
152 fn parse_url<T>(&self, name: &str) -> io::Result<Uri<'a, T>> {
153 let url = self.required_attribute(name)?;
154 Uri::parse(url).map_err(|e| {
155 format_err!(
156 "{} in {} attribute of <{}> element at {}",
157 e,
158 name,
159 self.tag_name().name(),
160 self.attr_value_location(name),
161 )
162 })
163 }
164
165 }
181
182#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
183struct Version {
184 minor: u32,
185 patch: u32,
186}
187
188impl Version {
189 const MIN: Self = Self::new(4, 0);
190
191 const fn new(minor: u32, patch: u32) -> Self {
192 Self { minor, patch }
193 }
194 fn is_1_4(self) -> bool {
195 self >= Self::new(4, 0) && self < Self::new(5, 0)
196 }
197}
198
199impl FromStr for Version {
200 type Err = io::Error;
201
202 fn from_str(s: &str) -> Result<Self, Self::Err> {
203 (|| {
204 let mut digits = s.splitn(3, '.');
205 let major = digits.next()?;
206 if major != "1" {
207 return None;
208 }
209 let minor = digits.next()?.parse().ok()?;
210 let patch = digits.next()?.parse().ok()?;
211 Some(Self::new(minor, patch))
212 })()
213 .ok_or_else(|| format_err!("unrecognized version format {:?}", s))
214 }
215}
216
217impl fmt::Display for Version {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 write!(f, "1.{}.{}", self.minor, self.patch)
220 }
221}
222
223struct Context<'a> {
224 version: Version,
225 asset: Asset,
226 library_effects: LibraryEffects<'a>,
227 library_geometries: LibraryGeometries<'a>,
228 library_images: LibraryImages<'a>,
229 library_materials: LibraryMaterials<'a>,
230 library_visual_scenes: LibraryVisualScenes<'a>,
231 scene: Scene<'a>,
232}
233
234struct Document<'a> {
235 asset: Asset,
236 library_effects: LibraryEffects<'a>,
237 library_geometries: LibraryGeometries<'a>,
238 library_images: LibraryImages<'a>,
239 library_materials: LibraryMaterials<'a>,
240 library_visual_scenes: LibraryVisualScenes<'a>,
241 scene: Scene<'a>,
242}
243
244impl<'a> Document<'a> {
245 fn parse(doc: &'a xml::Document<'_>) -> io::Result<Self> {
281 let node = doc.root_element();
282 if node.tag_name().name() != "COLLADA" {
283 bail!("root element is not <COLLADA>");
284 }
285
286 let version: Version = node.required_attribute("version")?.parse()?;
287 if version < Version::MIN {
288 bail!("collada schema version {} is not supported", version);
289 };
290 let mut cx = Context {
293 version,
294 asset: Asset {
295 unit: DEFAULT_UNIT_SIZE,
296 },
297 library_effects: LibraryEffects::default(),
298 library_geometries: LibraryGeometries::default(),
299 library_images: LibraryImages::default(),
300 library_materials: LibraryMaterials::default(),
301 library_visual_scenes: LibraryVisualScenes::default(),
302 scene: Scene::default(),
303 };
304
305 for node in node.element_children() {
306 match node.tag_name().name() {
307 "library_effects" => {
308 parse_library_effects(&mut cx, node)?;
309 }
310 "library_geometries" => {
311 parse_library_geometries(&mut cx, node)?;
312 }
313 "library_images" => {
314 parse_library_images(&mut cx, node)?;
315 }
316 "library_materials" => {
317 parse_library_materials(&mut cx, node)?;
318 }
319 "library_visual_scenes" => {
320 parse_library_visual_scenes(&mut cx, node)?;
321 }
322 "asset" => {
323 cx.asset = Asset::parse(node)?;
324 }
325 "scene" => {
326 cx.scene = parse_scene(&mut cx, node)?;
327 }
328 _name => {
329 }
331 }
332 }
333
334 Ok(Self {
335 asset: cx.asset,
336 library_effects: cx.library_effects,
337 library_geometries: cx.library_geometries,
338 library_images: cx.library_images,
339 library_materials: cx.library_materials,
340 library_visual_scenes: cx.library_visual_scenes,
341 scene: cx.scene,
342 })
343 }
344
345 fn get<T>(&self, url: &T) -> Option<&<Self as Get<T>>::Target>
346 where
347 Self: Get<T>,
348 {
349 <Self as Get<T>>::get(self, url)
350 }
351}
352
353impl<T> ops::Index<&T> for Document<'_>
354where
355 Self: Get<T>,
356{
357 type Output = <Self as Get<T>>::Target;
358
359 #[track_caller]
360 fn index(&self, url: &T) -> &Self::Output {
361 self.get(url).expect("no entry found for key")
362 }
363}
364
365const DEFAULT_UNIT_SIZE: f32 = 1.;
366
367struct Asset {
369 unit: f32,
371}
372
373impl Asset {
374 fn parse(node: xml::Node<'_, '_>) -> io::Result<Self> {
375 debug_assert_eq!(node.tag_name().name(), "asset");
376
377 let mut unit = None;
378 for child in node.element_children() {
379 match child.tag_name().name() {
380 "unit" => {
381 if let Some(v) = child.attribute("meter") {
382 let v = xml::comma_to_period(v);
383 unit = Some(v.parse().map_err(|e| {
384 format_err!(
385 "{} in <{}> element at {}: {:?}",
386 e,
387 child.tag_name().name(),
388 child.attr_value_location("meter"),
389 v
390 )
391 })?);
392 }
393 }
394 "up_axis" => {} _ => { }
396 }
397 }
398
399 Ok(Self {
400 unit: unit.unwrap_or(DEFAULT_UNIT_SIZE),
401 })
402 }
403}
404
405struct Source<'a> {
406 id: &'a str,
408 array_element: Option<ArrayElement<'a>>,
413 accessor: Option<Accessor<'a>>,
415}
416
417impl<'a> Source<'a> {
418 fn parse(node: xml::Node<'a, '_>) -> io::Result<Self> {
440 debug_assert_eq!(node.tag_name().name(), "source");
441 let id = node.required_attribute("id")?;
442 let mut array_element = None;
443 let mut accessor = None;
444
445 for child in node.element_children() {
446 match child.tag_name().name() {
447 "float_array" | "IDREF_array" | "Name_array" => {
448 array_element = Some(parse_array_element(child)?);
449 }
450 "technique_common" => {
451 for technique in child.element_children() {
452 match technique.tag_name().name() {
453 "accessor" => {
454 accessor = Some(Accessor::parse(technique)?);
455 }
456 _ => return Err(error::unexpected_child_elem(technique)),
457 }
458 }
459 }
460 "bool_array" | "int_array" | "SIDREF_array" | "token_array" => {
461 }
467 "asset" | "technique" => { }
468 _ => return Err(error::unexpected_child_elem(child)),
469 }
470 }
471
472 Ok(Self {
473 id,
474 array_element,
476 accessor,
477 })
478 }
479}
480
481struct ArrayElement<'a> {
482 id: &'a str,
484 data: ArrayData<'a>,
487}
488
489fn parse_array_element<'a>(node: xml::Node<'a, '_>) -> io::Result<ArrayElement<'a>> {
490 let name = node.tag_name().name();
491 let is_string_array = name == "IDREF_array" || name == "Name_array";
492
493 let id = node.required_attribute("id")?;
494 let count: u32 = node.parse_required_attribute("count")?;
495 let mut content = node.trimmed_text();
496
497 if content.is_empty() {
499 let data = if is_string_array {
500 ArrayData::String(vec![])
501 } else {
502 ArrayData::Float(vec![])
503 };
504 return Ok(ArrayElement {
505 id,
506 data,
508 });
509 }
510
511 if is_string_array {
512 let mut values = Vec::with_capacity(count as usize);
514 for _ in 0..count {
515 if content.is_empty() {
516 bail!(
517 "expected more values while reading <{}> contents at {}",
518 node.tag_name().name(),
519 node.node_location()
520 );
521 }
522
523 let mut n = 0;
524 while content
525 .as_bytes()
526 .first()
527 .map_or(false, |&b| !xml::is_whitespace(b as char))
528 {
529 n += 1;
530 }
531 values.push(&content[..n]);
532
533 content = xml::trim_start(content.get(n..).unwrap_or_default());
534 }
535
536 Ok(ArrayElement {
537 id,
538 data: ArrayData::String(values),
540 })
541 } else {
542 let mut values = Vec::with_capacity(count as usize);
544 let content = xml::comma_to_period(content);
545 let map_err = |e| {
547 format_err!(
548 "{e} in <{}> element ({})",
549 node.tag_name().name(),
550 node.text_location(),
551 )
552 };
553 for res in xml::parse_float_array_exact(&content, count as usize) {
554 let value = res.map_err(map_err)?;
555 values.push(value);
556 }
557
558 Ok(ArrayElement {
559 id,
560 data: ArrayData::Float(values),
562 })
563 }
564}
565
566enum ArrayData<'a> {
568 Float(Vec<f32>),
570 String(
572 #[allow(dead_code)] Vec<&'a str>,
574 ),
575 }
580
581impl ArrayData<'_> {
582 fn as_float(&self) -> Option<&[f32]> {
590 match self {
591 Self::Float(v) => Some(v),
592 Self::String(..) => None,
593 }
594 }
595 }
619
620struct Accessor<'a> {
621 count: u32,
623 source: Uri<'a, ArrayData<'a>>,
627 stride: u32,
629
630 params: Vec<Param<'a>>,
632}
633
634impl<'a> Accessor<'a> {
635 fn parse(node: xml::Node<'a, '_>) -> io::Result<Self> {
648 debug_assert_eq!(node.tag_name().name(), "accessor");
649 let count: u32 = node.parse_required_attribute("count")?;
650 let source = node.parse_url("source")?;
651 let _offset: u32 = node.parse_attribute("offset")?.unwrap_or(0);
652 let stride: u32 = node.parse_attribute("stride")?.unwrap_or(1);
653 let mut params = vec![];
654
655 for child in node.element_children() {
656 match child.tag_name().name() {
657 "param" => {
658 params.push(Param::parse(child)?);
659 }
660 _ => return Err(error::unexpected_child_elem(child)),
661 }
662 }
663
664 Ok(Self {
665 count,
666 source,
668 stride,
669 params,
670 })
671 }
672}
673
674struct Param<'a> {
681 ty: &'a str,
687 }
690
691impl<'a> Param<'a> {
692 fn parse(node: xml::Node<'a, '_>) -> io::Result<Self> {
703 let ty = node.required_attribute("type")?;
704 if let Some(child) = node.element_children().next() {
708 return Err(error::unexpected_child_elem(child));
709 }
710 Ok(Self {
711 ty,
714 })
716 }
717}
718
719struct SharedInput<'a, T = Accessor<'a>> {
726 offset: u32,
728 semantic: InputSemantic,
730 source: Uri<'a, T>,
732 set: u32,
734}
735
736impl<'a, T> SharedInput<'a, T> {
737 fn parse(node: xml::Node<'a, '_>) -> io::Result<Self> {
749 debug_assert_eq!(node.tag_name().name(), "input");
750 let semantic = node.parse_required_attribute("semantic")?;
751 let source = node.parse_url("source")?;
752 let offset: u32 = node.parse_required_attribute("offset")?;
753 let set: u32 = node.parse_attribute("set")?.unwrap_or(0);
754 if let Some(child) = node.element_children().next() {
755 return Err(error::unexpected_child_elem(child));
756 }
757 Ok(Self {
758 offset,
759 semantic,
760 source,
761 set,
762 })
763 }
764
765 fn cast<U>(self) -> SharedInput<'a, U> {
766 SharedInput {
767 offset: self.offset,
768 semantic: self.semantic,
769 source: self.source.cast(),
770 set: self.set,
771 }
772 }
773}
774
775struct UnsharedInput<'a> {
782 semantic: InputSemantic,
784 source: Uri<'a, Accessor<'a>>,
786}
787
788impl<'a> UnsharedInput<'a> {
789 fn parse(node: xml::Node<'a, '_>) -> io::Result<Self> {
799 debug_assert_eq!(node.tag_name().name(), "input");
800 let semantic = node.parse_required_attribute("semantic")?;
801 let source = node.parse_url("source")?;
802 if let Some(child) = node.element_children().next() {
803 return Err(error::unexpected_child_elem(child));
804 }
805 Ok(Self { semantic, source })
806 }
807}
808
809#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
816#[derive(Clone, Copy, PartialEq, Eq, Hash)]
817enum InputSemantic {
818 BINORMAL,
820 COLOR,
822 CONTINUITY,
824 IMAGE,
826 INPUT,
828 IN_TANGENT,
830 INTERPOLATION,
832 INV_BIND_MATRIX,
834 JOINT,
836 LINEAR_STEPS,
838 MORPH_TARGET,
840 MORPH_WEIGHT,
842 NORMAL,
844 OUTPUT,
846 OUT_TANGENT,
848 POSITION,
850 TANGENT,
852 TEXBINORMAL,
854 TEXCOORD,
856 TEXTANGENT,
858 UV,
860 VERTEX,
862 WEIGHT,
864}
865
866impl FromStr for InputSemantic {
867 type Err = io::Error;
868
869 fn from_str(s: &str) -> Result<Self, Self::Err> {
870 Ok(match s {
871 "BINORMAL" => Self::BINORMAL,
872 "COLOR" => Self::COLOR,
873 "CONTINUITY" => Self::CONTINUITY,
874 "IMAGE" => Self::IMAGE,
875 "INPUT" => Self::INPUT,
876 "IN_TANGENT" => Self::IN_TANGENT,
877 "INTERPOLATION" => Self::INTERPOLATION,
878 "INV_BIND_MATRIX" => Self::INV_BIND_MATRIX,
879 "JOINT" => Self::JOINT,
880 "LINEAR_STEPS" => Self::LINEAR_STEPS,
881 "MORPH_TARGET" => Self::MORPH_TARGET,
882 "MORPH_WEIGHT" => Self::MORPH_WEIGHT,
883 "NORMAL" => Self::NORMAL,
884 "OUTPUT" => Self::OUTPUT,
885 "OUT_TANGENT" => Self::OUT_TANGENT,
886 "POSITION" => Self::POSITION,
887 "TANGENT" => Self::TANGENT,
888 "TEXBINORMAL" => Self::TEXBINORMAL,
889 "TEXCOORD" => Self::TEXCOORD,
890 "TEXTANGENT" => Self::TEXTANGENT,
891 "UV" => Self::UV,
892 "VERTEX" => Self::VERTEX,
893 "WEIGHT" => Self::WEIGHT,
894 _ => bail!("unknown input semantic {:?}", s),
895 })
896 }
897}