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-2006 Torus Knot Software Ltd |
---|
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 | You may alternatively use this source under the terms of a specific version of |
---|
25 | the OGRE Unrestricted License provided you have obtained such a license from |
---|
26 | Torus Knot Software Ltd. |
---|
27 | ----------------------------------------------------------------------------- |
---|
28 | */ |
---|
29 | #include "OgreXSISkeletonExporter.h" |
---|
30 | #include "OgreResourceGroupManager.h" |
---|
31 | #include "OgreSkeletonManager.h" |
---|
32 | #include "OgreSkeleton.h" |
---|
33 | #include "OgreBone.h" |
---|
34 | #include "OgreAnimation.h" |
---|
35 | #include "OgreAnimationTrack.h" |
---|
36 | #include "OgreKeyFrame.h" |
---|
37 | #include "OgreSkeletonSerializer.h" |
---|
38 | #include "OgreQuaternion.h" |
---|
39 | #include <xsi_model.h> |
---|
40 | #include <xsi_kinematics.h> |
---|
41 | #include <xsi_kinematicstate.h> |
---|
42 | #include <xsi_math.h> |
---|
43 | #include <xsi_rotation.h> |
---|
44 | #include <xsi_animationsourceitem.h> |
---|
45 | #include <xsi_source.h> |
---|
46 | #include <xsi_fcurve.h> |
---|
47 | #include <xsi_fcurvekey.h> |
---|
48 | #include <xsi_time.h> |
---|
49 | #include <xsi_chaineffector.h> |
---|
50 | #include <xsi_chainroot.h> |
---|
51 | #include <xsi_chainbone.h> |
---|
52 | #include <xsi_matrix4.h> |
---|
53 | #include <xsi_transformation.h> |
---|
54 | #include <xsi_vector3.h> |
---|
55 | #include <xsi_constraint.h> |
---|
56 | #include <xsi_track.h> |
---|
57 | #include <xsi_clip.h> |
---|
58 | #include <xsi_selection.h> |
---|
59 | #include <xsi_statickinematicstate.h> |
---|
60 | |
---|
61 | using namespace XSI; |
---|
62 | |
---|
63 | namespace Ogre |
---|
64 | { |
---|
65 | //----------------------------------------------------------------------------- |
---|
66 | XsiSkeletonExporter::XsiSkeletonExporter() |
---|
67 | { |
---|
68 | mXsiSceneRoot = X3DObject(mXsiApp.GetActiveSceneRoot()); |
---|
69 | mXSITrackTypeNames["posx"] = XTT_POS_X; |
---|
70 | mXSITrackTypeNames["posy"] = XTT_POS_Y; |
---|
71 | mXSITrackTypeNames["posz"] = XTT_POS_Z; |
---|
72 | mXSITrackTypeNames["rotx"] = XTT_ROT_X; |
---|
73 | mXSITrackTypeNames["roty"] = XTT_ROT_Y; |
---|
74 | mXSITrackTypeNames["rotz"] = XTT_ROT_Z; |
---|
75 | mXSITrackTypeNames["sclx"] = XTT_SCL_X; |
---|
76 | mXSITrackTypeNames["scly"] = XTT_SCL_Y; |
---|
77 | mXSITrackTypeNames["sclz"] = XTT_SCL_Z; |
---|
78 | } |
---|
79 | //----------------------------------------------------------------------------- |
---|
80 | XsiSkeletonExporter::~XsiSkeletonExporter() |
---|
81 | { |
---|
82 | } |
---|
83 | //----------------------------------------------------------------------------- |
---|
84 | const AxisAlignedBox& XsiSkeletonExporter::exportSkeleton(const String& skeletonFileName, |
---|
85 | DeformerMap& deformers, float framesPerSecond, AnimationList& animList) |
---|
86 | { |
---|
87 | LogOgreAndXSI(L"** Begin OGRE Skeleton Export **"); |
---|
88 | |
---|
89 | copyDeformerMap(deformers); |
---|
90 | |
---|
91 | SkeletonPtr skeleton = SkeletonManager::getSingleton().create("export", |
---|
92 | ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); |
---|
93 | // construct the hierarchy |
---|
94 | buildBoneHierarchy(skeleton.get(), deformers, animList); |
---|
95 | |
---|
96 | // progress report |
---|
97 | ProgressManager::getSingleton().progress(); |
---|
98 | |
---|
99 | establishInitialTransforms(deformers); |
---|
100 | |
---|
101 | // create animations |
---|
102 | mAABB.setNull(); |
---|
103 | createAnimations(skeleton.get(), deformers, framesPerSecond, animList, mAABB); |
---|
104 | // progress report |
---|
105 | ProgressManager::getSingleton().progress(); |
---|
106 | |
---|
107 | // Optimise |
---|
108 | skeleton->optimiseAllAnimations(); |
---|
109 | |
---|
110 | SkeletonSerializer ser; |
---|
111 | ser.exportSkeleton(skeleton.get(), skeletonFileName); |
---|
112 | // progress report |
---|
113 | ProgressManager::getSingleton().progress(); |
---|
114 | |
---|
115 | LogOgreAndXSI(L"** OGRE Skeleton Export Complete **"); |
---|
116 | |
---|
117 | cleanup(); |
---|
118 | |
---|
119 | return mAABB; |
---|
120 | |
---|
121 | } |
---|
122 | //----------------------------------------------------------------------------- |
---|
123 | void XsiSkeletonExporter::copyDeformerMap(DeformerMap& deformers) |
---|
124 | { |
---|
125 | // Make lower-case version |
---|
126 | // some XSI animations appear to like to use case insensitive references :( |
---|
127 | for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i) |
---|
128 | { |
---|
129 | DeformerEntry* deformer = i->second; |
---|
130 | String name = XSItoOgre(deformer->obj.GetName()); |
---|
131 | StringUtil::toLowerCase(name); |
---|
132 | mLowerCaseDeformerMap[name] = deformer; |
---|
133 | } |
---|
134 | } |
---|
135 | //----------------------------------------------------------------------------- |
---|
136 | void XsiSkeletonExporter::buildBoneHierarchy(Skeleton* pSkeleton, |
---|
137 | DeformerMap& deformers, AnimationList& animList) |
---|
138 | { |
---|
139 | /// Copy all entries from map into a list so iterators won't get invalidated |
---|
140 | std::list<DeformerEntry*> deformerList; |
---|
141 | LogOgreAndXSI(L"-- Bones with vertex assignments:"); |
---|
142 | for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i) |
---|
143 | { |
---|
144 | DeformerEntry* deformer = i->second; |
---|
145 | deformerList.push_back(deformer); |
---|
146 | LogOgreAndXSI(deformer->obj.GetName()); |
---|
147 | } |
---|
148 | |
---|
149 | /* XSI allows you to use any object at all as a bone, not just chain elements. |
---|
150 | A typical choice is a hierarchy of nulls, for example. In order to |
---|
151 | build a skeleton hierarchy which represents the actual one, we need |
---|
152 | to find the relationships between all the deformers that we found. |
---|
153 | |
---|
154 | Well do this by navigating up the scene tree from each bone, looking for |
---|
155 | a match in the existing bone list or creating a new one where we need it |
---|
156 | to reach the root. We add bones even if they're not assigned vertices |
---|
157 | because the animation may depend on them. If the |
---|
158 | traversal hits the scene root this bone is clearly a root bone |
---|
159 | (there may be more than one). |
---|
160 | */ |
---|
161 | for (std::list<DeformerEntry*>::iterator i = deformerList.begin(); i != deformerList.end(); ++i) |
---|
162 | { |
---|
163 | DeformerEntry* deformer = *i; |
---|
164 | if (deformer->parentName.empty()) |
---|
165 | { |
---|
166 | linkBoneWithParent(deformer, deformers, deformerList); |
---|
167 | } |
---|
168 | } |
---|
169 | |
---|
170 | // Now eliminate all bones without any animated kine parameters |
---|
171 | // Need to do this after we've determined all relationships |
---|
172 | for (std::list<DeformerEntry*>::iterator i = deformerList.begin(); i != deformerList.end(); ++i) |
---|
173 | { |
---|
174 | DeformerEntry* deformer = *i; |
---|
175 | validateAsBone(pSkeleton, deformer, deformers, deformerList, animList); |
---|
176 | } |
---|
177 | |
---|
178 | // Now link |
---|
179 | for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i) |
---|
180 | { |
---|
181 | DeformerEntry* deformer = i->second; |
---|
182 | |
---|
183 | // link to parent |
---|
184 | if (!deformer->parentName.empty()) |
---|
185 | { |
---|
186 | DeformerEntry* parent = getDeformer(deformer->parentName, deformers); |
---|
187 | assert (parent && "Parent not found"); |
---|
188 | assert (deformer->pBone && "Child bone not created"); |
---|
189 | assert(parent->pBone && "Parent bone not created"); |
---|
190 | parent->pBone->addChild(deformer->pBone); |
---|
191 | |
---|
192 | } |
---|
193 | } |
---|
194 | |
---|
195 | } |
---|
196 | //----------------------------------------------------------------------------- |
---|
197 | DeformerEntry* XsiSkeletonExporter::getDeformer(const String& name, |
---|
198 | DeformerMap& deformers) |
---|
199 | { |
---|
200 | // Look in case sensitive list first |
---|
201 | DeformerMap::iterator i = deformers.find(name); |
---|
202 | if (i == deformers.end()) |
---|
203 | { |
---|
204 | String lcaseName = name; |
---|
205 | StringUtil::toLowerCase(lcaseName); |
---|
206 | i = mLowerCaseDeformerMap.find(lcaseName); |
---|
207 | if (i == mLowerCaseDeformerMap.end()) |
---|
208 | { |
---|
209 | return 0; |
---|
210 | } |
---|
211 | else |
---|
212 | { |
---|
213 | return i->second; |
---|
214 | } |
---|
215 | } |
---|
216 | else |
---|
217 | { |
---|
218 | return i->second; |
---|
219 | } |
---|
220 | |
---|
221 | } |
---|
222 | //----------------------------------------------------------------------------- |
---|
223 | void XsiSkeletonExporter::linkBoneWithParent(DeformerEntry* child, |
---|
224 | DeformerMap& deformers, std::list<DeformerEntry*>& deformerList) |
---|
225 | { |
---|
226 | X3DObject parent(child->obj.GetParent()); |
---|
227 | String childName = XSItoOgre(child->obj.GetName()); |
---|
228 | |
---|
229 | if (child->obj == mXsiSceneRoot /* safety check for start node */) |
---|
230 | return; |
---|
231 | |
---|
232 | // Check for parenting by a chain end effector |
---|
233 | // These are sneaky little buggers - we actually want to attach the |
---|
234 | // child to the end of the final bone in the chain |
---|
235 | if (parent.IsA(XSI::siChainEffectorID)) |
---|
236 | { |
---|
237 | ChainEffector effector(parent); |
---|
238 | CRefArray chainBones = effector.GetRoot().GetBones(); |
---|
239 | // get the last |
---|
240 | parent = chainBones[chainBones.GetCount()-1]; |
---|
241 | child->parentIsChainEndEffector = true; |
---|
242 | |
---|
243 | } |
---|
244 | // is the parent the scene root? |
---|
245 | if (parent == mXsiSceneRoot) |
---|
246 | { |
---|
247 | // we hit the root node |
---|
248 | } |
---|
249 | else |
---|
250 | { |
---|
251 | |
---|
252 | String parentName = XSItoOgre(parent.GetName()); |
---|
253 | // Otherwise, check to see if the parent is in the deformer list |
---|
254 | DeformerEntry* parentDeformer = getDeformer(parentName, deformers); |
---|
255 | if (!parentDeformer) |
---|
256 | { |
---|
257 | // not found, create entry for parent |
---|
258 | parentDeformer = new DeformerEntry(deformers.size(), parent); |
---|
259 | deformers[parentName] = parentDeformer; |
---|
260 | deformerList.push_back(parentDeformer); |
---|
261 | LogOgreAndXSI(CString(L"Added ") + parent.GetName() + |
---|
262 | CString(L" as a parent of ") + child->obj.GetName() ); |
---|
263 | } |
---|
264 | |
---|
265 | // Link child entry with parent (not bone yet) |
---|
266 | // link child to parent by name |
---|
267 | child->parentName = parentName; |
---|
268 | parentDeformer->childNames.push_back(childName); |
---|
269 | |
---|
270 | |
---|
271 | |
---|
272 | |
---|
273 | } |
---|
274 | |
---|
275 | } |
---|
276 | //----------------------------------------------------------------------------- |
---|
277 | void XsiSkeletonExporter::validateAsBone(Skeleton* pSkeleton, |
---|
278 | DeformerEntry* deformer, |
---|
279 | DeformerMap& deformers, std::list<DeformerEntry*>& deformerList, |
---|
280 | AnimationList& animList) |
---|
281 | { |
---|
282 | /* The purpose of this method is to find out whether a node in the |
---|
283 | bone hierarchy is animated, and if not, to eliminate it and propagate |
---|
284 | it's static transform contribution to it's children. |
---|
285 | We do this because it's quite easy in XSI to build deep bone chains |
---|
286 | with intermediate points that are only used for manipulation. We |
---|
287 | don't want to include all of those. |
---|
288 | */ |
---|
289 | |
---|
290 | // TODO |
---|
291 | |
---|
292 | |
---|
293 | // if we weren't static, create bone |
---|
294 | if (!deformer->pBone) |
---|
295 | { |
---|
296 | String name = XSItoOgre(deformer->obj.GetName()); |
---|
297 | deformer->pBone = pSkeleton->createBone(name, deformer->boneID); |
---|
298 | MATH::CTransformation trans; |
---|
299 | |
---|
300 | if (deformer->parentName.empty()) |
---|
301 | { |
---|
302 | // set transform on bone to global transform since no parents |
---|
303 | trans = deformer->obj.GetKinematics().GetGlobal().GetTransform(); |
---|
304 | } |
---|
305 | else |
---|
306 | { |
---|
307 | // set transform on bone to local transform (since child) |
---|
308 | trans = deformer->obj.GetKinematics().GetLocal().GetTransform(); |
---|
309 | } |
---|
310 | deformer->pBone->setPosition(XSItoOgre(trans.GetTranslation())); |
---|
311 | deformer->pBone->setOrientation(XSItoOgre(trans.GetRotation().GetQuaternion())); |
---|
312 | deformer->pBone->setScale(XSItoOgre(trans.GetScaling())); |
---|
313 | |
---|
314 | // special case a bone which is parented by a chain end |
---|
315 | if (deformer->parentIsChainEndEffector) |
---|
316 | { |
---|
317 | ChainEffector effector(deformer->obj.GetParent()); |
---|
318 | CRefArray chainBones = effector.GetRoot().GetBones(); |
---|
319 | // get the last |
---|
320 | X3DObject endBone = chainBones[chainBones.GetCount()-1]; |
---|
321 | // offset along X the length of the bone |
---|
322 | double boneLen = endBone.GetParameterValue(L"Length"); |
---|
323 | deformer->pBone->setPosition( |
---|
324 | deformer->pBone->getPosition() + Vector3::UNIT_X * boneLen); |
---|
325 | } |
---|
326 | |
---|
327 | } |
---|
328 | |
---|
329 | } |
---|
330 | //----------------------------------------------------------------------------- |
---|
331 | void XsiSkeletonExporter::processActionSource(const XSI::ActionSource& actSource, |
---|
332 | DeformerMap& deformers) |
---|
333 | { |
---|
334 | // Clear existing deformer links |
---|
335 | for(DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di) |
---|
336 | { |
---|
337 | for (int tt = XTT_POS_X; tt < XTT_COUNT; ++tt) |
---|
338 | { |
---|
339 | di->second->xsiTrack[tt].ResetObject(); |
---|
340 | } |
---|
341 | } |
---|
342 | // Get all the items |
---|
343 | CRefArray items = actSource.GetItems(); |
---|
344 | for (int i = 0; i < items.GetCount(); ++i) |
---|
345 | { |
---|
346 | XSI::AnimationSourceItem item = items[i]; |
---|
347 | |
---|
348 | // Check the target |
---|
349 | String target = XSItoOgre(item.GetTarget()); |
---|
350 | size_t firstDotPos = target.find_first_of("."); |
---|
351 | size_t lastDotPos = target.find_last_of("."); |
---|
352 | if (firstDotPos != String::npos && lastDotPos != String::npos) |
---|
353 | { |
---|
354 | String targetName = target.substr(0, firstDotPos); |
---|
355 | String paramName = target.substr(lastDotPos+1, |
---|
356 | target.size() - lastDotPos - 1); |
---|
357 | // locate deformer |
---|
358 | DeformerEntry* deformer = getDeformer(targetName, deformers); |
---|
359 | if (deformer) |
---|
360 | { |
---|
361 | // determine parameter |
---|
362 | std::map<String, int>::iterator pi = mXSITrackTypeNames.find(paramName); |
---|
363 | if (pi != mXSITrackTypeNames.end()) |
---|
364 | { |
---|
365 | deformer->xsiTrack[pi->second] = item; |
---|
366 | deformer->hasAnyTracks = true; |
---|
367 | } |
---|
368 | } |
---|
369 | } |
---|
370 | |
---|
371 | } |
---|
372 | } |
---|
373 | //----------------------------------------------------------------------------- |
---|
374 | void XsiSkeletonExporter::createAnimations(Skeleton* pSkel, |
---|
375 | DeformerMap& deformers, float fps, AnimationList& animList, AxisAlignedBox& AABBPadding) |
---|
376 | { |
---|
377 | for (AnimationList::iterator ai = animList.begin(); ai != animList.end(); ++ai) |
---|
378 | { |
---|
379 | AnimationEntry& animEntry = *ai; |
---|
380 | |
---|
381 | // Note that we don't know if this time period includes bone animation |
---|
382 | // but we sample it anyway just in case; animation optimisation will |
---|
383 | // eliminate anything that's redundant |
---|
384 | // A little wasteful perhaps, but it's the only guaranteed way to pick |
---|
385 | // up all the potentially derived effects on deformers |
---|
386 | |
---|
387 | float animLength = (float)(animEntry.endFrame - animEntry.startFrame) / fps; |
---|
388 | StringUtil::StrStreamType str; |
---|
389 | str << "Creating animation " << animEntry.animationName << |
---|
390 | " with length " << animLength << " seconds"; |
---|
391 | LogOgreAndXSI(str.str()); |
---|
392 | Animation* anim = pSkel->createAnimation(animEntry.animationName, animLength); |
---|
393 | |
---|
394 | createAnimationTracksSampled(anim, animEntry, deformers, fps, AABBPadding); |
---|
395 | |
---|
396 | } |
---|
397 | } |
---|
398 | //----------------------------------------------------------------------------- |
---|
399 | void XsiSkeletonExporter::createAnimationTracksSampled(Animation* pAnim, |
---|
400 | AnimationEntry& animEntry, DeformerMap& deformers, float fps, AxisAlignedBox& AABBPadding) |
---|
401 | { |
---|
402 | // Save the current selection |
---|
403 | CString seltext(mXsiApp.GetSelection().GetAsText()); |
---|
404 | |
---|
405 | // Clear current animation |
---|
406 | CValueArray args; |
---|
407 | CValue dummy; |
---|
408 | mXsiApp.ExecuteCommand(L"SelectAll", args, dummy); |
---|
409 | mXsiApp.ExecuteCommand(L"RemoveAllAnimation", args, dummy); |
---|
410 | |
---|
411 | // Reset selection |
---|
412 | mXsiApp.GetSelection().SetAsText(seltext); |
---|
413 | |
---|
414 | |
---|
415 | // Create all tracks first |
---|
416 | std::vector<NodeAnimationTrack*> deformerTracks; |
---|
417 | deformerTracks.resize(deformers.size()); |
---|
418 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di) |
---|
419 | { |
---|
420 | DeformerEntry* deformer = di->second; |
---|
421 | NodeAnimationTrack* track = pAnim->createNodeTrack(deformer->boneID, deformer->pBone); |
---|
422 | deformerTracks[deformer->boneID] = track; |
---|
423 | } |
---|
424 | |
---|
425 | // Iterate over frames, keying as we go |
---|
426 | long numFrames = animEntry.endFrame - animEntry.startFrame; |
---|
427 | if (animEntry.ikSampleInterval == 0) |
---|
428 | { |
---|
429 | // Don't allow zero samplign frequency - infinite loop! |
---|
430 | animEntry.ikSampleInterval = 5.0f; |
---|
431 | } |
---|
432 | |
---|
433 | // Sample all bones from start to before the end frame |
---|
434 | for (long frame = animEntry.startFrame; frame < animEntry.endFrame; |
---|
435 | frame += animEntry.ikSampleInterval) |
---|
436 | { |
---|
437 | Real time = (float)(frame - animEntry.startFrame) / fps; |
---|
438 | sampleAllBones(deformers, deformerTracks, frame, time, fps, AABBPadding); |
---|
439 | |
---|
440 | } |
---|
441 | // sample final frame (must be guaranteed to be done) |
---|
442 | Real time = (float)(animEntry.endFrame - animEntry.startFrame) / fps; |
---|
443 | sampleAllBones(deformers, deformerTracks, animEntry.endFrame, time, fps, AABBPadding); |
---|
444 | |
---|
445 | |
---|
446 | } |
---|
447 | //----------------------------------------------------------------------------- |
---|
448 | void XsiSkeletonExporter::establishInitialTransforms(DeformerMap& deformers) |
---|
449 | { |
---|
450 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di) |
---|
451 | { |
---|
452 | DeformerEntry* deformer = di->second; |
---|
453 | if (deformer->pBone->getParent() == 0) |
---|
454 | { |
---|
455 | // Based on global |
---|
456 | deformer->initialXform = |
---|
457 | deformer->obj.GetKinematics().GetGlobal().GetTransform(); |
---|
458 | } |
---|
459 | else |
---|
460 | { |
---|
461 | // Based on local |
---|
462 | deformer->initialXform = |
---|
463 | deformer->obj.GetKinematics().GetLocal().GetTransform(); |
---|
464 | } |
---|
465 | |
---|
466 | } |
---|
467 | } |
---|
468 | //----------------------------------------------------------------------------- |
---|
469 | void XsiSkeletonExporter::sampleAllBones(DeformerMap& deformers, |
---|
470 | std::vector<NodeAnimationTrack*> deformerTracks, double frame, |
---|
471 | Real time, float fps, AxisAlignedBox& AABBPadding) |
---|
472 | { |
---|
473 | CValueArray args; |
---|
474 | CValue dummy; |
---|
475 | args.Resize(2); |
---|
476 | // set the playcontrol |
---|
477 | args[0] = L"PlayControl.Key"; |
---|
478 | args[1] = frame; |
---|
479 | mXsiApp.ExecuteCommand(L"SetValue", args, dummy); |
---|
480 | args[0] = L"PlayControl.Current"; |
---|
481 | mXsiApp.ExecuteCommand(L"SetValue", args, dummy); |
---|
482 | |
---|
483 | // Refresh |
---|
484 | mXsiApp.ExecuteCommand(L"Refresh", CValueArray(), dummy); |
---|
485 | // Sample all bones |
---|
486 | for (DeformerMap::iterator di = deformers.begin(); di != deformers.end(); ++di) |
---|
487 | { |
---|
488 | DeformerEntry* deformer = di->second; |
---|
489 | NodeAnimationTrack* track = deformerTracks[deformer->boneID]; |
---|
490 | |
---|
491 | double initposx, initposy, initposz; |
---|
492 | deformer->initialXform.GetTranslationValues(initposx, initposy, initposz); |
---|
493 | double initrotx, initroty, initrotz; |
---|
494 | deformer->initialXform.GetRotation().GetXYZAngles(initrotx, initroty, initrotz); |
---|
495 | double initsclx, initscly, initsclz; |
---|
496 | deformer->initialXform.GetScalingValues(initsclx, initscly, initsclz); |
---|
497 | XSI::MATH::CMatrix4 invTrans = deformer->initialXform.GetMatrix4(); |
---|
498 | invTrans.InvertInPlace(); |
---|
499 | |
---|
500 | XSI::MATH::CTransformation transformation; |
---|
501 | if (deformer->pBone->getParent() == 0) |
---|
502 | { |
---|
503 | // Based on global |
---|
504 | transformation = |
---|
505 | deformer->obj.GetKinematics().GetGlobal().GetTransform(); |
---|
506 | } |
---|
507 | else |
---|
508 | { |
---|
509 | // Based on local |
---|
510 | transformation = |
---|
511 | deformer->obj.GetKinematics().GetLocal().GetTransform(); |
---|
512 | } |
---|
513 | |
---|
514 | double posx, posy, posz; |
---|
515 | transformation.GetTranslationValues(posx, posy, posz); |
---|
516 | double sclx, scly, sclz; |
---|
517 | transformation.GetScalingValues(sclx, scly, sclz); |
---|
518 | |
---|
519 | // Make relative to initial |
---|
520 | XSI::MATH::CMatrix4 transformationMatrix = transformation.GetMatrix4(); |
---|
521 | transformationMatrix.MulInPlace(invTrans); |
---|
522 | transformation.SetMatrix4(transformationMatrix); |
---|
523 | |
---|
524 | // create keyframe |
---|
525 | TransformKeyFrame* kf = track->createNodeKeyFrame(time); |
---|
526 | // not sure why inverted transform doesn't work for position, but it doesn't |
---|
527 | // I thought XSI used same transform order as OGRE |
---|
528 | kf->setTranslate(Vector3(posx - initposx, posy - initposy, posz - initposz)); |
---|
529 | kf->setRotation(XSItoOgre(transformation.GetRotationQuaternion())); |
---|
530 | kf->setScale(Vector3(sclx / initsclx, scly / initscly, sclz / initsclz)); |
---|
531 | |
---|
532 | // Derive AABB of bone positions, for padding animated mesh AABB |
---|
533 | XSI::MATH::CVector3 bonePos = |
---|
534 | deformer->obj.GetKinematics().GetGlobal().GetTransform().GetTranslation(); |
---|
535 | AABBPadding.merge(XSItoOgre(bonePos)); |
---|
536 | |
---|
537 | |
---|
538 | |
---|
539 | } |
---|
540 | |
---|
541 | } |
---|
542 | //----------------------------------------------------------------------------- |
---|
543 | void XsiSkeletonExporter::cleanup(void) |
---|
544 | { |
---|
545 | |
---|
546 | mLowerCaseDeformerMap.clear(); |
---|
547 | |
---|
548 | CValueArray args; |
---|
549 | CValue dummy; |
---|
550 | args.Resize(1); |
---|
551 | |
---|
552 | for (int i = 0; i < mIKSampledAnimations.GetCount(); ++i) |
---|
553 | { |
---|
554 | args[0] = mIKSampledAnimations[i]; |
---|
555 | mXsiApp.ExecuteCommand(L"DeleteObj", args, dummy); |
---|
556 | } |
---|
557 | mIKSampledAnimations.Clear(); |
---|
558 | |
---|
559 | } |
---|
560 | } |
---|