1 | /* |
---|
2 | // if skeleton data is present, stream skeleton file |
---|
3 | if (m_config.getExportSkeleton()) { |
---|
4 | // open the skeleton.xml output file |
---|
5 | of.open((m_config.getExportPath() + "\\" + m_skeletonFilename).c_str(), std::ios::out); |
---|
6 | |
---|
7 | // stream the skeleton file |
---|
8 | streamSkeleton(of); |
---|
9 | of.close(); |
---|
10 | } |
---|
11 | |
---|
12 | */ |
---|
13 | |
---|
14 | #ifdef 0 |
---|
15 | |
---|
16 | // ******************************************************************************* |
---|
17 | // Skeleton streaming functions |
---|
18 | // ******************************************************************************* |
---|
19 | |
---|
20 | std::string MeshXMLExporter::removeSpaces(const std::string &src) { |
---|
21 | std::string s(src); |
---|
22 | std::string::size_type pos; |
---|
23 | while ((pos=s.find_first_of(" \t\n")) != std::string::npos) |
---|
24 | s.replace(pos, 1, "_"); |
---|
25 | |
---|
26 | return s; |
---|
27 | } |
---|
28 | |
---|
29 | bool MeshXMLExporter::streamSkeleton(std::ostream &of) { |
---|
30 | |
---|
31 | // go through and sort out the bone hierarchy (include all of the non-null bones that were not |
---|
32 | // skinned, as those could still be needed in the application) |
---|
33 | of << "<?xml version=\"1.0\"?>" << std::endl << "<skeleton>" << std::endl; |
---|
34 | of << "\t<bones>" << std::endl; |
---|
35 | |
---|
36 | // write out the bone rest pose data |
---|
37 | std::map< std::string, int >::const_iterator it = m_boneIndexMap.begin(); |
---|
38 | while (it != m_boneIndexMap.end()) { |
---|
39 | |
---|
40 | INode *thisNode = m_i->GetINodeByName(it->first.c_str()); |
---|
41 | |
---|
42 | of << "\t\t<bone id=\"" << it->second << "\" name=\"" << removeSpaces(it->first) << "\" >" << std::endl; |
---|
43 | |
---|
44 | // assume rest pose is at time zero |
---|
45 | TimeValue start = m_i->GetAnimRange().Start(); |
---|
46 | ObjectState os = thisNode->EvalWorldState(start); |
---|
47 | Object *obj = os.obj; |
---|
48 | SClass_ID scid = obj->SuperClassID(); |
---|
49 | |
---|
50 | // SKELOBJ_CLASS_ID = 0x9125 = 37157 |
---|
51 | // BIPED_CLASS_ID = 0x9155 = 37205 |
---|
52 | // BIPSLAVE_CONTROL_CLASS_ID = 0x9154 = 37204 |
---|
53 | // BIPBODY_CONTROL_CLASS_ID = 0x9156 = 37206 |
---|
54 | // FOOTPRINT_CLASS_ID = 0x3011 = 12305 |
---|
55 | // DUMMY_CLASS_ID = 0x876234 = 8872500 |
---|
56 | Matrix3 tm(thisNode->GetNodeTM(start)); |
---|
57 | Matrix3 ptm(thisNode->GetParentTM(start)); |
---|
58 | Control *tmc = thisNode->GetTMController(); |
---|
59 | |
---|
60 | TCHAR *nm = thisNode->GetName(); |
---|
61 | Class_ID cid = tmc->ClassID(); |
---|
62 | |
---|
63 | if (cid == BIPBODY_CONTROL_CLASS_ID || cid == BIPED_CLASS_ID) { |
---|
64 | if (m_config.getInvertYZ()) { |
---|
65 | Matrix3 m = RotateXMatrix(PI / 2.0f); |
---|
66 | tm = tm * Inverse(m); |
---|
67 | } |
---|
68 | } |
---|
69 | else |
---|
70 | tm = tm * Inverse(ptm); |
---|
71 | |
---|
72 | Point3 pos = tm.GetTrans(); |
---|
73 | AngAxis aa(tm); |
---|
74 | |
---|
75 | of << "\t\t\t<position x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />" << std::endl; |
---|
76 | |
---|
77 | // there is still a lingering Max/Ogre handed-ness issue even after rotating to get the axes correct |
---|
78 | // so we negate the angle of rotation here |
---|
79 | of << "\t\t\t<rotation angle=\"" << -aa.angle << "\">" << std::endl; |
---|
80 | of << "\t\t\t\t<axis x=\"" << aa.axis.x << "\" y=\"" << aa.axis.y << "\" z=\"" << aa.axis.z << "\" />" << std::endl; |
---|
81 | of << "\t\t\t</rotation>" << std::endl; |
---|
82 | of << "\t\t</bone>" << std::endl; |
---|
83 | |
---|
84 | it++; |
---|
85 | } |
---|
86 | |
---|
87 | of << "\t</bones>" << std::endl; |
---|
88 | |
---|
89 | // write out the bone hierarchy |
---|
90 | it = m_boneIndexMap.begin(); |
---|
91 | of << "\t<bonehierarchy>" << std::endl; |
---|
92 | while (it != m_boneIndexMap.end()) { |
---|
93 | INode *thisNode = m_i->GetINodeByName(it->first.c_str()); |
---|
94 | |
---|
95 | if (thisNode != 0) { |
---|
96 | INode *parentNode = thisNode->GetParentNode(); |
---|
97 | |
---|
98 | if (parentNode != 0 && parentNode != m_i->GetRootNode()) |
---|
99 | of << "\t\t<boneparent bone=\"" << removeSpaces(it->first) << "\" parent=\"" << removeSpaces(std::string(parentNode->GetName())) << "\"/>" << std::endl; |
---|
100 | } |
---|
101 | |
---|
102 | it++; |
---|
103 | } |
---|
104 | of << "\t</bonehierarchy>" << std::endl; |
---|
105 | |
---|
106 | // the fun bits.... |
---|
107 | // Animations are named by the user during export; Max has no concept of animation subset names, |
---|
108 | // so we have to get the user to do that manually. If the user has entered anything for animations, |
---|
109 | // spit it all out here. |
---|
110 | std::list<NamedAnimation>::iterator anim = m_animations.begin(); |
---|
111 | |
---|
112 | if (anim != m_animations.end()) { |
---|
113 | of << "\t<animations>" << std::endl; |
---|
114 | |
---|
115 | while (anim != m_animations.end()) { |
---|
116 | |
---|
117 | NamedAnimation a = *anim; |
---|
118 | anim++; |
---|
119 | |
---|
120 | float fps = (float)GetFrameRate(); |
---|
121 | float length = (a.end - a.start) / fps; |
---|
122 | |
---|
123 | of << "\t\t<animation name=\"" << removeSpaces(a.name) << "\" length=\"" << length << "\">" << std::endl; |
---|
124 | |
---|
125 | streamAnimTracks(of, a.start, a.end); |
---|
126 | |
---|
127 | of << "\t\t</animation>" << std::endl; |
---|
128 | } |
---|
129 | |
---|
130 | of << "\t</animations>" << std::endl; |
---|
131 | } |
---|
132 | |
---|
133 | of << "</skeleton>" << std::endl; |
---|
134 | |
---|
135 | return true; |
---|
136 | } |
---|
137 | |
---|
138 | static int _compare_func(const void *a, const void *b) { return *(( int *)a) - *(( int *)b); } |
---|
139 | |
---|
140 | bool MeshXMLExporter::streamAnimTracks(std::ostream &of, int startFrame, int endFrame) { |
---|
141 | |
---|
142 | int start = startFrame * GetTicksPerFrame(); |
---|
143 | int end = endFrame * GetTicksPerFrame(); |
---|
144 | |
---|
145 | std::map< std::string, int >::const_iterator it = m_boneIndexMap.begin(); |
---|
146 | |
---|
147 | of << "\t\t\t<tracks>" << std::endl; |
---|
148 | |
---|
149 | // need this for calculating keyframe values |
---|
150 | Matrix3 initTM, bipedMasterTM0; |
---|
151 | IBipMaster *bip = 0; |
---|
152 | bipedMasterTM0.IdentityMatrix(); |
---|
153 | |
---|
154 | while (it != m_boneIndexMap.end()) { |
---|
155 | |
---|
156 | INode *thisNode = m_i->GetINodeByName(it->first.c_str()); |
---|
157 | it++; |
---|
158 | |
---|
159 | Control *c = thisNode->GetTMController(); |
---|
160 | Class_ID cid = c->ClassID(); |
---|
161 | |
---|
162 | Tab<TimeValue> keyTimes; |
---|
163 | Interval interval(start, end); |
---|
164 | |
---|
165 | /* |
---|
166 | -- gets initial transform at frame 0f |
---|
167 | at time 0f ( |
---|
168 | initTform = d.transform ; |
---|
169 | if (not isRootUniversal2 d) then ( |
---|
170 | mparent = d.parent.transform ; |
---|
171 | initTform = initTform*inverse(mparent) ; |
---|
172 | ) |
---|
173 | else if (flipYZ) then ( |
---|
174 | if (not g_MAX) then |
---|
175 | format " - flipping root track..." ; |
---|
176 | -- we add the bip Transform |
---|
177 | --initTform = initTform * d.controller.rootNode.transform ; |
---|
178 | initTform = flipYZTransform initTform ; |
---|
179 | ) |
---|
180 | ) |
---|
181 | */ |
---|
182 | |
---|
183 | initTM = thisNode->GetNodeTM(0); |
---|
184 | |
---|
185 | // must have at least a frame at the start... |
---|
186 | keyTimes.Append(1, &start); |
---|
187 | |
---|
188 | TCHAR *tch = thisNode->GetName(); |
---|
189 | |
---|
190 | // SKELOBJ_CLASS_ID = 0x9125 = 37157 |
---|
191 | // BIPED_CLASS_ID = 0x9155 = 37205 |
---|
192 | // BIPSLAVE_CONTROL_CLASS_ID = 0x9154 = 37204 |
---|
193 | // BIPBODY_CONTROL_CLASS_ID = 0x9156 = 37206 |
---|
194 | // FOOTPRINT_CLASS_ID = 0x3011 = 12305 |
---|
195 | // DUMMY_CLASS_ID = 0x876234 = 8872500 |
---|
196 | |
---|
197 | // three-part controller for Biped root -- taking this cue from the old MaxScript exporter code |
---|
198 | if (cid == BIPBODY_CONTROL_CLASS_ID) { |
---|
199 | |
---|
200 | // we deal with the initial transform as-is, except that it might need to |
---|
201 | // be rotated (since the root transform is in world coords) |
---|
202 | if (m_config.getInvertYZ()) |
---|
203 | initTM = initTM * Inverse(RotateXMatrix(PI/2.0f)); |
---|
204 | |
---|
205 | if (cid == BIPBODY_CONTROL_CLASS_ID) { |
---|
206 | // get the keys from the horiz, vert and turn controllers |
---|
207 | bip = GetBipMasterInterface(c); |
---|
208 | Control *biph = bip->GetHorizontalControl(); |
---|
209 | Control *bipv = bip->GetVerticalControl(); |
---|
210 | Control *bipr = bip->GetTurnControl(); |
---|
211 | |
---|
212 | biph->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION); |
---|
213 | bipv->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION); |
---|
214 | bipr->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION); |
---|
215 | } |
---|
216 | } |
---|
217 | else if (cid == BIPSLAVE_CONTROL_CLASS_ID) { |
---|
218 | // slaves just have keys, apparently |
---|
219 | c->GetKeyTimes(keyTimes, interval, KEYAT_POSITION | KEYAT_ROTATION); |
---|
220 | |
---|
221 | // put initial transform into local coordinates -- since this is relative to the |
---|
222 | // parent, we don't need to sweat that possible rotations here |
---|
223 | initTM = initTM * Inverse(thisNode->GetParentTM(0)); |
---|
224 | } |
---|
225 | |
---|
226 | // ...and stick a frame at the end as well...it will get sorted out if it is redundant |
---|
227 | keyTimes.Append(1, &end); |
---|
228 | |
---|
229 | // skip redundant key times here |
---|
230 | keyTimes.Sort(_compare_func); |
---|
231 | |
---|
232 | // if (cid == BIPSLAVE_CONTROL_CLASS_ID || cid == BIPBODY_CONTROL_CLASS_ID || cid == FOOTPRINT_CLASS_ID) { |
---|
233 | // |
---|
234 | // if (cid == BIPBODY_CONTROL_CLASS_ID) { |
---|
235 | // initTM = thisNode->GetNodeTM(0); |
---|
236 | // |
---|
237 | // if (m_flipYZ) |
---|
238 | // initTM = initTM * RotateXMatrix(PI/2.0f); |
---|
239 | // bipedMasterTM0 = initTM; |
---|
240 | // } |
---|
241 | // else |
---|
242 | // initTM = bipedMasterTM0; |
---|
243 | // |
---|
244 | // streamBipedKeyframes(of, bip, thisNode, keyTimes, interval, initTM); |
---|
245 | // } |
---|
246 | // else |
---|
247 | streamKeyframes(of, thisNode, keyTimes, interval, initTM); |
---|
248 | } |
---|
249 | |
---|
250 | of << "\t\t\t</tracks>" << std::endl; |
---|
251 | |
---|
252 | return true; |
---|
253 | } |
---|
254 | |
---|
255 | bool MeshXMLExporter::streamKeyframes(std::ostream &of, INode *thisNode, Tab<TimeValue> &keyTimes, Interval &interval, Matrix3 &initTM) { |
---|
256 | |
---|
257 | of << "\t\t\t\t<track bone=\"" << removeSpaces(std::string(thisNode->GetName())) << "\">" << std::endl; |
---|
258 | of << "\t\t\t\t\t<keyframes>" << std::endl; |
---|
259 | |
---|
260 | int i; |
---|
261 | int keyTime = -1; |
---|
262 | int start = interval.Start(); |
---|
263 | int end = interval.End(); |
---|
264 | |
---|
265 | /* |
---|
266 | |
---|
267 | -- gets initial transform at frame 0f |
---|
268 | at time 0f ( |
---|
269 | initTform = d.transform ; |
---|
270 | if (not isRootUniversal2 d) then ( |
---|
271 | mparent = d.parent.transform ; |
---|
272 | initTform = initTform*inverse(mparent) ; |
---|
273 | ) |
---|
274 | else if (flipYZ) then ( |
---|
275 | if (not g_MAX) then |
---|
276 | format " - flipping root track..." ; |
---|
277 | -- we add the bip Transform |
---|
278 | --initTform = initTform * d.controller.rootNode.transform ; |
---|
279 | initTform = flipYZTransform initTform ; |
---|
280 | ) |
---|
281 | ) |
---|
282 | */ |
---|
283 | initTM = thisNode->GetNodeTM(0); |
---|
284 | |
---|
285 | Control *c = thisNode->GetTMController(); |
---|
286 | Control *pc = thisNode->GetParentNode()->GetTMController(); |
---|
287 | bool isRoot = false; |
---|
288 | |
---|
289 | if (c > 0) |
---|
290 | if (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) |
---|
291 | isRoot = true; |
---|
292 | // if (pc > 0) |
---|
293 | // if (pc->ClassID() == BIPBODY_CONTROL_CLASS_ID || pc->ClassID() == FOOTPRINT_CLASS_ID) |
---|
294 | // isRoot = true; |
---|
295 | |
---|
296 | TCHAR *tc = thisNode->GetName(); |
---|
297 | if (!isRoot) { |
---|
298 | Matrix3 ptm = thisNode->GetParentTM(0); |
---|
299 | initTM = initTM * Inverse(ptm); |
---|
300 | } |
---|
301 | else if (m_config.getInvertYZ()) { |
---|
302 | initTM = initTM * Inverse(RotateXMatrix(PI/2.0f)); |
---|
303 | } |
---|
304 | |
---|
305 | for (i=0; i<keyTimes.Count(); i++) { |
---|
306 | |
---|
307 | // only operate within the supplied keyframe time range |
---|
308 | if (keyTimes[i] < start) |
---|
309 | continue; |
---|
310 | if (keyTimes[i] > end) |
---|
311 | break; |
---|
312 | |
---|
313 | // ignore key times we've already processed |
---|
314 | if (keyTimes[i] != keyTime) { |
---|
315 | |
---|
316 | keyTime = keyTimes[i]; |
---|
317 | float keyTimef = (float) (keyTimes[i] - start) / (float)GetTicksPerFrame() / (float)GetFrameRate(); |
---|
318 | |
---|
319 | of << "\t\t\t\t\t\t<keyframe time=\"" << keyTimef << "\">" << std::endl; |
---|
320 | |
---|
321 | /* |
---|
322 | |
---|
323 | function flipYZTransform Tform = ( |
---|
324 | local axis1,axis2,axis3,t,m |
---|
325 | |
---|
326 | -- computes the matrix |
---|
327 | axis1 = point3 1 0 0 ; |
---|
328 | axis2 = point3 0 0 1 ; |
---|
329 | axis3 = point3 0 -1 0 ; |
---|
330 | t = point3 0 0 0 ; |
---|
331 | m=matrix3 axis1 axis2 axis3 t ; |
---|
332 | |
---|
333 | -- multiplies by the inverse |
---|
334 | Tform = Tform*inverse(m) ; |
---|
335 | |
---|
336 | return Tform ; |
---|
337 | ) |
---|
338 | |
---|
339 | |
---|
340 | -- First, rotation which depends on initial transformation |
---|
341 | Tform = d.transform ; |
---|
342 | */ |
---|
343 | Matrix3 tm = thisNode->GetNodeTM(keyTime); |
---|
344 | |
---|
345 | /* |
---|
346 | -- if this is the pelvis |
---|
347 | if (isRootUniversal2 d) then ( |
---|
348 | mparent = matrix3 1 ; |
---|
349 | |
---|
350 | if (flipYZ) then |
---|
351 | Tform = flipYZTransform Tform ; |
---|
352 | ) |
---|
353 | else |
---|
354 | mparent = d.parent.transform ; |
---|
355 | */ |
---|
356 | |
---|
357 | // if this node's parent's controller is the biped controller, then this is either Pelvis or Footsteps, |
---|
358 | // and both should be treated as root nodes |
---|
359 | |
---|
360 | Matrix3 ident; |
---|
361 | Matrix3 ptm; |
---|
362 | ident.IdentityMatrix(); |
---|
363 | Control *tmc = thisNode->GetTMController(); |
---|
364 | TCHAR *tc = thisNode->GetName(); |
---|
365 | |
---|
366 | if (tmc->ClassID() == BIPBODY_CONTROL_CLASS_ID) { |
---|
367 | |
---|
368 | ptm = ident; |
---|
369 | if (m_config.getInvertYZ()) { |
---|
370 | tm = tm * Inverse(RotateXMatrix(PI/2.0f)); |
---|
371 | } |
---|
372 | } |
---|
373 | else |
---|
374 | ptm = thisNode->GetParentNode()->GetNodeTM(keyTime); |
---|
375 | |
---|
376 | /* |
---|
377 | |
---|
378 | |
---|
379 | -- computes rotation |
---|
380 | mref = initTform*mparent ; |
---|
381 | Tform = Tform*inverse(mref) ; |
---|
382 | */ |
---|
383 | |
---|
384 | Matrix3 mref = initTM * ptm; |
---|
385 | tm = tm * Inverse(mref); |
---|
386 | |
---|
387 | /* |
---|
388 | |
---|
389 | -- rotation part is saved. |
---|
390 | rot = toAngleAxis Tform.rotation ; |
---|
391 | axis = rot.axis; |
---|
392 | angle = - rot.angle; |
---|
393 | */ |
---|
394 | |
---|
395 | AngAxis aa(tm); |
---|
396 | |
---|
397 | /* |
---|
398 | -- Then, position which depends on parent |
---|
399 | Tform=d.transform ; |
---|
400 | Tform=Tform*inverse(mparent) ; |
---|
401 | |
---|
402 | */ |
---|
403 | |
---|
404 | tm = thisNode->GetNodeTM(keyTime) * Inverse(ptm); |
---|
405 | |
---|
406 | /* |
---|
407 | |
---|
408 | -- if this is the root bone and flipYZ == true |
---|
409 | if (isRootUniversal2 d and flipYZ) then ( |
---|
410 | Tform = flipYZTransform Tform ; |
---|
411 | ) |
---|
412 | |
---|
413 | */ |
---|
414 | |
---|
415 | if (m_config.getInvertYZ() && thisNode->GetParentNode()->GetParentTM(0).IsIdentity()) { |
---|
416 | tm = tm * Inverse(RotateXMatrix(PI/2.0f)); |
---|
417 | } |
---|
418 | |
---|
419 | /* |
---|
420 | -- substracts position of the initial transform |
---|
421 | Tform.pos -= initTform.pos ; |
---|
422 | Tform.pos = Tform.pos * scale ; |
---|
423 | |
---|
424 | pos = Tform.pos ; |
---|
425 | */ |
---|
426 | Point3 trans = tm.GetTrans(); |
---|
427 | trans -= initTM.GetTrans(); |
---|
428 | |
---|
429 | of << "\t\t\t\t\t\t\t<translate x=\"" << trans.x << "\" y=\"" << trans.y << "\" z=\"" << trans.z << "\" />" << std::endl; |
---|
430 | of << "\t\t\t\t\t\t\t<rotate angle=\"" << -aa.angle << "\">" << std::endl; |
---|
431 | of << "\t\t\t\t\t\t\t\t<axis x=\"" << aa.axis.x << "\" y=\"" << aa.axis.y << "\" z=\"" << aa.axis.z << "\" />" << std::endl; |
---|
432 | of << "\t\t\t\t\t\t\t</rotate>" << std::endl; |
---|
433 | |
---|
434 | of << "\t\t\t\t\t\t</keyframe>" << std::endl; |
---|
435 | } |
---|
436 | } |
---|
437 | |
---|
438 | of << "\t\t\t\t\t</keyframes>" << std::endl; |
---|
439 | of << "\t\t\t\t</track>" << std::endl; |
---|
440 | |
---|
441 | return true; |
---|
442 | } |
---|
443 | |
---|
444 | bool MeshXMLExporter::streamBipedKeyframes(std::ostream &of, IBipMaster *bip, INode *thisNode, Tab<TimeValue> &keyTimes, Interval &interval, Matrix3 &initTM) { |
---|
445 | |
---|
446 | of << "\t\t\t\t<track bone=\"" << removeSpaces(std::string(thisNode->GetName())) << "\">" << std::endl; |
---|
447 | of << "\t\t\t\t\t<keyframes>" << std::endl; |
---|
448 | |
---|
449 | int i; |
---|
450 | int keyTime = -1; |
---|
451 | int start = interval.Start(); |
---|
452 | int end = interval.End(); |
---|
453 | Matrix3 tm(thisNode->GetNodeTM(start)); |
---|
454 | Matrix3 ptm(thisNode->GetParentTM(start)); |
---|
455 | |
---|
456 | for (i=0; i<keyTimes.Count(); i++) { |
---|
457 | |
---|
458 | // only operate within the supplied keyframe time range |
---|
459 | if (keyTimes[i] < start) |
---|
460 | continue; |
---|
461 | if (keyTimes[i] > end) |
---|
462 | break; |
---|
463 | |
---|
464 | // ignore key times we've already processed |
---|
465 | if (keyTimes[i] != keyTime) { |
---|
466 | |
---|
467 | keyTime = keyTimes[i]; |
---|
468 | float keyTimef = (float) (keyTimes[i] - start) / (float)GetTicksPerFrame() / (float)GetFrameRate(); |
---|
469 | |
---|
470 | of << "\t\t\t\t\t\t<keyframe time=\"" << keyTimef << "\">" << std::endl; |
---|
471 | |
---|
472 | Control *tmc = thisNode->GetTMController(); |
---|
473 | |
---|
474 | TCHAR *nm = thisNode->GetName(); |
---|
475 | Class_ID cid = tmc->ClassID(); |
---|
476 | |
---|
477 | if (cid == BIPBODY_CONTROL_CLASS_ID || cid == BIPED_CLASS_ID) { |
---|
478 | if (m_config.getInvertYZ()) { |
---|
479 | Matrix3 m = RotateXMatrix(PI / 2.0f); |
---|
480 | tm = tm * Inverse(m); |
---|
481 | } |
---|
482 | } |
---|
483 | else |
---|
484 | tm = tm * Inverse(ptm); |
---|
485 | |
---|
486 | //Point3 trans = bip->GetBipedPos(keyTime, thisNode); |
---|
487 | //Quat q = bip->GetBipedRot(keyTime, thisNode); |
---|
488 | |
---|
489 | Point3 trans = tm.GetTrans(); |
---|
490 | trans = trans * Inverse(initTM); |
---|
491 | trans -= initTM.GetTrans(); |
---|
492 | |
---|
493 | //AngAxis aa(q); |
---|
494 | AngAxis aa(tm); |
---|
495 | float ang = aa.angle; |
---|
496 | Point3 axis = aa.axis; |
---|
497 | |
---|
498 | of << "\t\t\t\t\t\t\t<translate x=\"" << trans.x << "\" y=\"" << trans.y << "\" z=\"" << trans.z << "\" />" << std::endl; |
---|
499 | of << "\t\t\t\t\t\t\t<rotate angle=\"" << -ang << "\">" << std::endl; |
---|
500 | of << "\t\t\t\t\t\t\t\t<axis x=\"" << axis.x << "\" y=\"" << axis.y << "\" z=\"" << axis.z << "\" />" << std::endl; |
---|
501 | of << "\t\t\t\t\t\t\t</rotate>" << std::endl; |
---|
502 | |
---|
503 | of << "\t\t\t\t\t\t</keyframe>" << std::endl; |
---|
504 | } |
---|
505 | } |
---|
506 | |
---|
507 | of << "\t\t\t\t\t</keyframes>" << std::endl; |
---|
508 | of << "\t\t\t\t</track>" << std::endl; |
---|
509 | |
---|
510 | return true; |
---|
511 | } |
---|
512 | |
---|
513 | |
---|
514 | |
---|
515 | #endif |
---|