source: OGRE/trunk/ogrenew/Tools/XSIExport/src/OgreXSISkeletonExporter.cpp @ 692

Revision 692, 18.5 KB checked in by mattausch, 19 years ago (diff)

adding ogre 1.2 and dependencies

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