Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/ogre/Tools/XSIExport/src/OgreXSISkeletonExporter.cpp @ 45

Last change on this file since 45 was 6, checked in by anonymous, 17 years ago

=…

File size: 18.6 KB
Line 
1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4(Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus 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
61using namespace XSI;
62
63namespace 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}
Note: See TracBrowser for help on using the repository browser.