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