Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/Tools/MayaExport/src/skeleton.cpp @ 10

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

=…

File size: 17.7 KB
Line 
1////////////////////////////////////////////////////////////////////////////////
2// skeleton.cpp
3// Author     : Francesco Giordana
4// Start Date : January 13, 2005
5// Copyright  : (C) 2006 by Francesco Giordana
6// Email      : fra.giordana@tiscali.it
7////////////////////////////////////////////////////////////////////////////////
8
9/*********************************************************************************
10*                                                                                *
11*   This program is free software; you can redistribute it and/or modify         *
12*   it under the terms of the GNU Lesser General Public License as published by  *
13*   the Free Software Foundation; either version 2 of the License, or            *
14*   (at your option) any later version.                                          *
15*                                                                                *
16**********************************************************************************/
17
18#include "skeleton.h"
19#include "submesh.h"
20#include <maya/MFnMatrixData.h>
21
22namespace OgreMayaExporter
23{
24        // Constructor
25        Skeleton::Skeleton()
26        {
27                m_joints.clear();
28                m_animations.clear();
29                m_restorePose = "";
30        }
31
32
33        // Destructor
34        Skeleton::~Skeleton()
35        {
36                clear();
37        }
38
39
40        // Clear skeleton data
41        void Skeleton::clear()
42        {
43                m_joints.clear();
44                m_animations.clear();
45                m_restorePose = "";
46        }
47
48
49        // Load skeleton data from given skin cluster
50        MStatus Skeleton::load(MFnSkinCluster* pSkinCluster,ParamList& params)
51        {
52                MStatus stat;
53                //check for valid skin cluster pointer
54                if (!pSkinCluster)
55                {
56                        std::cout << "Could not load skeleton data, no skin cluster specified\n";
57                        std::cout.flush();
58                        return MS::kFailure;
59                }
60                //retrieve and load joints from the skin cluster
61                MDagPath jointDag,rootDag;
62                MDagPathArray influenceDags;
63                int numInfluenceObjs = pSkinCluster->influenceObjects(influenceDags,&stat);
64                std::cout << "num influence objects: " << numInfluenceObjs << "\n";
65                std::cout.flush();
66                for (int i=0; i<numInfluenceObjs; i++)
67                {
68                        jointDag = influenceDags[i];
69                        if (influenceDags[i].hasFn(MFn::kJoint))
70                        {
71                                //retrieve root joint
72                                rootDag = jointDag;
73                                while (jointDag.length()>0)
74                                {
75                                        jointDag.pop();
76                                        if (jointDag.hasFn(MFn::kJoint) && jointDag.length()>0)
77                                                rootDag = jointDag;
78                                }
79                                //check if skeleton has already been loaded
80                                bool skip = false;
81                                for (int j=0; j<m_joints.size() && !skip; j++)
82                                {
83                                        //skip skeleton if already loaded
84                                        if (rootDag.partialPathName() == m_joints[j].name)
85                                        {
86                                                skip = true;
87                                        }
88                                }
89                                //load joints data from root
90                                if (!skip)
91                                {
92                                        // load the skeleton
93                                        std::cout <<  "Loading skeleton with root: " << rootDag.fullPathName().asChar() << "...\n";
94                                        std::cout.flush();
95                                        // save current selection list
96                                        MSelectionList selectionList;
97                                        MGlobal::getActiveSelectionList(selectionList);
98                                        // select the root joint dag
99                                        MGlobal::selectByName(rootDag.fullPathName(),MGlobal::kReplaceList);
100                                        //save current pose (if no pose has been saved yet)
101                                        if (m_restorePose == "")
102                                        {
103                                                MString poseName;
104                                                MGlobal::executeCommand("dagPose -s",poseName,true);
105                                                m_restorePose = poseName;
106                                        }
107                                        //set the skeleton to the desired neutral pose
108                                        if (params.neutralPoseType == NPT_BINDPOSE)
109                                        {
110                                                //disable constraints, IK, etc...
111                                                MGlobal::executeCommand("doEnableNodeItems false all",true);
112                                                // Note: we reset to the bind pose
113                                                MGlobal::executeCommand("dagPose -r -g -bp",true);
114                                        }
115                                        //load joints data
116                                        stat = loadJoint(rootDag,NULL,params,pSkinCluster);
117                                        if (MS::kSuccess == stat)
118                                        {
119                                                std::cout << "OK\n";
120                                                std::cout.flush();
121                                        }
122                                        else
123                                        {
124                                                std::cout << "Failed\n";
125                                                std::cout.flush();
126                                        }
127                                        //restore selection list
128                                        MGlobal::setActiveSelectionList(selectionList,MGlobal::kReplaceList);
129                                }
130                        }
131                }
132
133                return MS::kSuccess;
134        }
135
136
137        // Load a joint
138        MStatus Skeleton::loadJoint(MDagPath& jointDag,joint* parent,ParamList& params,MFnSkinCluster* pSkinCluster)
139        {
140                MStatus stat;
141                int i;
142                joint newJoint;
143                joint* parentJoint = parent;
144                // if it is a joint node translate it and then proceed to child nodes, otherwise skip it
145                // and proceed directly to child nodes
146                if (jointDag.hasFn(MFn::kJoint))
147                {
148                        MFnIkJoint jointFn(jointDag);
149                        // Display info
150                        std::cout << "Loading joint: " << jointFn.fullPathName().asChar();
151                        std::cout.flush();
152                        if (parent)
153                        {
154                                std::cout << " (parent: " << parent->name.asChar() << ")\n";
155                                std::cout.flush();
156                        }
157                        else
158                        {
159                                std::cout << "\n";
160                                std::cout.flush();
161                        }
162                        // Get parent index
163                        int idx=-1;
164                        if (parent)
165                        {
166                                for (i=0; i<m_joints.size() && idx<0; i++)
167                                {
168                                        if (m_joints[i].name == parent->name)
169                                                idx=i;
170                                }
171                        }
172                        // Get world bind matrix
173                        MMatrix bindMatrix = jointDag.inclusiveMatrix();;
174                        // Calculate local bind matrix
175                        MMatrix localMatrix;
176                        if (parent)
177                                localMatrix = bindMatrix * parent->bindMatrix.inverse();
178                        else
179                        {       // root node of skeleton
180                                localMatrix = bindMatrix;
181                        }
182                        // Get translation
183                        MVector translation = ((MTransformationMatrix)localMatrix).translation(MSpace::kPostTransform);
184                        if (fabs(translation.x) < PRECISION)
185                                translation.x = 0;
186                        if (fabs(translation.y) < PRECISION)
187                                translation.y = 0;
188                        if (fabs(translation.z) < PRECISION)
189                                translation.z = 0;
190                        // Calculate rotation data
191                        double qx,qy,qz,qw;
192                        ((MTransformationMatrix)localMatrix).getRotationQuaternion(qx,qy,qz,qw);
193                        MQuaternion rotation(qx,qy,qz,qw);
194                        MVector axis;
195                        double theta;
196                        rotation.getAxisAngle(axis,theta);
197                        if (fabs(axis.x) < PRECISION)
198                                axis.x = 0;
199                        if (fabs(axis.y) < PRECISION)
200                                axis.y = 0;
201                        if (fabs(axis.z) < PRECISION)
202                                axis.z = 0;
203                        axis.normalize();
204                        if (fabs(theta) < PRECISION)
205                                theta = 0;
206                        if (axis.length() < 0.5)
207                        {
208                                axis.x = 0;
209                                axis.y = 1;
210                                axis.z = 0;
211                                theta = 0;
212                        }
213                        // Get joint scale
214                        double scale[3];
215                        ((MTransformationMatrix)localMatrix).getScale(scale,MSpace::kPostTransform);
216                        if (fabs(scale[0]) < PRECISION)
217                                scale[0] = 0;
218                        if (fabs(scale[1]) < PRECISION)
219                                scale[1] = 0;
220                        if (fabs(scale[2]) < PRECISION)
221                                scale[2] = 0;
222                        // Set joint info
223                        newJoint.name = jointFn.partialPathName();
224                        newJoint.id = m_joints.size();
225                        newJoint.parentIndex = idx;
226                        newJoint.bindMatrix = bindMatrix;
227                        newJoint.localMatrix = localMatrix;
228                        newJoint.posx = translation.x * params.lum;
229                        newJoint.posy = translation.y * params.lum;
230                        newJoint.posz = translation.z * params.lum;
231                        newJoint.angle = theta;
232                        newJoint.axisx = axis.x;
233                        newJoint.axisy = axis.y;
234                        newJoint.axisz = axis.z;
235                        newJoint.scalex = scale[0];
236                        newJoint.scaley = scale[1];
237                        newJoint.scalez = scale[2];
238                        newJoint.jointDag = jointDag;
239                        m_joints.push_back(newJoint);
240                        // If root is a root joint, save it's index in the roots list
241                        if (idx < 0)
242                        {
243                                m_roots.push_back(m_joints.size() - 1);
244                        }
245                        // Get pointer to newly created joint
246                        parentJoint = &newJoint;
247                }
248                // Load child joints
249                for (i=0; i<jointDag.childCount();i++)
250                {
251                        MObject child;
252                        child = jointDag.child(i);
253                        MDagPath childDag = jointDag;
254                        childDag.push(child);
255                        loadJoint(childDag,parentJoint,params,pSkinCluster);
256                }
257                return MS::kSuccess;
258        }
259
260
261        // Load animations
262        MStatus Skeleton::loadAnims(ParamList& params)
263        {
264                //enable constraints, IK, etc...
265                MGlobal::executeCommand("doEnableNodeItems true all",true);
266                MStatus stat;
267                int i;
268                // save current time for later restore
269                MTime curTime = MAnimControl::currentTime();
270                std::cout << "Loading joint animations...\n";
271                std::cout.flush();
272                // clear animations list
273                m_animations.clear();
274                // load skeleton animation clips for the whole skeleton
275                for (i=0; i<params.skelClipList.size(); i++)
276                {
277                        stat = loadClip(params.skelClipList[i].name,params.skelClipList[i].start,
278                                params.skelClipList[i].stop,params.skelClipList[i].rate,params);
279                        if (stat == MS::kSuccess)
280                        {
281                                std::cout << "Clip successfully loaded\n";
282                                std::cout.flush();
283                        }
284                        else
285                        {
286                                std::cout << "Failed loading clip\n";
287                                std::cout.flush();
288                        }
289                }
290                //restore current time
291                MAnimControl::setCurrentTime(curTime);
292                return MS::kSuccess;
293        }
294
295        // Load an animation clip
296        MStatus Skeleton::loadClip(MString clipName,float start,float stop,float rate,ParamList& params)
297        {
298                MStatus stat;
299                int i,j,k;
300                MString msg;
301                std::vector<float> times;
302                // if skeleton has no joints we can't load the clip
303                if (m_joints.size() < 0)
304                        return MS::kFailure;
305                // display clip name
306                std::cout << "clip \"" << clipName.asChar() << "\"\n";
307                std::cout.flush();
308                // calculate times from clip sample rate
309                times.clear();
310                if (rate <= 0)
311                {
312                        std::cout << "invalid sample rate for the clip (must be >0), we skip it\n";
313                        std::cout.flush();
314                        return MS::kFailure;
315                }
316                for (float t=start; t<stop; t+=rate)
317                        times.push_back(t);
318                times.push_back(stop);
319                // get animation length
320                float length=0;
321                if (times.size() >= 0)
322                        length = times[times.size()-1] - times[0];
323                if (length < 0)
324                {
325                        std::cout << "invalid time range for the clip, we skip it\n";
326                        std::cout.flush();
327                        return MS::kFailure;
328                }
329                // create the animation
330                Animation a;
331                a.m_name = clipName.asChar();
332                a.m_tracks.clear();
333                a.m_length = length;
334                m_animations.push_back(a);
335                int animIdx = m_animations.size() - 1;
336                // create a track for current clip for all joints
337                std::vector<Track> animTracks;
338                for (i=0; i<m_joints.size(); i++)
339                {
340                        Track t;
341                        t.m_type = TT_SKELETON;
342                        t.m_bone = m_joints[i].name;
343                        t.m_skeletonKeyframes.clear();
344                        animTracks.push_back(t);
345                }
346                // evaluate animation curves at selected times
347                for (i=0; i<times.size(); i++)
348                {
349                        //set time to wanted sample time
350                        MAnimControl::setCurrentTime(MTime(times[i],MTime::kSeconds));
351                        //load a keyframe for every joint at current time
352                        for (j=0; j<m_joints.size(); j++)
353                        {
354                                skeletonKeyframe key = loadKeyframe(m_joints[j],times[i]-times[0],params);
355                                //add keyframe to joint track
356                                animTracks[j].addSkeletonKeyframe(key);
357                        }
358                        if (params.skelBB)
359                        {
360                                // Update bounding boxes of loaded submeshes
361                                for (j=0; j<params.loadedSubmeshes.size(); j++)
362                                {
363                                        MFnMesh mesh(params.loadedSubmeshes[j]->m_dagPath);
364                                        MPoint min = mesh.boundingBox().min();
365                                        MPoint max = mesh.boundingBox().max();
366                                        MBoundingBox bbox(min,max);
367                                        if (params.exportWorldCoords)
368                                                bbox.transformUsing(params.loadedSubmeshes[j]->m_dagPath.inclusiveMatrix());
369                                        min = bbox.min() * params.lum;
370                                        max = bbox.max() * params.lum;
371                                        MBoundingBox newbbox(min,max);
372                                        params.loadedSubmeshes[j]->m_boundingBox.expand(newbbox);
373                                }
374                        }
375                }
376                // add created tracks to current clip
377                for (i=0; i<animTracks.size(); i++)
378                {
379                        m_animations[animIdx].addTrack(animTracks[i]);
380                }
381                // display info
382                std::cout << "length: " << m_animations[animIdx].m_length << "\n";
383                std::cout << "num keyframes: " << animTracks[0].m_skeletonKeyframes.size() << "\n";
384                std::cout.flush();
385                // clip successfully loaded
386                return MS::kSuccess;
387        }
388
389        // Load a keyframe for a given joint at current time
390        skeletonKeyframe Skeleton::loadKeyframe(joint& j,float time,ParamList& params)
391        {
392                MVector position;
393                int parentIdx = j.parentIndex;
394                // Get joint matrix
395                MMatrix worldMatrix = j.jointDag.inclusiveMatrix();
396                // Calculate Local Matrix
397                MMatrix localMatrix;
398                if (parentIdx >= 0)
399                {
400                        // Get parent joint
401                        MDagPath parentDag = m_joints[parentIdx].jointDag;
402                        localMatrix = worldMatrix * parentDag.inclusiveMatrixInverse();
403                }
404                else
405                {       // Root node of skeleton
406                        if (params.exportWorldCoords)
407                                localMatrix = worldMatrix;
408                        else
409                                localMatrix = worldMatrix * j.jointDag.exclusiveMatrixInverse();
410                }
411                // Get relative transformation matrix
412                MMatrix relMatrix = localMatrix * j.localMatrix.inverse();
413                // Get relative translation
414                MVector translation = ((MTransformationMatrix)localMatrix).translation(MSpace::kPostTransform) - 
415                        ((MTransformationMatrix)j.localMatrix).translation(MSpace::kPostTransform);
416                if (fabs(translation.x) < PRECISION)
417                        translation.x = 0;
418                if (fabs(translation.y) < PRECISION)
419                        translation.y = 0;
420                if (fabs(translation.z) < PRECISION)
421                        translation.z = 0;
422                // Get relative rotation
423                double qx,qy,qz,qw;
424                ((MTransformationMatrix)relMatrix).getRotationQuaternion(qx,qy,qz,qw);
425                MQuaternion rotation(qx,qy,qz,qw);
426                MVector axis;
427                double theta;
428                rotation.getAxisAngle(axis,theta);
429                if (fabs(axis.x) < PRECISION)
430                        axis.x = 0;
431                if (fabs(axis.y) < PRECISION)
432                        axis.y = 0;
433                if (fabs(axis.z) < PRECISION)
434                        axis.z = 0;
435                axis.normalize();
436                if (fabs(theta) < PRECISION)
437                        theta = 0;
438                if (axis.length() < 0.5)
439                {
440                        axis.x = 0;
441                        axis.y = 1;
442                        axis.z = 0;
443                        theta = 0;
444                }
445                // Get relative scale
446                double scale[3];
447                ((MTransformationMatrix)relMatrix).getScale(scale,MSpace::kPostTransform);
448                if (fabs(scale[0]) < PRECISION)
449                        scale[0] = 0;
450                if (fabs(scale[1]) < PRECISION)
451                        scale[1] = 0;
452                if (fabs(scale[2]) < PRECISION)
453                        scale[2] = 0;
454                //create keyframe
455                skeletonKeyframe key;
456                key.time = time;
457                key.tx = translation.x * params.lum;
458                key.ty = translation.y * params.lum;
459                key.tz = translation.z * params.lum;
460                key.angle = theta;
461                key.axis_x = axis.x;
462                key.axis_y = axis.y;
463                key.axis_z = axis.z;
464                key.sx = scale[0];
465                key.sy = scale[1];
466                key.sz = scale[2];
467                return key;
468        }
469
470
471        // Restore skeleton pose
472        void Skeleton::restorePose()
473        {
474                // save current selection list
475                MSelectionList selectionList;
476                MGlobal::getActiveSelectionList(selectionList);
477                int i;
478                for (i=0; i<m_roots.size(); i++)
479                {
480                        MDagPath rootDag = m_joints[m_roots[i]].jointDag;
481                        // select the root joint dag
482                        MGlobal::selectByName(rootDag.fullPathName(),MGlobal::kReplaceList);
483                        // restore pose
484                        MString cmd = "dagPose -r -g -n \""+ m_restorePose;
485                        cmd += "\"";
486                        MGlobal::executeCommand(cmd,true);
487                }
488                //restore selection list
489                MGlobal::setActiveSelectionList(selectionList,MGlobal::kReplaceList);
490                //enable constraints, IK, etc...
491                MGlobal::executeCommand("doEnableNodeItems true all",true);
492        }
493
494
495
496        // Get joint list
497        std::vector<joint>& Skeleton::getJoints()
498        {
499                return m_joints;
500        }
501
502
503
504        // Get animations
505        std::vector<Animation>& Skeleton::getAnimations()
506        {
507                return m_animations;
508        }
509
510
511
512        // Write to an OGRE binary skeleton
513        MStatus Skeleton::writeOgreBinary(ParamList &params)
514        {
515                MStatus stat;
516                // Construct skeleton
517                MString name = "mayaExport";
518                Ogre::SkeletonPtr pSkeleton = Ogre::SkeletonManager::getSingleton().create(name.asChar(), 
519                        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
520                // Create skeleton bones
521                stat = createOgreBones(pSkeleton,params);
522                if (stat != MS::kSuccess)
523                {
524                        std::cout << "Error writing skeleton binary file\n";
525                        std::cout.flush();
526                }
527                // Create skeleton animation
528                if (params.exportSkelAnims)
529                {
530                        stat = createOgreSkeletonAnimations(pSkeleton,params);
531                        if (stat != MS::kSuccess)
532                        {
533                                std::cout << "Error writing ogre skeleton animations\n";
534                                std::cout.flush();
535                        }
536                }
537                pSkeleton->setBindingPose();
538                // Optimise animations
539                pSkeleton->optimiseAllAnimations();
540                // Export skeleton binary
541                Ogre::SkeletonSerializer serializer;
542                serializer.exportSkeleton(pSkeleton.getPointer(),params.skeletonFilename.asChar());
543                pSkeleton.setNull();
544                // Skeleton successfully exported
545                return MS::kSuccess;
546        }
547
548        // Write joints to an Ogre skeleton
549        MStatus Skeleton::createOgreBones(Ogre::SkeletonPtr pSkeleton,ParamList& params)
550        {
551                int i;
552                // Create the bones
553                for (i=0; i<m_joints.size(); i++)
554                {
555                        joint* j = &m_joints[i];
556                        // Create a new bone
557                        Ogre::Bone* pBone = pSkeleton->createBone(m_joints[i].name.asChar(), m_joints[i].id);
558                        // Set bone position (relative to it's parent)
559                        pBone->setPosition(j->posx,j->posy,j->posz);
560                        // Set bone orientation (relative to it's parent)
561                        Ogre::Quaternion orient;
562                        orient.FromAngleAxis(Ogre::Radian(j->angle),Ogre::Vector3(j->axisx,j->axisy,j->axisz));
563                        pBone->setOrientation(orient);
564                        // Set bone scale (relative to it's parent
565                        pBone->setScale(j->scalex,j->scaley,j->scalez);
566                }
567                // Create the hierarchy
568                for (i=0; i<m_joints.size(); i++)
569                {
570                        int parentIdx = m_joints[i].parentIndex;
571                        if (parentIdx >= 0)
572                        {
573                                // Get the parent joint
574                                Ogre::Bone* pParent = pSkeleton->getBone(m_joints[parentIdx].id);
575                                // Get current joint from skeleton
576                                Ogre::Bone* pBone = pSkeleton->getBone(m_joints[i].id);
577                                // Place current bone in the parent's child list
578                                pParent->addChild(pBone);
579                        }
580                }
581                return MS::kSuccess;
582        }
583
584
585        // Write skeleton animations to an Ogre skeleton
586        MStatus Skeleton::createOgreSkeletonAnimations(Ogre::SkeletonPtr pSkeleton,ParamList& params)
587        {
588                int i,j,k;
589                // Read loaded skeleton animations
590                for (i=0; i<m_animations.size(); i++)
591                {
592                        // Create a new animation
593                        Ogre::Animation* pAnimation = pSkeleton->createAnimation(m_animations[i].m_name.asChar(),
594                                m_animations[i].m_length);
595                        // Create tracks for current animation
596                        for (j=0; j<m_animations[i].m_tracks.size(); j++)
597                        {
598                                Track* t = &m_animations[i].m_tracks[j];
599                                // Create a new track
600                                Ogre::NodeAnimationTrack* pTrack = pAnimation->createNodeTrack(j,
601                                        pSkeleton->getBone(t->m_bone.asChar()));
602                                // Create keyframes for current track
603                                for (k=0; k<t->m_skeletonKeyframes.size(); k++)
604                                {
605                                        skeletonKeyframe* keyframe = &t->m_skeletonKeyframes[k];
606                                        // Create a new keyframe
607                                        Ogre::TransformKeyFrame* pKeyframe = pTrack->createNodeKeyFrame(keyframe->time);
608                                        // Set translation
609                                        pKeyframe->setTranslate(Ogre::Vector3(keyframe->tx,keyframe->ty,keyframe->tz));
610                                        // Set rotation
611                                        Ogre::Quaternion rot;
612                                        rot.FromAngleAxis(Ogre::Radian(keyframe->angle),
613                                                Ogre::Vector3(keyframe->axis_x,keyframe->axis_y,keyframe->axis_z));
614                                        pKeyframe->setRotation(rot);
615                                        // Set scale
616                                        pKeyframe->setScale(Ogre::Vector3(keyframe->sx,keyframe->sy,keyframe->sz));
617                                }
618                        }
619                }
620                return MS::kSuccess;
621        }
622
623
624};      //end namespace
Note: See TracBrowser for help on using the repository browser.