1 | #include "skeleton.h"
|
---|
2 |
|
---|
3 | namespace OgreMayaExporter
|
---|
4 | {
|
---|
5 | // Constructor
|
---|
6 | Skeleton::Skeleton()
|
---|
7 | {
|
---|
8 | m_joints.clear();
|
---|
9 | m_animations.clear();
|
---|
10 | }
|
---|
11 |
|
---|
12 |
|
---|
13 | // Destructor
|
---|
14 | Skeleton::~Skeleton()
|
---|
15 | {
|
---|
16 | clear();
|
---|
17 | }
|
---|
18 |
|
---|
19 |
|
---|
20 | // Clear skeleton data
|
---|
21 | void Skeleton::clear()
|
---|
22 | {
|
---|
23 | m_joints.clear();
|
---|
24 | m_animations.clear();
|
---|
25 | }
|
---|
26 |
|
---|
27 |
|
---|
28 | // Load skeleton data from given skin cluster
|
---|
29 | MStatus Skeleton::load(MFnSkinCluster* pSkinCluster,ParamList& params)
|
---|
30 | {
|
---|
31 | MStatus stat;
|
---|
32 | //update skin cluster pointer
|
---|
33 | if (!pSkinCluster)
|
---|
34 | {
|
---|
35 | std::cout << "Could not load skeleton data, no skin cluster specified\n";
|
---|
36 | return MS::kFailure;
|
---|
37 | }
|
---|
38 | //retrieve and load joints from the skin cluster
|
---|
39 | MDagPath jointDag,rootDag;
|
---|
40 | MDagPathArray influenceDags;
|
---|
41 | int numInfluenceObjs = pSkinCluster->influenceObjects(influenceDags,&stat);
|
---|
42 | std::cout << "num influence objects: " << numInfluenceObjs << "\n";
|
---|
43 | for (int i=0; i<numInfluenceObjs; i++)
|
---|
44 | {
|
---|
45 | jointDag = influenceDags[i];
|
---|
46 | std::cout << "influence object " << i << ": " << jointDag.fullPathName().asChar() << "\n";
|
---|
47 | if (influenceDags[i].hasFn(MFn::kJoint))
|
---|
48 | {
|
---|
49 | //retrieve root joint
|
---|
50 | rootDag = jointDag;
|
---|
51 | while (jointDag.length()>0)
|
---|
52 | {
|
---|
53 | jointDag.pop();
|
---|
54 | if (jointDag.hasFn(MFn::kJoint) && jointDag.length()>0)
|
---|
55 | rootDag = jointDag;
|
---|
56 | }
|
---|
57 | //check if skeleton has already been loaded
|
---|
58 | bool skip = false;
|
---|
59 | for (int j=0; j<m_joints.size() && !skip; j++)
|
---|
60 | {
|
---|
61 | //skip skeleton if already loaded
|
---|
62 | if (rootDag.partialPathName() == m_joints[j].name)
|
---|
63 | {
|
---|
64 | skip = true;
|
---|
65 | std::cout << "joint already loaded: skipped\n";
|
---|
66 | }
|
---|
67 | }
|
---|
68 | //load joints data from root
|
---|
69 | if (!skip)
|
---|
70 | {
|
---|
71 | std::cout << "Loading skeleton with root: " << rootDag.partialPathName().asChar() << "...\n";
|
---|
72 | MSelectionList selectionList;
|
---|
73 | MGlobal::getActiveSelectionList(selectionList);
|
---|
74 | // Set Neutral Pose
|
---|
75 | //check if we want the skin bind pose
|
---|
76 | if (params.neutralPoseFrame == NPT_BINDPOSE)
|
---|
77 | {
|
---|
78 | // Note: we reset to the bind pose, then get current matrix
|
---|
79 | // if bind pose could not be restored we use the current pose as a bind pose
|
---|
80 | MGlobal::selectByName(jointDag.partialPathName(),MGlobal::kReplaceList);
|
---|
81 | MGlobal::executeCommand("dagPose -r -g -bp");
|
---|
82 | }
|
---|
83 | //check if we want specified frame as neutral pose
|
---|
84 | else if (params.neutralPoseType == NPT_FRAME)
|
---|
85 | {
|
---|
86 | //set time to desired time
|
---|
87 | MTime npTime = (double)params.neutralPoseFrame;
|
---|
88 | MAnimControl::setCurrentTime(npTime.as(MTime::kSeconds));
|
---|
89 | }
|
---|
90 |
|
---|
91 | //load joints data
|
---|
92 | stat = loadJoint(rootDag,NULL,params);
|
---|
93 | if (MS::kSuccess == stat)
|
---|
94 | std::cout << "OK\n";
|
---|
95 | else
|
---|
96 | std::cout << "Failed\n";
|
---|
97 |
|
---|
98 | /*
|
---|
99 | //restore skeleton to neutral pose
|
---|
100 | if (params.neutralPoseFrame == 1)
|
---|
101 | {
|
---|
102 | MGlobal::executeCommand("dagPose -r -g -bp");
|
---|
103 | MGlobal::setActiveSelectionList(selectionList,MGlobal::kReplaceList);
|
---|
104 | }
|
---|
105 | else if (params.neutralPoseType == 2)
|
---|
106 | {
|
---|
107 | //set time to desired time
|
---|
108 | MTime npTime = (double)params.neutralPoseFrame;
|
---|
109 | MAnimControl::setCurrentTime(npTime.as(MTime::kSeconds));
|
---|
110 | }
|
---|
111 | */
|
---|
112 | }
|
---|
113 | }
|
---|
114 | }
|
---|
115 |
|
---|
116 | return MS::kSuccess;
|
---|
117 | }
|
---|
118 |
|
---|
119 |
|
---|
120 | // Load a joint
|
---|
121 | MStatus Skeleton::loadJoint(MDagPath& jointDag,joint* parent,ParamList& params)
|
---|
122 | {
|
---|
123 | int i;
|
---|
124 | joint newJoint;
|
---|
125 | joint* parentJoint = parent;
|
---|
126 | // if it is a joint node translate it and then proceed to child nodes, otherwise skip it
|
---|
127 | // and proceed directly to child nodes
|
---|
128 | if (jointDag.hasFn(MFn::kJoint))
|
---|
129 | {
|
---|
130 | MFnIkJoint jointFn(jointDag);
|
---|
131 | // Display info
|
---|
132 | std::cout << "Loading joint: " << jointFn.partialPathName().asChar();
|
---|
133 | if (parent)
|
---|
134 | std::cout << " (parent: " << parent->name.asChar() << ")\n";
|
---|
135 | else
|
---|
136 | std::cout << "\n";
|
---|
137 | // Get parent index
|
---|
138 | int idx=-1;
|
---|
139 | if (parent)
|
---|
140 | {
|
---|
141 | for (i=0; i<m_joints.size() && idx<0; i++)
|
---|
142 | {
|
---|
143 | if (m_joints[i].name == parent->name)
|
---|
144 | idx=i;
|
---|
145 | }
|
---|
146 | }
|
---|
147 | // Get joint matrix
|
---|
148 | MMatrix bindMatrix = jointDag.inclusiveMatrix();
|
---|
149 | // Calculate scaling factor inherited by parent
|
---|
150 | double scale[3];
|
---|
151 | if (parent)
|
---|
152 | {
|
---|
153 | MTransformationMatrix M(parent->worldMatrix);
|
---|
154 | M.getScale(scale,MSpace::kWorld);
|
---|
155 | }
|
---|
156 | else
|
---|
157 | {
|
---|
158 | scale[0] = 1;
|
---|
159 | scale[1] = 1;
|
---|
160 | scale[2] = 1;
|
---|
161 | }
|
---|
162 | // Calculate Local Matrix
|
---|
163 | MMatrix localMatrix;
|
---|
164 | if (parent)
|
---|
165 | localMatrix = bindMatrix * parent->worldMatrix.inverse();
|
---|
166 | else
|
---|
167 | { // root node of skeleton
|
---|
168 | if (params.exportWorldCoords)
|
---|
169 | localMatrix = bindMatrix;
|
---|
170 | else
|
---|
171 | localMatrix = bindMatrix * jointDag.exclusiveMatrix().inverse();
|
---|
172 | }
|
---|
173 | // Calculate rotation data
|
---|
174 | double qx,qy,qz,qw;
|
---|
175 | ((MTransformationMatrix)localMatrix).getRotationQuaternion(qx,qy,qz,qw);
|
---|
176 | MQuaternion rotation(qx,qy,qz,qw);
|
---|
177 | MVector axis;
|
---|
178 | double theta;
|
---|
179 | rotation.getAxisAngle(axis,theta);
|
---|
180 | axis.normalize();
|
---|
181 | if (axis.length() < 0.5)
|
---|
182 | {
|
---|
183 | axis.x = 0;
|
---|
184 | axis.y = 1;
|
---|
185 | axis.z = 0;
|
---|
186 | theta = 0;
|
---|
187 | }
|
---|
188 | // Set joint info
|
---|
189 | newJoint.name = jointFn.partialPathName();
|
---|
190 | newJoint.id = m_joints.size();
|
---|
191 | newJoint.parentIndex = idx;
|
---|
192 | newJoint.worldMatrix = bindMatrix;
|
---|
193 | newJoint.localMatrix = localMatrix;
|
---|
194 | newJoint.posx = localMatrix(3,0) * scale[0];
|
---|
195 | newJoint.posy = localMatrix(3,1) * scale[1];
|
---|
196 | newJoint.posz = localMatrix(3,2) * scale[2];
|
---|
197 | newJoint.angle = theta;
|
---|
198 | newJoint.axisx = axis.x;
|
---|
199 | newJoint.axisy = axis.y;
|
---|
200 | newJoint.axisz = axis.z;
|
---|
201 | newJoint.jointDag = jointDag;
|
---|
202 | m_joints.push_back(newJoint);
|
---|
203 | // Get pointer to newly created joint
|
---|
204 | parentJoint = &newJoint;
|
---|
205 | }
|
---|
206 | // Load children joints
|
---|
207 | for (i=0; i<jointDag.childCount();i++)
|
---|
208 | {
|
---|
209 | MObject child;
|
---|
210 | child = jointDag.child(i);
|
---|
211 | MDagPath childDag = jointDag;
|
---|
212 | childDag.push(child);
|
---|
213 | loadJoint(childDag,parentJoint,params);
|
---|
214 | }
|
---|
215 | return MS::kSuccess;
|
---|
216 | }
|
---|
217 |
|
---|
218 |
|
---|
219 | // Load animations
|
---|
220 | MStatus Skeleton::loadAnims(ParamList& params)
|
---|
221 | {
|
---|
222 | MStatus stat;
|
---|
223 | int i;
|
---|
224 | std::cout << "Loading joint animations...\n";
|
---|
225 | // save current time for later restore
|
---|
226 | double curtime = MAnimControl::currentTime().as(MTime::kSeconds);
|
---|
227 | // clear animations list
|
---|
228 | m_animations.clear();
|
---|
229 | // load animation clips for the whole skeleton
|
---|
230 | for (i=0; i<params.clipList.size(); i++)
|
---|
231 | {
|
---|
232 | stat = loadClip(params.clipList[i].name,params.clipList[i].start,
|
---|
233 | params.clipList[i].stop,params.clipList[i].rate,params);
|
---|
234 | if (stat == MS::kSuccess)
|
---|
235 | std::cout << "Clip successfully loaded\n";
|
---|
236 | else
|
---|
237 | std::cout << "Failed loading clip\n";
|
---|
238 | }
|
---|
239 | //restore current time
|
---|
240 | MAnimControl::setCurrentTime(MTime(curtime,MTime::kSeconds));
|
---|
241 | return MS::kSuccess;
|
---|
242 | }
|
---|
243 |
|
---|
244 |
|
---|
245 | // Load an animation clip
|
---|
246 | MStatus Skeleton::loadClip(MString clipName,double start,double stop,double rate,ParamList& params)
|
---|
247 | {
|
---|
248 | MStatus stat;
|
---|
249 | int i,j;
|
---|
250 | MString msg;
|
---|
251 | std::vector<double> times;
|
---|
252 | // if skeleton has no joint we can't load the clip
|
---|
253 | if (m_joints.size() < 0)
|
---|
254 | return MS::kFailure;
|
---|
255 | // display clip name
|
---|
256 | std::cout << "clip \"" << clipName.asChar() << "\"\n";
|
---|
257 | // calculate times from clip sample rate
|
---|
258 | times.clear();
|
---|
259 | for (double t=start; t<stop; t+=rate)
|
---|
260 | times.push_back(t);
|
---|
261 | times.push_back(stop);
|
---|
262 | // get animation length
|
---|
263 | double length=0;
|
---|
264 | if (times.size() > 0)
|
---|
265 | length = times[times.size()-1] - times[0];
|
---|
266 | // check if clip length is >0
|
---|
267 | if (length <= 0)
|
---|
268 | {
|
---|
269 | std::cout << "the clip has 0 length, we skip it\n";
|
---|
270 | return MS::kFailure;
|
---|
271 | }
|
---|
272 | // create the animation
|
---|
273 | animation a;
|
---|
274 | a.name = clipName.asChar();
|
---|
275 | a.tracks.clear();
|
---|
276 | a.length = length;
|
---|
277 | m_animations.push_back(a);
|
---|
278 | int animIdx = m_animations.size() - 1;
|
---|
279 | // create a track for current clip for all joints
|
---|
280 | std::vector<track> animTracks;
|
---|
281 | for (i=0; i<m_joints.size(); i++)
|
---|
282 | {
|
---|
283 | track t;
|
---|
284 | t.bone = m_joints[i].name;
|
---|
285 | t.keyframes.clear();
|
---|
286 | animTracks.push_back(t);
|
---|
287 | }
|
---|
288 | // evaluate animation curves at selected times
|
---|
289 | for (i=0; i<times.size(); i++)
|
---|
290 | {
|
---|
291 | //set time to wanted sample time
|
---|
292 | MAnimControl::setCurrentTime(MTime(times[i],MTime::kSeconds));
|
---|
293 | //load a keyframe for every joint at current time
|
---|
294 | for (j=0; j<m_joints.size(); j++)
|
---|
295 | {
|
---|
296 | keyframe key = loadKeyframe(m_joints[j],times[i]-times[0],params);
|
---|
297 | //add keyframe to joint track
|
---|
298 | animTracks[j].keyframes.push_back(key);
|
---|
299 | }
|
---|
300 | }
|
---|
301 | // add created tracks to current clip
|
---|
302 | for (i=0; i<animTracks.size(); i++)
|
---|
303 | {
|
---|
304 | m_animations[animIdx].tracks.push_back(animTracks[i]);
|
---|
305 | }
|
---|
306 | // display info
|
---|
307 | std::cout << "length: " << m_animations[animIdx].length << "\n";
|
---|
308 | std::cout << "num keyframes: " << animTracks[0].keyframes.size() << "\n";
|
---|
309 | // clip successfully loaded
|
---|
310 | return MS::kSuccess;
|
---|
311 | }
|
---|
312 |
|
---|
313 |
|
---|
314 | // Load a keyframe for a given joint at current time
|
---|
315 | keyframe Skeleton::loadKeyframe(joint& j,double time,ParamList& params)
|
---|
316 | {
|
---|
317 | MFnIkJoint jointFn(j.jointDag);
|
---|
318 | MTransformationMatrix matrix;
|
---|
319 | MVector position;
|
---|
320 | double scale[3];
|
---|
321 | scale[0] = 1; scale[1] = 1; scale[2] = 1;
|
---|
322 | int parentIdx = j.parentIndex;
|
---|
323 | //get joint matrix at current time
|
---|
324 | matrix = j.jointDag.inclusiveMatrix();
|
---|
325 | if (parentIdx >= 0)
|
---|
326 | {
|
---|
327 | //calculate inherited scale factor
|
---|
328 | ((MTransformationMatrix)j.jointDag.exclusiveMatrix()).getScale(scale,MSpace::kWorld);
|
---|
329 | //calculate relative matrix
|
---|
330 | matrix = j.jointDag.inclusiveMatrix() * j.jointDag.exclusiveMatrixInverse();
|
---|
331 | }
|
---|
332 | else
|
---|
333 | { // root joint
|
---|
334 | if (params.exportWorldCoords)
|
---|
335 | matrix = j.jointDag.inclusiveMatrix();
|
---|
336 | else
|
---|
337 | matrix = j.jointDag.inclusiveMatrix() * j.jointDag.exclusiveMatrixInverse();
|
---|
338 | }
|
---|
339 | //calculate position of joint at given time
|
---|
340 | position.x = matrix.asMatrix()(3,0) * scale[0];
|
---|
341 | position.y = matrix.asMatrix()(3,1) * scale[1];
|
---|
342 | position.z = matrix.asMatrix()(3,2) * scale[2];
|
---|
343 | //get relative transformation matrix
|
---|
344 | matrix = matrix.asMatrix() * j.localMatrix.inverse();
|
---|
345 | //calculate rotation
|
---|
346 | double qx,qy,qz,qw;
|
---|
347 | ((MTransformationMatrix)matrix).getRotationQuaternion(qx,qy,qz,qw);
|
---|
348 | MQuaternion rotation(qx,qy,qz,qw);
|
---|
349 | double theta;
|
---|
350 | MVector axis;
|
---|
351 | rotation.getAxisAngle(axis,theta);
|
---|
352 | axis.normalize();
|
---|
353 | if (axis.length() < 0.5)
|
---|
354 | {
|
---|
355 | axis.x = 0;
|
---|
356 | axis.y = 1;
|
---|
357 | axis.z = 0;
|
---|
358 | theta = 0;
|
---|
359 | }
|
---|
360 | //create keyframe
|
---|
361 | keyframe key;
|
---|
362 | key.time = time;
|
---|
363 | key.tx = position.x - j.posx;
|
---|
364 | key.ty = position.y - j.posy;
|
---|
365 | key.tz = position.z - j.posz;
|
---|
366 | key.angle = theta;
|
---|
367 | key.axis_x = axis.x;
|
---|
368 | key.axis_y = axis.y;
|
---|
369 | key.axis_z = axis.z;
|
---|
370 | key.sx = 1;
|
---|
371 | key.sy = 1;
|
---|
372 | key.sz = 1;
|
---|
373 | return key;
|
---|
374 | }
|
---|
375 |
|
---|
376 |
|
---|
377 | // Get joint list
|
---|
378 | std::vector<joint>& Skeleton::getJoints()
|
---|
379 | {
|
---|
380 | return m_joints;
|
---|
381 | }
|
---|
382 |
|
---|
383 |
|
---|
384 | // Get animations
|
---|
385 | std::vector<animation>& Skeleton::getAnimations()
|
---|
386 | {
|
---|
387 | return m_animations;
|
---|
388 | }
|
---|
389 |
|
---|
390 |
|
---|
391 | // Write skeleton data to Ogre XML file
|
---|
392 | MStatus Skeleton::writeXML(ParamList ¶ms)
|
---|
393 | {
|
---|
394 | // Start skeleton description
|
---|
395 | params.outSkeleton << "<skeleton>\n";
|
---|
396 |
|
---|
397 | // Write bones list
|
---|
398 | params.outSkeleton << "\t<bones>\n";
|
---|
399 | // For each joint write it's description
|
---|
400 | for (int i=0; i<m_joints.size(); i++)
|
---|
401 | {
|
---|
402 | params.outSkeleton << "\t\t<bone id=\"" << m_joints[i].id << "\" name=\"" << m_joints[i].name.asChar() << "\">\n";
|
---|
403 | params.outSkeleton << "\t\t\t<position x=\"" << m_joints[i].posx << "\" y=\"" << m_joints[i].posy
|
---|
404 | << "\" z=\"" << m_joints[i].posz << "\"/>\n";
|
---|
405 | params.outSkeleton << "\t\t\t<rotation angle=\"" << m_joints[i].angle << "\">\n";
|
---|
406 | params.outSkeleton << "\t\t\t\t<axis x=\"" << m_joints[i].axisx << "\" y=\"" << m_joints[i].axisy
|
---|
407 | << "\" z=\"" << m_joints[i].axisz << "\"/>\n";
|
---|
408 | params.outSkeleton << "\t\t\t</rotation>\n";
|
---|
409 | params.outSkeleton << "\t\t</bone>\n";
|
---|
410 | }
|
---|
411 | params.outSkeleton << "\t</bones>\n";
|
---|
412 |
|
---|
413 | // Write bone hierarchy
|
---|
414 | params.outSkeleton << "\t<bonehierarchy>\n";
|
---|
415 | for (i=0; i<m_joints.size(); i++)
|
---|
416 | {
|
---|
417 | if (m_joints[i].parentIndex>=0)
|
---|
418 | {
|
---|
419 | params.outSkeleton << "\t\t<boneparent bone=\"" << m_joints[i].name.asChar() << "\" parent=\""
|
---|
420 | << m_joints[m_joints[i].parentIndex].name.asChar() << "\"/>\n";
|
---|
421 | }
|
---|
422 | }
|
---|
423 | params.outSkeleton << "\t</bonehierarchy>\n";
|
---|
424 |
|
---|
425 | // Write animations description
|
---|
426 | if (params.exportAnims)
|
---|
427 | {
|
---|
428 | params.outSkeleton << "\t<animations>\n";
|
---|
429 | // For every animation
|
---|
430 | for (i=0; i<m_animations.size(); i++)
|
---|
431 | {
|
---|
432 | // Write animation info
|
---|
433 | params.outSkeleton << "\t\t<animation name=\"" << m_animations[i].name.asChar() << "\" length=\"" <<
|
---|
434 | m_animations[i].length << "\">\n";
|
---|
435 | // Write tracks
|
---|
436 | params.outSkeleton << "\t\t\t<tracks>\n";
|
---|
437 | // Cycle through tracks
|
---|
438 | for (int j=0; j<m_animations[i].tracks.size(); j++)
|
---|
439 | {
|
---|
440 | track t = m_animations[i].tracks[j];
|
---|
441 | params.outSkeleton << "\t\t\t\t<track bone=\"" << t.bone.asChar() << "\">\n";
|
---|
442 | // Write track keyframes
|
---|
443 | params.outSkeleton << "\t\t\t\t\t<keyframes>\n";
|
---|
444 | for (int k=0; k<t.keyframes.size(); k++)
|
---|
445 | {
|
---|
446 | // time
|
---|
447 | params.outSkeleton << "\t\t\t\t\t\t<keyframe time=\"" << t.keyframes[k].time << "\">\n";
|
---|
448 | // translation
|
---|
449 | params.outSkeleton << "\t\t\t\t\t\t\t<translate x=\"" << t.keyframes[k].tx << "\" y=\"" <<
|
---|
450 | t.keyframes[k].ty << "\" z=\"" << t.keyframes[k].tz << "\"/>\n";
|
---|
451 | // rotation
|
---|
452 | params.outSkeleton << "\t\t\t\t\t\t\t<rotate angle=\"" << t.keyframes[k].angle << "\">\n";
|
---|
453 | params.outSkeleton << "\t\t\t\t\t\t\t\t<axis x=\"" << t.keyframes[k].axis_x << "\" y=\"" <<
|
---|
454 | t.keyframes[k].axis_y << "\" z=\"" << t.keyframes[k].axis_z << "\"/>\n";
|
---|
455 | params.outSkeleton << "\t\t\t\t\t\t\t</rotate>\n";
|
---|
456 | //scale
|
---|
457 | params.outSkeleton << "\t\t\t\t\t\t\t<scale x=\"" << t.keyframes[k].sx << "\" y=\"" <<
|
---|
458 | t.keyframes[k].sy << "\" z=\"" << t.keyframes[k].sz << "\"/>\n";
|
---|
459 | params.outSkeleton << "\t\t\t\t\t\t</keyframe>\n";
|
---|
460 | }
|
---|
461 | params.outSkeleton << "\t\t\t\t\t</keyframes>\n";
|
---|
462 | params.outSkeleton << "\t\t\t\t</track>\n";
|
---|
463 | }
|
---|
464 | // End tracks description
|
---|
465 | params.outSkeleton << "\t\t\t</tracks>\n";
|
---|
466 | // End animation description
|
---|
467 | params.outSkeleton << "\t\t</animation>\n";
|
---|
468 | }
|
---|
469 | params.outSkeleton << "\t</animations>\n";
|
---|
470 | }
|
---|
471 |
|
---|
472 | // End skeleton description
|
---|
473 | params.outSkeleton << "</skeleton>\n";
|
---|
474 |
|
---|
475 | return MS::kSuccess;
|
---|
476 | }
|
---|
477 |
|
---|
478 |
|
---|
479 | }; //end namespace |
---|