1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2017, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12 
13 * Redistributions of source code must retain the above
14   copyright notice, this list of conditions and the
15   following disclaimer.
16 
17 * Redistributions in binary form must reproduce the above
18   copyright notice, this list of conditions and the
19   following disclaimer in the documentation and/or other
20   materials provided with the distribution.
21 
22 * Neither the name of the assimp team, nor the names of its
23   contributors may be used to endorse or promote products
24   derived from this software without specific prior
25   written permission of the assimp team.
26 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39 ----------------------------------------------------------------------
40 */
41 
42 /** @file  FBXAnimation.cpp
43  *  @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode,
44  *         Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack
45  */
46 
47 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
48 
49 #include "FBXParser.h"
50 #include "FBXDocument.h"
51 #include "FBXImporter.h"
52 #include "FBXDocumentUtil.h"
53 
54 namespace Assimp {
55 namespace FBX {
56 
57 using namespace Util;
58 
59 // ------------------------------------------------------------------------------------------------
AnimationCurve(uint64_t id,const Element & element,const std::string & name,const Document &)60 AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/)
61 : Object(id, element, name)
62 {
63     const Scope& sc = GetRequiredScope(element);
64     const Element& KeyTime = GetRequiredElement(sc,"KeyTime");
65     const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat");
66 
67     ParseVectorDataArray(keys, KeyTime);
68     ParseVectorDataArray(values, KeyValueFloat);
69 
70     if(keys.size() != values.size()) {
71         DOMError("the number of key times does not match the number of keyframe values",&KeyTime);
72     }
73 
74     // check if the key times are well-ordered
75     if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) {
76         DOMError("the keyframes are not in ascending order",&KeyTime);
77     }
78 
79     const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"];
80     if(KeyAttrDataFloat) {
81         ParseVectorDataArray(attributes, *KeyAttrDataFloat);
82     }
83 
84     const Element* KeyAttrFlags = sc["KeyAttrFlags"];
85     if(KeyAttrFlags) {
86         ParseVectorDataArray(flags, *KeyAttrFlags);
87     }
88 }
89 
90 // ------------------------------------------------------------------------------------------------
~AnimationCurve()91 AnimationCurve::~AnimationCurve()
92 {
93     // empty
94 }
95 
96 // ------------------------------------------------------------------------------------------------
AnimationCurveNode(uint64_t id,const Element & element,const std::string & name,const Document & doc,const char * const * target_prop_whitelist,size_t whitelist_size)97 AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name,
98         const Document& doc, const char* const * target_prop_whitelist /*= NULL*/,
99         size_t whitelist_size /*= 0*/)
100 : Object(id, element, name)
101 , target()
102 , doc(doc)
103 {
104     const Scope& sc = GetRequiredScope(element);
105 
106     // find target node
107     const char* whitelist[] = {"Model","NodeAttribute"};
108     const std::vector<const Connection*>& conns = doc.GetConnectionsBySourceSequenced(ID(),whitelist,2);
109 
110     for(const Connection* con : conns) {
111 
112         // link should go for a property
113         if (!con->PropertyName().length()) {
114             continue;
115         }
116 
117         if(target_prop_whitelist) {
118             const char* const s = con->PropertyName().c_str();
119             bool ok = false;
120             for (size_t i = 0; i < whitelist_size; ++i) {
121                 if (!strcmp(s, target_prop_whitelist[i])) {
122                     ok = true;
123                     break;
124                 }
125             }
126 
127             if (!ok) {
128                 throw std::range_error("AnimationCurveNode target property is not in whitelist");
129             }
130         }
131 
132         const Object* const ob = con->DestinationObject();
133         if(!ob) {
134             DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element);
135             continue;
136         }
137 
138         // XXX support constraints as DOM class
139         //ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
140         target = ob;
141         if(!target) {
142             continue;
143         }
144 
145         prop = con->PropertyName();
146         break;
147     }
148 
149     if(!target) {
150         DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element);
151     }
152 
153     props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false);
154 }
155 
156 // ------------------------------------------------------------------------------------------------
~AnimationCurveNode()157 AnimationCurveNode::~AnimationCurveNode()
158 {
159     // empty
160 }
161 
162 // ------------------------------------------------------------------------------------------------
Curves() const163 const AnimationCurveMap& AnimationCurveNode::Curves() const
164 {
165     if ( curves.empty() ) {
166         // resolve attached animation curves
167         const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
168 
169         for(const Connection* con : conns) {
170 
171             // link should go for a property
172             if (!con->PropertyName().length()) {
173                 continue;
174             }
175 
176             const Object* const ob = con->SourceObject();
177             if(!ob) {
178                 DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element);
179                 continue;
180             }
181 
182             const AnimationCurve* const anim = dynamic_cast<const AnimationCurve*>(ob);
183             if(!anim) {
184                 DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element);
185                 continue;
186             }
187 
188             curves[con->PropertyName()] = anim;
189         }
190     }
191 
192     return curves;
193 }
194 
195 // ------------------------------------------------------------------------------------------------
AnimationLayer(uint64_t id,const Element & element,const std::string & name,const Document & doc)196 AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc)
197 : Object(id, element, name)
198 , doc(doc)
199 {
200     const Scope& sc = GetRequiredScope(element);
201 
202     // note: the props table here bears little importance and is usually absent
203     props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true);
204 }
205 
206 // ------------------------------------------------------------------------------------------------
~AnimationLayer()207 AnimationLayer::~AnimationLayer()
208 {
209     // empty
210 }
211 
212 // ------------------------------------------------------------------------------------------------
Nodes(const char * const * target_prop_whitelist,size_t whitelist_size) const213 AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/,
214     size_t whitelist_size /*= 0*/) const
215 {
216     AnimationCurveNodeList nodes;
217 
218     // resolve attached animation nodes
219     const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurveNode");
220     nodes.reserve(conns.size());
221 
222     for(const Connection* con : conns) {
223 
224         // link should not go to a property
225         if (con->PropertyName().length()) {
226             continue;
227         }
228 
229         const Object* const ob = con->SourceObject();
230         if(!ob) {
231             DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element);
232             continue;
233         }
234 
235         const AnimationCurveNode* const anim = dynamic_cast<const AnimationCurveNode*>(ob);
236         if(!anim) {
237             DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element);
238             continue;
239         }
240 
241         if(target_prop_whitelist) {
242             const char* s = anim->TargetProperty().c_str();
243             bool ok = false;
244             for (size_t i = 0; i < whitelist_size; ++i) {
245                 if (!strcmp(s, target_prop_whitelist[i])) {
246                     ok = true;
247                     break;
248                 }
249             }
250             if(!ok) {
251                 continue;
252             }
253         }
254         nodes.push_back(anim);
255     }
256 
257     return nodes; // pray for NRVO
258 }
259 
260 // ------------------------------------------------------------------------------------------------
AnimationStack(uint64_t id,const Element & element,const std::string & name,const Document & doc)261 AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc)
262 : Object(id, element, name)
263 {
264     const Scope& sc = GetRequiredScope(element);
265 
266     // note: we don't currently use any of these properties so we shouldn't bother if it is missing
267     props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true);
268 
269     // resolve attached animation layers
270     const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationLayer");
271     layers.reserve(conns.size());
272 
273     for(const Connection* con : conns) {
274 
275         // link should not go to a property
276         if (con->PropertyName().length()) {
277             continue;
278         }
279 
280         const Object* const ob = con->SourceObject();
281         if(!ob) {
282             DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element);
283             continue;
284         }
285 
286         const AnimationLayer* const anim = dynamic_cast<const AnimationLayer*>(ob);
287         if(!anim) {
288             DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element);
289             continue;
290         }
291         layers.push_back(anim);
292     }
293 }
294 
295 // ------------------------------------------------------------------------------------------------
~AnimationStack()296 AnimationStack::~AnimationStack()
297 {
298     // empty
299 }
300 
301 } //!FBX
302 } //!Assimp
303 
304 #endif // ASSIMP_BUILD_NO_FBX_IMPORTER
305