1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following 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
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF 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
54namespace Assimp {
55namespace FBX {
56
57using namespace Util;
58
59// ------------------------------------------------------------------------------------------------
60AnimationCurve::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// ------------------------------------------------------------------------------------------------
91AnimationCurve::~AnimationCurve()
92{
93 // empty
94}
95
96// ------------------------------------------------------------------------------------------------
97AnimationCurveNode::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// ------------------------------------------------------------------------------------------------
157AnimationCurveNode::~AnimationCurveNode()
158{
159 // empty
160}
161
162// ------------------------------------------------------------------------------------------------
163const 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// ------------------------------------------------------------------------------------------------
196AnimationLayer::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// ------------------------------------------------------------------------------------------------
207AnimationLayer::~AnimationLayer()
208{
209 // empty
210}
211
212// ------------------------------------------------------------------------------------------------
213AnimationCurveNodeList 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// ------------------------------------------------------------------------------------------------
261AnimationStack::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// ------------------------------------------------------------------------------------------------
296AnimationStack::~AnimationStack()
297{
298 // empty
299}
300
301} //!FBX
302} //!Assimp
303
304#endif // ASSIMP_BUILD_NO_FBX_IMPORTER
305