1 | /*
|
---|
2 | -----------------------------------------------------------------------------
|
---|
3 | This source file is part of OGRE
|
---|
4 | (Object-oriented Graphics Rendering Engine)
|
---|
5 | For the latest info, see http://www.ogre3d.org/
|
---|
6 |
|
---|
7 | Copyright (c) 2000-2005 The OGRE Team
|
---|
8 | Also see acknowledgements in Readme.html
|
---|
9 |
|
---|
10 | This program is free software; you can redistribute it and/or modify it under
|
---|
11 | the terms of the GNU Lesser General Public License as published by the Free Software
|
---|
12 | Foundation; either version 2 of the License, or (at your option) any later
|
---|
13 | version.
|
---|
14 |
|
---|
15 | This program is distributed in the hope that it will be useful, but WITHOUT
|
---|
16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
---|
17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
---|
18 |
|
---|
19 | You should have received a copy of the GNU Lesser General Public License along with
|
---|
20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
---|
21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to
|
---|
22 | http://www.gnu.org/copyleft/lesser.txt.
|
---|
23 | -----------------------------------------------------------------------------
|
---|
24 | */
|
---|
25 | #include "OgreXSISkeletonExporter.h"
|
---|
26 | #include "OgreResourceGroupManager.h"
|
---|
27 | #include "OgreSkeletonManager.h"
|
---|
28 | #include "OgreSkeleton.h"
|
---|
29 | #include "OgreBone.h"
|
---|
30 | #include "OgreAnimation.h"
|
---|
31 | #include "OgreAnimationTrack.h"
|
---|
32 | #include "OgreKeyFrame.h"
|
---|
33 | #include "OgreSkeletonSerializer.h"
|
---|
34 | #include "OgreQuaternion.h"
|
---|
35 | #include <xsi_model.h>
|
---|
36 | #include <xsi_kinematics.h>
|
---|
37 | #include <xsi_kinematicstate.h>
|
---|
38 | #include <xsi_math.h>
|
---|
39 | #include <xsi_rotation.h>
|
---|
40 | #include <xsi_animationsourceitem.h>
|
---|
41 | #include <xsi_source.h>
|
---|
42 | #include <xsi_fcurve.h>
|
---|
43 | #include <xsi_fcurvekey.h>
|
---|
44 | #include <xsi_time.h>
|
---|
45 | #include <xsi_chaineffector.h>
|
---|
46 | #include <xsi_chainroot.h>
|
---|
47 | #include <xsi_chainbone.h>
|
---|
48 | #include <xsi_matrix4.h>
|
---|
49 | #include <xsi_transformation.h>
|
---|
50 | #include <xsi_vector3.h>
|
---|
51 | #include <xsi_constraint.h>
|
---|
52 | #include <xsi_track.h>
|
---|
53 | #include <xsi_clip.h>
|
---|
54 | #include <xsi_selection.h>
|
---|
55 | #include <xsi_statickinematicstate.h>
|
---|
56 |
|
---|
57 | using namespace XSI;
|
---|
58 |
|
---|
59 | namespace Ogre
|
---|
60 | {
|
---|
61 | //-----------------------------------------------------------------------------
|
---|
62 | XsiSkeletonExporter::XsiSkeletonExporter()
|
---|
63 | {
|
---|
64 | mXsiSceneRoot = X3DObject(mXsiApp.GetActiveSceneRoot());
|
---|
65 | mXSITrackTypeNames["posx"] = XTT_POS_X;
|
---|
66 | mXSITrackTypeNames["posy"] = XTT_POS_Y;
|
---|
67 | mXSITrackTypeNames["posz"] = XTT_POS_Z;
|
---|
68 | mXSITrackTypeNames["rotx"] = XTT_ROT_X;
|
---|
69 | mXSITrackTypeNames["roty"] = XTT_ROT_Y;
|
---|
70 | mXSITrackTypeNames["rotz"] = XTT_ROT_Z;
|
---|
71 | mXSITrackTypeNames["sclx"] = XTT_SCL_X;
|
---|
72 | mXSITrackTypeNames["scly"] = XTT_SCL_Y;
|
---|
73 | mXSITrackTypeNames["sclz"] = XTT_SCL_Z;
|
---|
74 | }
|
---|
75 | //-----------------------------------------------------------------------------
|
---|
76 | XsiSkeletonExporter::~XsiSkeletonExporter()
|
---|
77 | {
|
---|
78 | }
|
---|
79 | //-----------------------------------------------------------------------------
|
---|
80 | void XsiSkeletonExporter::exportSkeleton(const String& skeletonFileName,
|
---|
81 | DeformerMap& deformers, float framesPerSecond, AnimationList& animList)
|
---|
82 | {
|
---|
83 | LogOgreAndXSI(L"** Begin OGRE Skeleton Export **");
|
---|
84 |
|
---|
85 | copyDeformerMap(deformers);
|
---|
86 |
|
---|
87 | SkeletonPtr skeleton = SkeletonManager::getSingleton().create("export",
|
---|
88 | ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
|
---|
89 | // construct the hierarchy
|
---|
90 | buildBoneHierarchy(skeleton.get(), deformers, animList);
|
---|
91 |
|
---|
92 | // pre-parse all animations for lengths
|
---|
93 | determineAnimationLengths(animList);
|
---|
94 |
|
---|
95 | // progress report
|
---|
96 | ProgressManager::getSingleton().progress();
|
---|
97 |
|
---|
98 | establishInitialTransforms(deformers);
|
---|
99 |
|
---|
100 | // create animations
|
---|
101 | createAnimations(skeleton.get(), deformers, framesPerSecond, animList);
|
---|
102 | // progress report
|
---|
103 | ProgressManager::getSingleton().progress();
|
---|
104 |
|
---|
105 | // Optimise
|
---|
106 | skeleton->optimiseAllAnimations();
|
---|
107 |
|
---|
108 | SkeletonSerializer ser;
|
---|
109 | ser.exportSkeleton(skeleton.get(), skeletonFileName);
|
---|
110 | // progress report
|
---|
111 | ProgressManager::getSingleton().progress();
|
---|
112 |
|
---|
113 | LogOgreAndXSI(L"** OGRE Skeleton Export Complete **");
|
---|
114 |
|
---|
115 | cleanup();
|
---|
116 |
|
---|
117 | }
|
---|
118 | //-----------------------------------------------------------------------------
|
---|
119 | void XsiSkeletonExporter::copyDeformerMap(DeformerMap& deformers)
|
---|
120 | {
|
---|
121 | // Make lower-case version
|
---|
122 | // some XSI animations appear to like to use case insensitive references :(
|
---|
123 | for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i)
|
---|
124 | {
|
---|
125 | DeformerEntry* deformer = i->second;
|
---|
126 | String name = XSItoOgre(deformer->obj.GetName());
|
---|
127 | StringUtil::toLowerCase(name);
|
---|
128 | mLowerCaseDeformerMap[name] = deformer;
|
---|
129 | }
|
---|
130 | }
|
---|
131 | //-----------------------------------------------------------------------------
|
---|
132 | void XsiSkeletonExporter::buildBoneHierarchy(Skeleton* pSkeleton,
|
---|
133 | DeformerMap& deformers, AnimationList& animList)
|
---|
134 | {
|
---|
135 | /// Copy all entries from map into a list so iterators won't get invalidated
|
---|
136 | std::list<DeformerEntry*> deformerList;
|
---|
137 | LogOgreAndXSI(L"-- Bones with vertex assignments:");
|
---|
138 | for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i)
|
---|
139 | {
|
---|
140 | DeformerEntry* deformer = i->second;
|
---|
141 | deformerList.push_back(deformer);
|
---|
142 | LogOgreAndXSI(deformer->obj.GetName());
|
---|
143 | }
|
---|
144 |
|
---|
145 | /* XSI allows you to use any object at all as a bone, not just chain elements.
|
---|
146 | A typical choice is a hierarchy of nulls, for example. In order to
|
---|
147 | build a skeleton hierarchy which represents the actual one, we need
|
---|
148 | to find the relationships between all the deformers that we found.
|
---|
149 |
|
---|
150 | Well do this by navigating up the scene tree from each bone, looking for
|
---|
151 | a match in the existing bone list or creating a new one where we need it
|
---|
152 | to reach the root. We add bones even if they're not assigned vertices
|
---|
153 | because the animation may depend on them. If the
|
---|
154 | traversal hits the scene root this bone is clearly a root bone
|
---|
155 | (there may be more than one).
|
---|
156 | */
|
---|
157 | for (std::list<DeformerEntry*>::iterator i = deformerList.begin(); i != deformerList.end(); ++i)
|
---|
158 | {
|
---|
159 | DeformerEntry* deformer = *i;
|
---|
160 | if (deformer->parentName.empty())
|
---|
161 | {
|
---|
162 | linkBoneWithParent(deformer, deformers, deformerList);
|
---|
163 | }
|
---|
164 | }
|
---|
165 |
|
---|
166 | // Now eliminate all bones without any animated kine parameters
|
---|
167 | // Need to do this after we've determined all relationships
|
---|
168 | for (std::list<DeformerEntry*>::iterator i = deformerList.begin(); i != deformerList.end(); ++i)
|
---|
169 | {
|
---|
170 | DeformerEntry* deformer = *i;
|
---|
171 | validateAsBone(pSkeleton, deformer, deformers, deformerList, animList);
|
---|
172 | }
|
---|
173 |
|
---|
174 | // Now link
|
---|
175 | for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i)
|
---|
176 | {
|
---|
177 | DeformerEntry* deformer = i->second;
|
---|
178 |
|
---|
179 | // link to parent
|
---|
180 | if (!deformer->parentName.empty())
|
---|
181 | {
|
---|
182 | DeformerEntry* parent = getDeformer(deformer->parentName, deformers);
|
---|
183 | assert (parent && "Parent not found");
|
---|
184 | assert (deformer->pBone && "Child bone not created");
|
---|
185 | assert(parent->pBone && "Parent bone not created");
|
---|
186 | parent->pBone->addChild(deformer->pBone);
|
---|
187 |
|
---|
188 | }
|
---|
189 | }
|
---|
190 |
|
---|
191 | }
|
---|
192 | //-----------------------------------------------------------------------------
|
---|
193 | DeformerEntry* XsiSkeletonExporter::getDeformer(const String& name,
|
---|
194 | DeformerMap& deformers)
|
---|
195 | {
|
---|
196 | // Look in case sensitive list first
|
---|
197 | DeformerMap::iterator i = deformers.find(name);
|
---|
198 | if (i == deformers.end())
|
---|
199 | {
|
---|
200 | String lcaseName = name;
|
---|
201 | StringUtil::toLowerCase(lcaseName);
|
---|
202 | i = mLowerCaseDeformerMap.find(lcaseName);
|
---|
203 | if (i == mLowerCaseDeformerMap.end())
|
---|
204 | {
|
---|
205 | return 0;
|
---|
206 | }
|
---|
207 | else
|
---|
208 | {
|
---|
209 | return i->second;
|
---|
210 | }
|
---|
211 | }
|
---|
212 | else
|
---|
213 | {
|
---|
214 | return i->second;
|
---|
215 | }
|
---|
216 |
|
---|
217 | }
|
---|
218 | //-----------------------------------------------------------------------------
|
---|
219 | void XsiSkeletonExporter::linkBoneWithParent(DeformerEntry* child,
|
---|
220 | DeformerMap& deformers, std::list<DeformerEntry*>& deformerList)
|
---|
221 | {
|
---|
222 | X3DObject parent(child->obj.GetParent());
|
---|
223 | String childName = XSItoOgre(child->obj.GetName());
|
---|
224 |
|
---|
225 | if (child->obj == mXsiSceneRoot /* safety check for start node */)
|
---|
226 | return;
|
---|
227 |
|
---|
228 | // Check for parenting by a chain end effector
|
---|
229 | // These are sneaky little buggers - we actually want to attach the
|
---|
230 | // child to the end of the final bone in the chain
|
---|
231 | if (parent.IsA(XSI::siChainEffectorID))
|
---|
232 | {
|
---|
233 | ChainEffector effector(parent);
|
---|
234 | CRefArray chainBones = effector.GetRoot().GetBones();
|
---|
235 | // get the last
|
---|
236 | parent = chainBones[chainBones.GetCount()-1];
|
---|
237 | child->parentIsChainEndEffector = true;
|
---|
238 |
|
---|
239 | }
|
---|
240 | // is the parent the scene root?
|
---|
241 | if (parent == mXsiSceneRoot)
|
---|
242 | {
|
---|
243 | // we hit the root node
|
---|
244 | }
|
---|
245 | else
|
---|
246 | {
|
---|
247 |
|
---|
248 | String parentName = XSItoOgre(parent.GetName());
|
---|
249 | // Otherwise, check to see if the parent is in the deformer list
|
---|
250 | DeformerEntry* parentDeformer = getDeformer(parentName, deformers);
|
---|
251 | if (!parentDeformer)
|
---|
252 | {
|
---|
253 | // not found, create entry for parent
|
---|
254 | parentDeformer = new DeformerEntry(deformers.size(), parent);
|
---|
255 | deformers[parentName] = parentDeformer;
|
---|
256 | deformerList.push_back(parentDeformer);
|
---|
257 | LogOgreAndXSI(CString(L"Added ") + parent.GetName() +
|
---|
258 | CString(L" as a parent of ") + child->obj.GetName() );
|
---|
259 | }
|
---|
260 |
|
---|
261 | // Link child entry with parent (not bone yet)
|
---|
262 | // link child to parent by name
|
---|
263 | child->parentName = parentName;
|
---|
264 | parentDeformer->childNames.push_back(childName);
|
---|
265 |
|
---|
266 |
|
---|
267 |
|
---|
268 |
|
---|
269 | }
|
---|
270 |
|
---|
271 | }
|
---|
272 | //-----------------------------------------------------------------------------
|
---|
273 | void XsiSkeletonExporter::validateAsBone(Skeleton* pSkeleton,
|
---|
274 | DeformerEntry* deformer,
|
---|
275 | DeformerMap& deformers, std::list<DeformerEntry*>& deformerList,
|
---|
276 | AnimationList& animList)
|
---|
277 | {
|
---|
278 | /* The purpose of this method is to find out whether a node in the
|
---|
279 | bone hierarchy is animated, and if not, to eliminate it and propagate
|
---|
280 | it's static transform contribution to it's children.
|
---|
281 | We do this because it's quite easy in XSI to build deep bone chains
|
---|
282 | with intermediate points that are only used for manipulation. We
|
---|
283 | don't want to include all of those.
|
---|
284 | */
|
---|
285 |
|
---|
286 | // TODO
|
---|
287 |
|
---|
288 |
|
---|
289 | // if we weren't static, create bone
|
---|
290 | if (!deformer->pBone)
|
---|
291 | {
|
---|
292 | String name = XSItoOgre(deformer->obj.GetName());
|
---|
293 | deformer->pBone = pSkeleton->createBone(name, deformer->boneID);
|
---|
294 | MATH::CTransformation trans;
|
---|
295 |
|
---|
296 | if (deformer->parentName.empty())
|
---|
297 | {
|
---|
298 | // set transform on bone to global transform since no parents
|
---|
299 | trans = deformer->obj.GetKinematics().GetGlobal().GetTransform();
|
---|
300 | }
|
---|
301 | else
|
---|
302 | {
|
---|
303 | // set transform on bone to local transform (since child)
|
---|
304 | trans = deformer->obj.GetKinematics().GetLocal().GetTransform();
|
---|
305 | }
|
---|
306 | deformer->pBone->setPosition(XSItoOgre(trans.GetTranslation()));
|
---|
307 | deformer->pBone->setOrientation(XSItoOgre(trans.GetRotation().GetQuaternion()));
|
---|
308 | deformer->pBone->setScale(XSItoOgre(trans.GetScaling()));
|
---|
309 |
|
---|
310 | // special case a bone which is parented by a chain end
|
---|
311 | if (deformer->parentIsChainEndEffector)
|
---|
312 | {
|
---|
313 | ChainEffector effector(deformer->obj.GetParent());
|
---|
314 | CRefArray chainBones = effector.GetRoot().GetBones();
|
---|
315 | // get the last
|
---|
316 | X3DObject endBone = chainBones[chainBones.GetCount()-1];
|
---|
317 | // offset along X the length of the bone
|
---|
318 | double boneLen = endBone.GetParameterValue(L"Length");
|
---|
319 | deformer->pBone->setPosition(
|
---|
320 | deformer->pBone->getPosition() + Vector3::UNIT_X * boneLen);
|
---|
321 | }
|
---|
322 |
|
---|
323 | }
|
---|
324 |
|
---|
325 | }
|
---|
326 | //-----------------------------------------------------------------------------
|
---|
327 | void XsiSkeletonExporter::processActionSource(const XSI::ActionSource& actSource,
|
---|
328 | DeformerMap& deformers)
|
---|
329 | {
|
---|
330 | // Clear existing deformer links
|
---|
331 | for(DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di)
|
---|
332 | {
|
---|
333 | for (int tt = XTT_POS_X; tt < XTT_COUNT; ++tt)
|
---|
334 | {
|
---|
335 | di->second->xsiTrack[tt].ResetObject();
|
---|
336 | }
|
---|
337 | }
|
---|
338 | // Get all the items
|
---|
339 | CRefArray items = actSource.GetItems();
|
---|
340 | for (int i = 0; i < items.GetCount(); ++i)
|
---|
341 | {
|
---|
342 | XSI::AnimationSourceItem item = items[i];
|
---|
343 |
|
---|
344 | // Check the target
|
---|
345 | String target = XSItoOgre(item.GetTarget());
|
---|
346 | size_t firstDotPos = target.find_first_of(".");
|
---|
347 | size_t lastDotPos = target.find_last_of(".");
|
---|
348 | if (firstDotPos != String::npos && lastDotPos != String::npos)
|
---|
349 | {
|
---|
350 | String targetName = target.substr(0, firstDotPos);
|
---|
351 | String paramName = target.substr(lastDotPos+1,
|
---|
352 | target.size() - lastDotPos - 1);
|
---|
353 | // locate deformer
|
---|
354 | DeformerEntry* deformer = getDeformer(targetName, deformers);
|
---|
355 | if (deformer)
|
---|
356 | {
|
---|
357 | // determine parameter
|
---|
358 | std::map<String, int>::iterator pi = mXSITrackTypeNames.find(paramName);
|
---|
359 | if (pi != mXSITrackTypeNames.end())
|
---|
360 | {
|
---|
361 | deformer->xsiTrack[pi->second] = item;
|
---|
362 | deformer->hasAnyTracks = true;
|
---|
363 | }
|
---|
364 | }
|
---|
365 | }
|
---|
366 |
|
---|
367 | }
|
---|
368 | }
|
---|
369 | //-----------------------------------------------------------------------------
|
---|
370 | void XsiSkeletonExporter::createAnimations(Skeleton* pSkel,
|
---|
371 | DeformerMap& deformers, float fps, AnimationList& animList)
|
---|
372 | {
|
---|
373 | for (AnimationList::iterator ai = animList.begin(); ai != animList.end(); ++ai)
|
---|
374 | {
|
---|
375 | AnimationEntry& animEntry = *ai;
|
---|
376 |
|
---|
377 | float animLength = (float)(animEntry.endFrame - animEntry.startFrame) / fps;
|
---|
378 | StringUtil::StrStreamType str;
|
---|
379 | str << "Creating animation " << animEntry.animationName <<
|
---|
380 | " with length " << animLength << " seconds";
|
---|
381 | LogOgreAndXSI(str.str());
|
---|
382 | Animation* anim = pSkel->createAnimation(animEntry.animationName, animLength);
|
---|
383 |
|
---|
384 | if (animEntry.ikSample)
|
---|
385 | {
|
---|
386 | createAnimationTracksSampled(anim, animEntry, deformers, fps);
|
---|
387 | }
|
---|
388 | else
|
---|
389 | {
|
---|
390 | createAnimationTracksDirect(anim, animEntry, deformers, fps);
|
---|
391 | }
|
---|
392 |
|
---|
393 | }
|
---|
394 | }
|
---|
395 | //-----------------------------------------------------------------------------
|
---|
396 | void XsiSkeletonExporter::determineAnimationLengths(AnimationList& animList)
|
---|
397 | {
|
---|
398 | for (AnimationList::iterator ai = animList.begin(); ai != animList.end(); ++ai)
|
---|
399 | {
|
---|
400 | AnimationEntry& animEntry = *ai;
|
---|
401 | determineAnimationLength(animEntry);
|
---|
402 | }
|
---|
403 | }
|
---|
404 | //-----------------------------------------------------------------------------
|
---|
405 | void XsiSkeletonExporter::determineAnimationLength(AnimationEntry& animEntry)
|
---|
406 | {
|
---|
407 | bool first = true;
|
---|
408 | CRefArray items = animEntry.source.GetItems();
|
---|
409 | for (int i = 0; i < items.GetCount(); ++i)
|
---|
410 | {
|
---|
411 | XSI::AnimationSourceItem item = items[i];
|
---|
412 |
|
---|
413 | // skip invalid or non-FCurve items
|
---|
414 | if (!item.IsValid() || !item.GetSource().IsA(XSI::siFCurveID))
|
---|
415 | continue;
|
---|
416 |
|
---|
417 | FCurve fcurve = item.GetSource();
|
---|
418 | CRefArray keys = fcurve.GetKeys();
|
---|
419 | for (int k = 0; k < keys.GetCount(); ++k)
|
---|
420 | {
|
---|
421 | long currFrame = (long)(fcurve.GetKeyTime(k).GetTime());
|
---|
422 | if (first)
|
---|
423 | {
|
---|
424 | animEntry.startFrame = currFrame;
|
---|
425 | animEntry.endFrame = currFrame;
|
---|
426 | first = false;
|
---|
427 | }
|
---|
428 | else
|
---|
429 | {
|
---|
430 | if (currFrame < animEntry.startFrame)
|
---|
431 | {
|
---|
432 | animEntry.startFrame = currFrame;
|
---|
433 | }
|
---|
434 | if (currFrame > animEntry.endFrame)
|
---|
435 | {
|
---|
436 | animEntry.endFrame = currFrame;
|
---|
437 | }
|
---|
438 | }
|
---|
439 |
|
---|
440 | }
|
---|
441 | }
|
---|
442 |
|
---|
443 |
|
---|
444 |
|
---|
445 | }
|
---|
446 | //-----------------------------------------------------------------------------
|
---|
447 | void XsiSkeletonExporter::buildKeyframeList(DeformerEntry* deformer,
|
---|
448 | AnimationEntry& animEntry)
|
---|
449 | {
|
---|
450 | bool first = true;
|
---|
451 | animEntry.frames.clear();
|
---|
452 | for (int tt = XTT_POS_X; tt < XTT_COUNT; ++tt)
|
---|
453 | {
|
---|
454 | AnimationSourceItem item = deformer->xsiTrack[tt];
|
---|
455 | // skip invalid or non-FCurve items
|
---|
456 | if (!item.IsValid() || !item.GetSource().IsA(XSI::siFCurveID))
|
---|
457 | continue;
|
---|
458 |
|
---|
459 | FCurve fcurve = item.GetSource();
|
---|
460 | CRefArray keys = fcurve.GetKeys();
|
---|
461 | for (int k = 0; k < keys.GetCount(); ++k)
|
---|
462 | {
|
---|
463 | long currFrame = (long)(fcurve.GetKeyTime(k).GetTime());
|
---|
464 | if (first)
|
---|
465 | {
|
---|
466 | animEntry.startFrame = currFrame;
|
---|
467 | animEntry.endFrame = currFrame;
|
---|
468 | first = false;
|
---|
469 | }
|
---|
470 | else
|
---|
471 | {
|
---|
472 | if (currFrame < animEntry.startFrame)
|
---|
473 | {
|
---|
474 | animEntry.startFrame = currFrame;
|
---|
475 | }
|
---|
476 | if (currFrame > animEntry.endFrame)
|
---|
477 | {
|
---|
478 | animEntry.endFrame = currFrame;
|
---|
479 | }
|
---|
480 | }
|
---|
481 |
|
---|
482 | animEntry.frames.insert(currFrame);
|
---|
483 | }
|
---|
484 | }
|
---|
485 |
|
---|
486 |
|
---|
487 |
|
---|
488 |
|
---|
489 | }
|
---|
490 | //-----------------------------------------------------------------------------
|
---|
491 | void XsiSkeletonExporter::createAnimationTracksSampled(Animation* pAnim,
|
---|
492 | AnimationEntry& animEntry, DeformerMap& deformers, float fps)
|
---|
493 | {
|
---|
494 | // Save the current selection
|
---|
495 | CString seltext(mXsiApp.GetSelection().GetAsText());
|
---|
496 |
|
---|
497 | // Clear current animation
|
---|
498 | CValueArray args;
|
---|
499 | CValue dummy;
|
---|
500 | mXsiApp.ExecuteCommand(L"SelectAll", args, dummy);
|
---|
501 | mXsiApp.ExecuteCommand(L"RemoveAllAnimation", args, dummy);
|
---|
502 |
|
---|
503 | // Reset selection
|
---|
504 | mXsiApp.GetSelection().SetAsText(seltext);
|
---|
505 |
|
---|
506 |
|
---|
507 | // Create all tracks first
|
---|
508 | std::vector<AnimationTrack*> deformerTracks;
|
---|
509 | deformerTracks.resize(deformers.size());
|
---|
510 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di)
|
---|
511 | {
|
---|
512 | DeformerEntry* deformer = di->second;
|
---|
513 | AnimationTrack* track = pAnim->createTrack(deformer->boneID, deformer->pBone);
|
---|
514 | deformerTracks[deformer->boneID] = track;
|
---|
515 | }
|
---|
516 |
|
---|
517 | Model model = placeAnimationInMixer(animEntry);
|
---|
518 |
|
---|
519 | // Iterate over frames, keying as we go
|
---|
520 | long numFrames = animEntry.endFrame - animEntry.startFrame;
|
---|
521 | if (animEntry.ikSampleInterval == 0)
|
---|
522 | {
|
---|
523 | // Don't allow zero samplign frequency - infinite loop!
|
---|
524 | animEntry.ikSampleInterval = 5.0f;
|
---|
525 | }
|
---|
526 |
|
---|
527 | for (long frame = 0; frame < numFrames; frame += animEntry.ikSampleInterval)
|
---|
528 | {
|
---|
529 | sampleAllBones(deformers, deformerTracks, frame, fps);
|
---|
530 |
|
---|
531 | }
|
---|
532 | // sample final frame
|
---|
533 | sampleAllBones(deformers, deformerTracks,
|
---|
534 | animEntry.endFrame - animEntry.startFrame, fps);
|
---|
535 |
|
---|
536 | // remove the clip we added
|
---|
537 | Mixer mixer(model.GetMixer());
|
---|
538 | removeAllFromMixer(mixer);
|
---|
539 |
|
---|
540 | }
|
---|
541 | //-----------------------------------------------------------------------------
|
---|
542 | void XsiSkeletonExporter::establishInitialTransforms(DeformerMap& deformers)
|
---|
543 | {
|
---|
544 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di)
|
---|
545 | {
|
---|
546 | DeformerEntry* deformer = di->second;
|
---|
547 | if (deformer->pBone->getParent() == 0)
|
---|
548 | {
|
---|
549 | // Based on global
|
---|
550 | deformer->initialXform =
|
---|
551 | deformer->obj.GetKinematics().GetGlobal().GetTransform();
|
---|
552 | }
|
---|
553 | else
|
---|
554 | {
|
---|
555 | // Based on local
|
---|
556 | deformer->initialXform =
|
---|
557 | deformer->obj.GetKinematics().GetLocal().GetTransform();
|
---|
558 | }
|
---|
559 |
|
---|
560 | }
|
---|
561 | }
|
---|
562 | //-----------------------------------------------------------------------------
|
---|
563 | void XsiSkeletonExporter::sampleAllBones(DeformerMap& deformers,
|
---|
564 | std::vector<AnimationTrack*> deformerTracks, double frame, float fps)
|
---|
565 | {
|
---|
566 | CValueArray args;
|
---|
567 | CValue dummy;
|
---|
568 | args.Resize(2);
|
---|
569 | // set the playcontrol
|
---|
570 | args[0] = L"PlayControl.Key";
|
---|
571 | args[1] = frame;
|
---|
572 | mXsiApp.ExecuteCommand(L"SetValue", args, dummy);
|
---|
573 | args[0] = L"PlayControl.Current";
|
---|
574 | mXsiApp.ExecuteCommand(L"SetValue", args, dummy);
|
---|
575 |
|
---|
576 | // Refresh
|
---|
577 | mXsiApp.ExecuteCommand(L"Refresh", CValueArray(), dummy);
|
---|
578 | // Sample all bones
|
---|
579 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di)
|
---|
580 | {
|
---|
581 | DeformerEntry* deformer = di->second;
|
---|
582 | AnimationTrack* track = deformerTracks[deformer->boneID];
|
---|
583 |
|
---|
584 | double initposx, initposy, initposz;
|
---|
585 | deformer->initialXform.GetTranslationValues(initposx, initposy, initposz);
|
---|
586 | double initrotx, initroty, initrotz;
|
---|
587 | deformer->initialXform.GetRotation().GetXYZAngles(initrotx, initroty, initrotz);
|
---|
588 | double initsclx, initscly, initsclz;
|
---|
589 | deformer->initialXform.GetScalingValues(initsclx, initscly, initsclz);
|
---|
590 | XSI::MATH::CMatrix4 invTrans = deformer->initialXform.GetMatrix4();
|
---|
591 | invTrans.InvertInPlace();
|
---|
592 |
|
---|
593 | XSI::MATH::CTransformation transformation;
|
---|
594 | if (deformer->pBone->getParent() == 0)
|
---|
595 | {
|
---|
596 | // Based on global
|
---|
597 | transformation =
|
---|
598 | deformer->obj.GetKinematics().GetGlobal().GetTransform();
|
---|
599 | }
|
---|
600 | else
|
---|
601 | {
|
---|
602 | // Based on local
|
---|
603 | transformation =
|
---|
604 | deformer->obj.GetKinematics().GetLocal().GetTransform();
|
---|
605 | }
|
---|
606 |
|
---|
607 | // Make relative to initial
|
---|
608 | XSI::MATH::CMatrix4 transformationMatrix = transformation.GetMatrix4();
|
---|
609 | transformationMatrix.MulInPlace(invTrans);
|
---|
610 | transformation.SetMatrix4(transformationMatrix);
|
---|
611 |
|
---|
612 | double posx, posy, posz;
|
---|
613 | transformation.GetTranslationValues(posx, posy, posz);
|
---|
614 |
|
---|
615 | // create keyframe
|
---|
616 | KeyFrame* kf = track->createKeyFrame((float)frame / fps);
|
---|
617 | // not sure why inverted transform doesn't work for position, but it doesn't
|
---|
618 | // I thought XSI used same transform order as OGRE
|
---|
619 | kf->setTranslate(XSItoOgre(transformation.GetTranslation()));
|
---|
620 | //kf->setTranslate(Vector3(posx - initposx, posy - initposy, posz - initposz));
|
---|
621 | kf->setRotation(XSItoOgre(transformation.GetRotationQuaternion()));
|
---|
622 | kf->setScale(XSItoOgre(transformation.GetScaling()));
|
---|
623 |
|
---|
624 | }
|
---|
625 |
|
---|
626 | }
|
---|
627 | //-----------------------------------------------------------------------------
|
---|
628 | void XsiSkeletonExporter::createAnimationTracksDirect(Animation* pAnim,
|
---|
629 | AnimationEntry& animEntry, DeformerMap& deformers, float fps)
|
---|
630 | {
|
---|
631 |
|
---|
632 | // tease out all the animation source items
|
---|
633 | processActionSource(animEntry.source, deformers);
|
---|
634 |
|
---|
635 | /* We have to iterate over the list of deformers, and create a track
|
---|
636 | * for each one. Since XSI stores keys for all 9 components separately,
|
---|
637 | * we need to bake OGRE keyframes (which include position, rotation and
|
---|
638 | * translation) by interpolation. We can merge the list of frames from all
|
---|
639 | * action source item fcurves, then use the Eval method to get XSI to sample
|
---|
640 | * the curve properly for us.
|
---|
641 | * We will also use this to ensure there is a keyframe at the start and end of the
|
---|
642 | * animation.
|
---|
643 | */
|
---|
644 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di)
|
---|
645 | {
|
---|
646 | DeformerEntry* deformer = di->second;
|
---|
647 |
|
---|
648 | // Skip deformers which have no animated parameters
|
---|
649 | if (!deformer->hasAnyTracks)
|
---|
650 | continue;
|
---|
651 |
|
---|
652 | StringUtil::StrStreamType str;
|
---|
653 | str << "Creating track for bone " << deformer->pBone->getName() <<
|
---|
654 | "(" << deformer->boneID << ")";
|
---|
655 | LogOgreAndXSI(str.str());
|
---|
656 | // create track
|
---|
657 | AnimationTrack* track = pAnim->createTrack(deformer->boneID, deformer->pBone);
|
---|
658 |
|
---|
659 | XSI::MATH::CTransformation initialTransformation;
|
---|
660 | if (deformer->pBone->getParent() == 0)
|
---|
661 | {
|
---|
662 | // Based on global
|
---|
663 | initialTransformation =
|
---|
664 | deformer->obj.GetKinematics().GetGlobal().GetTransform();
|
---|
665 | }
|
---|
666 | else
|
---|
667 | {
|
---|
668 | // Based on local
|
---|
669 | initialTransformation =
|
---|
670 | deformer->obj.GetKinematics().GetLocal().GetTransform();
|
---|
671 | }
|
---|
672 | XSI::MATH::CMatrix4 invTrans = initialTransformation.GetMatrix4();
|
---|
673 | invTrans.InvertInPlace();
|
---|
674 |
|
---|
675 | double initposx, initposy, initposz;
|
---|
676 | initialTransformation.GetTranslationValues(initposx, initposy, initposz);
|
---|
677 | double initrotx, initroty, initrotz;
|
---|
678 | initialTransformation.GetRotation().GetXYZAngles(initrotx, initroty, initrotz);
|
---|
679 | double initsclx, initscly, initsclz;
|
---|
680 | initialTransformation.GetScalingValues(initsclx, initscly, initsclz);
|
---|
681 |
|
---|
682 |
|
---|
683 | // Get the keyframe numbers from all XSI tracks
|
---|
684 | // XSI tracks might be sparse
|
---|
685 | buildKeyframeList(deformer, animEntry);
|
---|
686 |
|
---|
687 | // Iterate over the frames and pull out the values we need
|
---|
688 | // bake keyframe for all
|
---|
689 | for (std::set<long>::iterator fi = animEntry.frames.begin();
|
---|
690 | fi != animEntry.frames.end(); ++fi)
|
---|
691 | {
|
---|
692 | double posx = deriveKeyFrameValue(deformer->xsiTrack[XTT_POS_X], *fi, initposx);
|
---|
693 | double posy = deriveKeyFrameValue(deformer->xsiTrack[XTT_POS_Y], *fi, initposy);
|
---|
694 | double posz = deriveKeyFrameValue(deformer->xsiTrack[XTT_POS_Z], *fi, initposz);
|
---|
695 | double rotx = deriveKeyFrameValue(deformer->xsiTrack[XTT_ROT_X], *fi, initrotx);
|
---|
696 | double roty = deriveKeyFrameValue(deformer->xsiTrack[XTT_ROT_Y], *fi, initroty);
|
---|
697 | double rotz = deriveKeyFrameValue(deformer->xsiTrack[XTT_ROT_Z], *fi, initrotz);
|
---|
698 | double sclx = deriveKeyFrameValue(deformer->xsiTrack[XTT_SCL_X], *fi, initsclx);
|
---|
699 | double scly = deriveKeyFrameValue(deformer->xsiTrack[XTT_SCL_Y], *fi, initscly);
|
---|
700 | double sclz = deriveKeyFrameValue(deformer->xsiTrack[XTT_SCL_Z], *fi, initsclz);
|
---|
701 |
|
---|
702 |
|
---|
703 | // Build transformation relative to initial
|
---|
704 | XSI::MATH::CTransformation transformation;
|
---|
705 |
|
---|
706 | XSI::MATH::CVector3 scaling(sclx, scly, sclz);
|
---|
707 | transformation.SetScaling(scaling);
|
---|
708 | transformation.SetRotationFromXYZAnglesValues(
|
---|
709 | XSI::MATH::DegreesToRadians(rotx),
|
---|
710 | XSI::MATH::DegreesToRadians(roty),
|
---|
711 | XSI::MATH::DegreesToRadians(rotz),
|
---|
712 | XSI::MATH::CRotation::siXYZ);
|
---|
713 | transformation.SetTranslationFromValues(posx, posy, posz);
|
---|
714 |
|
---|
715 |
|
---|
716 | XSI::MATH::CMatrix4 transformationMatrix = transformation.GetMatrix4();
|
---|
717 | transformationMatrix.MulInPlace(invTrans);
|
---|
718 | transformation.SetMatrix4(transformationMatrix);
|
---|
719 |
|
---|
720 |
|
---|
721 | // create keyframe
|
---|
722 | KeyFrame* kf = track->createKeyFrame((float)(*fi - animEntry.startFrame) / fps);
|
---|
723 | // not sure why inverted transform doesn't work for position, but it doesn't
|
---|
724 | // I thought XSI used same transform order as OGRE
|
---|
725 | //kf->setTranslate(XSItoOgre(transformation.GetTranslation()));
|
---|
726 | kf->setTranslate(Vector3(posx - initposx, posy - initposy, posz - initposz));
|
---|
727 | kf->setRotation(XSItoOgre(transformation.GetRotationQuaternion()));
|
---|
728 | kf->setScale(XSItoOgre(transformation.GetScaling()));
|
---|
729 |
|
---|
730 |
|
---|
731 | }
|
---|
732 | }
|
---|
733 |
|
---|
734 |
|
---|
735 | }
|
---|
736 | //-----------------------------------------------------------------------------
|
---|
737 | double XsiSkeletonExporter::deriveKeyFrameValue(
|
---|
738 | XSI::AnimationSourceItem item, long frame, double defaultVal)
|
---|
739 | {
|
---|
740 | if (item.IsValid())
|
---|
741 | {
|
---|
742 | FCurve curve(item.GetSource());
|
---|
743 | // let fcurve evaluate
|
---|
744 | return curve.Eval(CTime(frame));
|
---|
745 | }
|
---|
746 | else
|
---|
747 | {
|
---|
748 | return defaultVal;
|
---|
749 | }
|
---|
750 | }
|
---|
751 | //-----------------------------------------------------------------------------
|
---|
752 | void XsiSkeletonExporter::cleanup(void)
|
---|
753 | {
|
---|
754 |
|
---|
755 | mLowerCaseDeformerMap.clear();
|
---|
756 |
|
---|
757 | CValueArray args;
|
---|
758 | CValue dummy;
|
---|
759 | args.Resize(1);
|
---|
760 |
|
---|
761 | for (int i = 0; i < mIKSampledAnimations.GetCount(); ++i)
|
---|
762 | {
|
---|
763 | args[0] = mIKSampledAnimations[i];
|
---|
764 | mXsiApp.ExecuteCommand(L"DeleteObj", args, dummy);
|
---|
765 | }
|
---|
766 | mIKSampledAnimations.Clear();
|
---|
767 |
|
---|
768 | }
|
---|
769 | //-----------------------------------------------------------------------------
|
---|
770 | void XsiSkeletonExporter::removeAllFromMixer(Mixer& mixer)
|
---|
771 | {
|
---|
772 | CRefArray tracks(mixer.GetTracks());
|
---|
773 | for (int t = 0; t < tracks.GetCount(); ++t)
|
---|
774 | {
|
---|
775 | Track track(tracks[t]);
|
---|
776 | CRefArray clips(track.GetClips());
|
---|
777 | for (int c = 0; c < clips.GetCount(); ++c)
|
---|
778 | {
|
---|
779 | Clip clip(clips[c]);
|
---|
780 | CValueArray args;
|
---|
781 | CValue dummy;
|
---|
782 | args.Add(clip.GetFullName());
|
---|
783 | mXsiApp.ExecuteCommand(L"DeleteObj", args, dummy);
|
---|
784 | }
|
---|
785 |
|
---|
786 | }
|
---|
787 |
|
---|
788 | }
|
---|
789 | //-----------------------------------------------------------------------------
|
---|
790 | XSI::Mixer XsiSkeletonExporter::getMixer(AnimationEntry& anim)
|
---|
791 | {
|
---|
792 | Model model(anim.source.GetModel());
|
---|
793 | if (!model.HasMixer())
|
---|
794 | {
|
---|
795 | model = mXsiApp.GetActiveSceneRoot();
|
---|
796 | }
|
---|
797 | return model.GetMixer();
|
---|
798 | }
|
---|
799 | //-----------------------------------------------------------------------------
|
---|
800 | XSI::Model XsiSkeletonExporter::placeAnimationInMixer(AnimationEntry& anim)
|
---|
801 | {
|
---|
802 | Mixer mixer(getMixer(anim));
|
---|
803 | Model model(mixer.GetModel()) ;
|
---|
804 |
|
---|
805 | removeAllFromMixer(mixer);
|
---|
806 |
|
---|
807 | // Clear all clips from the mixer
|
---|
808 |
|
---|
809 | CValueArray args;
|
---|
810 | CValue dummy;
|
---|
811 |
|
---|
812 | // Add the new clip to the mixer
|
---|
813 | CRefArray tracks(mixer.GetTracks());
|
---|
814 | if (tracks.GetCount() == 0)
|
---|
815 | {
|
---|
816 | // Must be at least one track
|
---|
817 | args.Resize(3);
|
---|
818 | args[0] = model.GetFullName();
|
---|
819 | args[1] = mixer.GetFullName();
|
---|
820 | args[2] = 0.0f;
|
---|
821 | mXsiApp.ExecuteCommand(L"AddTrack", args, dummy);
|
---|
822 | tracks = mixer.GetTracks();
|
---|
823 | }
|
---|
824 | Track instrack(tracks[0]);
|
---|
825 | args.Resize(5);
|
---|
826 | args[0] = model.GetFullName(); // target model
|
---|
827 | args[1] = anim.source.GetFullName(); // source
|
---|
828 | args[2] = L""; // compound clip
|
---|
829 | args[3] = instrack.GetFullName();
|
---|
830 | args[4] = 0.0f; // start frame
|
---|
831 | mXsiApp.ExecuteCommand(L"AddClip", args, dummy);
|
---|
832 |
|
---|
833 |
|
---|
834 | // Reset the animation to frame 0, such that following animations start with the initial bone transforms
|
---|
835 | args.Resize(2);
|
---|
836 | args[0] = L"PlayControl.Key";
|
---|
837 | args[1] = (long)0;
|
---|
838 | mXsiApp.ExecuteCommand(L"SetValue", args, dummy);
|
---|
839 | args[0] = L"PlayControl.Current";
|
---|
840 | args[1] = (long)0;
|
---|
841 | mXsiApp.ExecuteCommand(L"SetValue", args, dummy);
|
---|
842 | mXsiApp.ExecuteCommand(L"Refresh", CValueArray(), dummy);
|
---|
843 |
|
---|
844 |
|
---|
845 | return model;
|
---|
846 |
|
---|
847 | }
|
---|
848 | }
|
---|