Skip to main content

fbx_dom/objects/
animation_stack.rs

1//! FBX `AnimationStack` — Assimp [`AnimationStack`](https://github.com/assimp/assimp/blob/master/code/AssetLib/FBX/FBXAnimation.cpp) / [`FBXDocument.h`](https://github.com/assimp/assimp/blob/master/code/AssetLib/FBX/FBXDocument.h).
2//!
3//! Attached layers are resolved via connections in Assimp; this wrapper exposes the
4//! `fbx_simple_property` time fields and the property table on [`OwnedObject`].
5
6use std::collections::HashMap;
7use std::convert::TryFrom;
8
9use crate::Property;
10use crate::{OwnedDocument, OwnedObject};
11
12use super::{AnimationLayer, FbxObjectTag, FbxTypeMismatch, fbx_object_tag};
13
14#[derive(Debug, PartialEq)]
15pub struct AnimationStack(pub OwnedObject);
16
17impl AnimationStack {
18    pub fn inner(&self) -> &OwnedObject {
19        &self.0
20    }
21
22    pub fn into_inner(self) -> OwnedObject {
23        self.0
24    }
25
26    pub fn properties(&self) -> &HashMap<String, Property> {
27        &self.0.properties
28    }
29
30    pub fn property(&self, name: &str) -> Option<&Property> {
31        self.0.properties.get(name)
32    }
33
34    pub fn local_start(&self) -> i64 {
35        match self.property("LocalStart") {
36            Some(Property::ILongLong(v)) => *v,
37            _ => 0,
38        }
39    }
40
41    pub fn local_stop(&self) -> i64 {
42        match self.property("LocalStop") {
43            Some(Property::ILongLong(v)) => *v,
44            _ => 0,
45        }
46    }
47
48    pub fn reference_start(&self) -> i64 {
49        match self.property("ReferenceStart") {
50            Some(Property::ILongLong(v)) => *v,
51            _ => 0,
52        }
53    }
54
55    pub fn reference_stop(&self) -> i64 {
56        match self.property("ReferenceStop") {
57            Some(Property::ILongLong(v)) => *v,
58            _ => 0,
59        }
60    }
61
62    /// Resolve incoming `AnimationLayer -> AnimationStack` OO links.
63    pub fn get_animation_layers<'a>(
64        &'a self,
65        document: &'a OwnedDocument,
66    ) -> Vec<&'a AnimationLayer> {
67        let stack_id = self.inner().object_index;
68        document
69            .animation_layers
70            .iter()
71            .filter(|layer| layer.inner().connected_object_ids.contains(&stack_id))
72            .collect()
73    }
74}
75
76impl TryFrom<OwnedObject> for AnimationStack {
77    type Error = FbxTypeMismatch;
78
79    fn try_from(o: OwnedObject) -> Result<Self, Self::Error> {
80        match fbx_object_tag(&o) {
81            FbxObjectTag::AnimationStack => Ok(AnimationStack(o)),
82            _ => Err(FbxTypeMismatch::wrong_object_kind(
83                o,
84                "AnimationStack".to_string(),
85            )),
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use std::collections::HashMap;
93    use std::convert::TryFrom;
94
95    use crate::objects::{
96        ANIMATION_LAYER_CLASS_NAME, ANIMATION_LAYER_TYPE_NAME, ANIMATION_STACK_CLASS_NAME,
97        ANIMATION_STACK_TYPE_NAME, AnimationLayer,
98    };
99    use crate::{OwnedDocument, OwnedObject, Property};
100
101    use super::AnimationStack;
102
103    #[test]
104    fn stack_time_defaults_and_properties() {
105        let o_empty = OwnedObject {
106            object_index: 5,
107            name: "AnimStack::Take001".into(),
108            type_name: ANIMATION_STACK_TYPE_NAME.into(),
109            class_name: ANIMATION_STACK_CLASS_NAME.into(),
110            properties: HashMap::new(),
111            attributes: HashMap::new(),
112            connected_object_ids: vec![],
113            object_property_targets: vec![],
114            pp_property_targets: HashMap::new(),
115        };
116        let s = AnimationStack::try_from(o_empty).unwrap();
117        assert_eq!(s.local_start(), 0);
118        assert_eq!(s.local_stop(), 0);
119        assert_eq!(s.reference_start(), 0);
120        assert_eq!(s.reference_stop(), 0);
121
122        let mut properties = HashMap::new();
123        properties.insert("LocalStart".to_string(), Property::ILongLong(10));
124        properties.insert("LocalStop".to_string(), Property::ILongLong(100));
125        properties.insert("ReferenceStart".to_string(), Property::ILongLong(20));
126        properties.insert("ReferenceStop".to_string(), Property::ILongLong(200));
127        let o = OwnedObject {
128            object_index: 6,
129            name: "AnimStack::Take002".into(),
130            type_name: ANIMATION_STACK_TYPE_NAME.into(),
131            class_name: ANIMATION_STACK_CLASS_NAME.into(),
132            properties,
133            attributes: HashMap::new(),
134            connected_object_ids: vec![],
135            object_property_targets: vec![],
136            pp_property_targets: HashMap::new(),
137        };
138        let s = AnimationStack::try_from(o).unwrap();
139        assert_eq!(s.local_start(), 10);
140        assert_eq!(s.local_stop(), 100);
141        assert_eq!(s.reference_start(), 20);
142        assert_eq!(s.reference_stop(), 200);
143    }
144
145    #[test]
146    fn resolves_animation_layer_links() {
147        let stack = AnimationStack::try_from(OwnedObject {
148            object_index: 1000,
149            name: "AnimStack::Main".into(),
150            type_name: ANIMATION_STACK_TYPE_NAME.into(),
151            class_name: ANIMATION_STACK_CLASS_NAME.into(),
152            properties: HashMap::new(),
153            attributes: HashMap::new(),
154            connected_object_ids: vec![],
155            object_property_targets: vec![],
156            pp_property_targets: HashMap::new(),
157        })
158        .unwrap();
159        let layer = AnimationLayer::try_from(OwnedObject {
160            object_index: 1001,
161            name: "AnimLayer::Layer0".into(),
162            type_name: ANIMATION_LAYER_TYPE_NAME.into(),
163            class_name: ANIMATION_LAYER_CLASS_NAME.into(),
164            properties: HashMap::new(),
165            attributes: HashMap::new(),
166            connected_object_ids: vec![1000],
167            object_property_targets: vec![],
168            pp_property_targets: HashMap::new(),
169        })
170        .unwrap();
171        let mut owned = OwnedDocument::default();
172        owned.animation_layers = vec![layer];
173        let links = stack.get_animation_layers(&owned);
174        assert_eq!(links.len(), 1);
175        assert_eq!(links[0].inner().object_index, 1001);
176    }
177}