dae_parser/core/
anim.rs

1use crate::*;
2
3/// Categorizes the declaration of animation information.
4#[derive(Clone, Debug, Default)]
5pub struct Animation {
6    /// A text string containing the unique identifier of the element.
7    pub id: Option<String>,
8    /// The text string name of this element.
9    pub name: Option<String>,
10    /// Asset management information about this element.
11    pub asset: Option<Box<Asset>>,
12    /// Allows the formation of a hierarchy of related animations.
13    pub children: Vec<Animation>,
14    /// The data repository that provides values according to the semantics of an
15    /// [`Input`] element that refers to it.
16    pub source: Vec<Source>,
17    /// Describes the interpolation sampling function for the animation.
18    pub sampler: Vec<Sampler>,
19    /// Describes an output channel for the animation.
20    pub channel: Vec<Channel>,
21    /// Provides arbitrary additional information about this element.
22    pub extra: Vec<Extra>,
23}
24
25impl XNode for Animation {
26    const NAME: &'static str = "animation";
27    fn parse(element: &Element) -> Result<Self> {
28        debug_assert_eq!(element.name(), Self::NAME);
29        let mut it = element.children().peekable();
30        let res = Animation {
31            id: element.attr("id").map(Into::into),
32            name: element.attr("name").map(Into::into),
33            asset: Asset::parse_opt_box(&mut it)?,
34            children: Animation::parse_list(&mut it)?,
35            source: Source::parse_list(&mut it)?,
36            sampler: Sampler::parse_list(&mut it)?,
37            channel: Channel::parse_list(&mut it)?,
38            extra: Extra::parse_many(it)?,
39        };
40        // Note: blender will produce empty animation containers,
41        // and the spec even has examples of such, so this constraint seems bogus
42        // if res.children.is_empty() && res.sampler.is_empty() {
43        //     return Err("animation: no sampler/channel or children".into());
44        // }
45        if res.sampler.is_empty() != res.channel.is_empty() {
46            return Err("animation: sampler and channel must be used together".into());
47        }
48        Ok(res)
49    }
50}
51
52impl XNodeWrite for Animation {
53    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
54        let mut e = Self::elem();
55        e.opt_attr("id", &self.id);
56        e.opt_attr("name", &self.name);
57        let e = e.start(w)?;
58        self.asset.write_to(w)?;
59        self.children.write_to(w)?;
60        self.source.write_to(w)?;
61        self.sampler.write_to(w)?;
62        self.channel.write_to(w)?;
63        self.extra.write_to(w)?;
64        e.end(w)
65    }
66}
67
68impl CollectLocalMaps for Animation {
69    fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
70        maps.insert(self);
71        self.children.collect_local_maps(maps);
72        self.source.collect_local_maps(maps);
73        self.sampler.collect_local_maps(maps);
74    }
75}
76
77impl Animation {
78    fn on_children<'a, E>(
79        &'a self,
80        f: &mut impl FnMut(&'a Self) -> Result<(), E>,
81    ) -> Result<(), E> {
82        f(self)?;
83        for child in &self.children {
84            child.on_children(f)?
85        }
86        Ok(())
87    }
88}
89
90impl Traversable for Animation {
91    fn traverse<'a, E>(
92        doc: &'a Document,
93        mut f: impl FnMut(&'a Animation) -> Result<(), E>,
94    ) -> Result<(), E> {
95        doc.iter()
96            .try_for_each(|e: &Animation| e.on_children(&mut f))
97    }
98}
99
100/// Defines a section of a set of animation curves to be used together as an animation clip.
101#[derive(Clone, Debug)]
102pub struct AnimationClip {
103    /// A text string containing the unique identifier of the element.
104    pub id: Option<String>,
105    /// The text string name of this element.
106    pub name: Option<String>,
107    /// The time in seconds of the beginning of the clip. This time is the same as that used in the
108    /// key-frame data and is used to determine which set of key frames will be included in the
109    /// clip. The start time does not specify when the clip will be played. If the time falls between
110    /// two key frames of a referenced animation, an interpolated value should be used.
111    pub start: f32,
112    /// The time in seconds of the end of the clip. This is used in the same way as the start time.
113    /// If `end` is not specified, the value is taken to be the end time of the longest animation.
114    pub end: Option<f32>,
115    /// Asset management information about this element.
116    pub asset: Option<Box<Asset>>,
117    /// Instantiates an [`Animation`] object.
118    pub instance_animation: Vec<Instance<Animation>>,
119    /// Provides arbitrary additional information about this element.
120    pub extra: Vec<Extra>,
121}
122
123impl AnimationClip {
124    /// Create a new `AnimationClip` from the given list of instances.
125    pub fn new(instance_animation: Vec<Instance<Animation>>) -> Self {
126        assert!(!instance_animation.is_empty());
127        Self {
128            id: Default::default(),
129            name: Default::default(),
130            start: Default::default(),
131            end: Default::default(),
132            asset: Default::default(),
133            instance_animation,
134            extra: Default::default(),
135        }
136    }
137}
138
139impl XNode for AnimationClip {
140    const NAME: &'static str = "animation_clip";
141    fn parse(element: &Element) -> Result<Self> {
142        debug_assert_eq!(element.name(), Self::NAME);
143        let mut it = element.children().peekable();
144        Ok(AnimationClip {
145            id: element.attr("id").map(Into::into),
146            name: element.attr("name").map(Into::into),
147            start: parse_attr(element.attr("start"))?.unwrap_or(0.),
148            end: parse_attr(element.attr("end"))?,
149            asset: Asset::parse_opt_box(&mut it)?,
150            instance_animation: Instance::parse_list_n::<1>(&mut it)?,
151            extra: Extra::parse_many(it)?,
152        })
153    }
154}
155
156impl XNodeWrite for AnimationClip {
157    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
158        let mut e = Self::elem();
159        e.opt_attr("id", &self.id);
160        e.opt_attr("name", &self.name);
161        e.print_attr("start", self.start);
162        e.opt_print_attr("end", &self.end);
163        let e = e.start(w)?;
164        self.asset.write_to(w)?;
165        self.instance_animation.write_to(w)?;
166        self.extra.write_to(w)?;
167        e.end(w)
168    }
169}
170
171/// Declares an output channel of an animation.
172#[derive(Clone, Debug)]
173pub struct Channel {
174    /// The location of the animation sampler using a URL expression.
175    pub source: UrlRef<Sampler>,
176    /// The location of the element bound to the output of the sampler.
177    pub target: Address,
178}
179
180impl Channel {
181    /// Construct a new `Channel` from a source and target.
182    pub fn new(source: Url, target: String) -> Self {
183        Self {
184            source: Ref::new(source),
185            target: Address(target),
186        }
187    }
188}
189
190impl XNode for Channel {
191    const NAME: &'static str = "channel";
192    fn parse(element: &Element) -> Result<Self> {
193        debug_assert_eq!(element.name(), Self::NAME);
194        let target = element.attr("target").ok_or("expecting target attr")?;
195        Ok(Channel {
196            source: parse_attr(element.attr("source"))?.ok_or("missing source attr")?,
197            target: Address(target.into()),
198        })
199    }
200}
201
202impl XNodeWrite for Channel {
203    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
204        let mut e = Self::elem();
205        e.print_attr("source", &self.source);
206        e.print_attr("target", &self.target);
207        e.end(w)
208    }
209}
210
211/// Declares an interpolation sampling function for an animation.
212#[derive(Clone, Debug)]
213pub struct Sampler {
214    /// A text string containing the unique identifier of the element.
215    pub id: Option<String>,
216    /// Assigns semantics to each [`Source`]. See the COLLADA spec for details.
217    pub inputs: Vec<Input>,
218    /// The index into `inputs` for the [`Semantic::Interpolation`] input (which must exist).
219    pub interpolation: usize,
220}
221
222impl Sampler {
223    /// Construct a new `Sampler` from a list of inputs.
224    /// One of the inputs must have [`Semantic::Interpolation`].
225    pub fn new(inputs: Vec<Input>) -> Self {
226        Self {
227            id: None,
228            interpolation: inputs
229                .iter()
230                .position(|i| i.semantic == Semantic::Interpolation)
231                .expect("sampler: missing INTERPOLATION input"),
232            inputs,
233        }
234    }
235}
236
237impl XNode for Sampler {
238    const NAME: &'static str = "sampler";
239    fn parse(element: &Element) -> Result<Self> {
240        debug_assert_eq!(element.name(), Self::NAME);
241        let mut it = element.children().peekable();
242        let inputs = Input::parse_list(&mut it)?;
243        let res = Sampler {
244            id: element.attr("id").map(Into::into),
245            interpolation: inputs
246                .iter()
247                .position(|i| i.semantic == Semantic::Interpolation)
248                .ok_or("sampler: missing INTERPOLATION input")?,
249            inputs,
250        };
251        finish(res, it)
252    }
253}
254
255impl XNodeWrite for Sampler {
256    fn write_to<W: Write>(&self, w: &mut XWriter<W>) -> Result<()> {
257        let mut e = Self::elem();
258        e.opt_attr("id", &self.id);
259        let e = e.start(w)?;
260        self.inputs.write_to(w)?;
261        e.end(w)
262    }
263}
264
265impl CollectLocalMaps for Sampler {
266    fn collect_local_maps<'a>(&'a self, maps: &mut LocalMaps<'a>) {
267        maps.insert(self)
268    }
269}
270
271impl Traversable for Sampler {
272    fn traverse<'a, E>(
273        doc: &'a Document,
274        mut f: impl FnMut(&'a Self) -> Result<(), E>,
275    ) -> Result<(), E>
276    where
277        Self: 'a,
278    {
279        doc.iter()
280            .try_for_each(|lib: &Animation| lib.sampler.iter().try_for_each(&mut f))
281    }
282}
283
284impl Sampler {
285    /// The input with [`Semantic::Interpolation`].
286    pub fn interpolation_input(&self) -> &Input {
287        &self.inputs[self.interpolation]
288    }
289}