1include "ogre/lib/ogreSkeletonLib_usefulfns.ms" 2 3----------------------------------------------------------------------------- 4-- gets keyframes from the controllers animation 5----------------------------------------------------------------------------- 6function getTimeList obj firstframe lastframe samplerate IKsamplerate = 7( 8 local list,rotContr,posContr,e ; 9 list = #(firstframe) ; 10 list2 = #() ; -- this is the list which will be returned. 11 12 -- Biped Bones and the root: Bip01 for example don't have the same controller 13 14 -- Root : Bip01 15 if (isPelvis obj) then ( 16 -- vertical controller 17 for e in obj.controller.vertical.controller.keys do ( 18 t =e.time ; 19 if (t>firstFrame and t<=lastFrame) then ( 20 append list t ; 21 --print t ; 22 ) 23 ) 24 -- horizontal controller 25 for e in obj.controller.horizontal.controller.keys do ( 26 t =e.time ; 27 if (t>firstFrame and t<=lastFrame) then 28 append list t ; 29 ) 30 -- turn controller 31 for e in obj.controller.turning.controller.keys do ( 32 t =e.time ; 33 if (t>firstFrame and t<=lastFrame) then 34 append list t ; 35 ) 36 sort list ; 37 ) 38 -- Biped Bones 39 else if (isKindOf obj Biped_Object) then ( 40 41 for e in obj.controller.keys do ( 42 t =e.time ; 43 if (t>firstFrame and t<=lastFrame) then 44 append list t ; 45 ) 46 ) 47 -- Standard Bones 48 else 49 ( 50 --print obj.name; 51 --print (classof obj.controller) ; 52 if (classof obj.controller == prs) then -- standard controller 53 ( 54 rotContr = obj.rotation.controller ; 55 posContr = obj.pos.controller ; 56 57 for e in rotContr.keys do 58 ( 59 t = e.time ; 60 if (t>firstFrame and t<=lastFrame) then 61 append list t ; 62 ) 63 for e in posContr.keys do 64 ( 65 t = e.time ; 66 if (t>firstFrame and t<=lastFrame) then 67 append list t ; 68 ) 69 ) 70 else if ((classof obj.controller == IK_ControllerMatrix3Controller) or (classof obj.controller == IKControl)) then -- IK controller 71 ( 72 local IKSR = IKsamplerate; 73 74 if (IKSR == 0.0) then 75 IKSR = 1.0; 76 77 i=firstFrame as Float; 78 while (i<=lastFrame) do 79 ( 80 append list (i as Float); 81 i = i + IKSR; 82 ) 83 ) 84 ) 85 86 append list (firstFrame as Float); -- add a keyframe on the first frame 87 append list (lastFrame as Float); -- add a keyframe on then last frame 88 89 if (samplerate > 0) then -- sample the animation by adding keyframes on regular intervals 90 ( 91 i=firstFrame as Float; 92 while (i<=lastFrame) do 93 ( 94 append list (i as Float); 95 i = i + samplerate; 96 ) 97 ) 98 sort list ; 99 100 -- if several keyframes have the same value, we keep just one 101 keepLoneValues list list2 ; 102 list2 ; 103) 104 105 106----------------------------------------------------------------------------- 107-- write <track /> 108-- Selected keys belongs to [firstframe,lastFrame] 109-- time = (key.time - firstFrame)*length/(lastFrame-firstFrame) 110-- (e.g. first key has time 0.) 111----------------------------------------------------------------------------- 112function writeTrack bone_name boneId firstframe lastframe samplerate IKsamplerate length scale flipYZ outFile= 113( 114 local angle,timef,i,bname,d,mref,mparent ; 115 116 -- displays information in the maxscript listener 117 if (not g_MAX) then 118 format "retrieving key information for % ...\n" (bone_name) ; 119 120 -- gets bone acording to the parameter boneId 121 bname = bone_name ; 122 replaceSpaces bname ; 123 d = getNodeByName bname ; 124 125 -- gets keyframe list 126 timelist = getTimeList d firstframe lastframe samplerate IKsamplerate; 127 128 -- track header 129 format("\t\t\t\t<track bone = \"%\">\n") bname to:outFile ; 130 format("\t\t\t\t\t<keyframes>\n") to:outFile ; 131 132 -- gets initial transform at frame 0f 133 at time 0f ( 134 initTform = d.transform ; 135 if (not isRootUniversal2 d) then ( 136 mparent = d.parent.transform ; 137 initTform = initTform*inverse(mparent) ; 138 ) 139 else if (flipYZ) then ( 140 if (not g_MAX) then 141 format " - flipping root track..." ; 142 -- we add the bip Transform 143 --initTform = initTform * d.controller.rootNode.transform ; 144 initTform = flipYZTransform initTform ; 145 ) 146 ) 147 148 -- for each frame in the list 149 for i in timelist do 150 ( 151 -- moves slider time and compute OGRE time 152 at time i ( 153 timef = ((float) (i-firstFrame)*length)/(lastframe - firstframe ) ; 154 155 -- First, rotation which depends on initial transformation 156 Tform = d.transform ; 157 -- if this is the pelvis 158 if (isRootUniversal2 d) then ( 159 mparent = matrix3 1 ; 160 -- if flipYZ == true 161 if (flipYZ) then 162 Tform = flipYZTransform Tform ; 163 ) 164 else 165 mparent = d.parent.transform ; 166 167 -- computes rotation 168 mref = initTform*mparent ; 169 Tform = Tform*inverse(mref) ; 170 171 -- rotation part is saved. 172 --rot = Tform.rotation as angleaxis ; 173 --angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :(((((( 174 rot = toAngleAxis Tform.rotation ; 175 axis = rot.axis; 176 angle = - rot.angle; 177 178 -- Then, position which depends on parent 179 Tform=d.transform ; 180 Tform=Tform*inverse(mparent) ; 181 182 -- if this is the root bone and flipYZ == true 183 if (isRootUniversal2 d and flipYZ) then ( 184 Tform = flipYZTransform Tform ; 185 ) 186 187 -- substracts position of the initial transform 188 Tform.pos -= initTform.pos ; 189 Tform.pos = Tform.pos * scale ; 190 191 pos = Tform.pos ; 192 193 -- writes them ! 194 if (abs(pos.x)<1e-5) then pos.x = 0 ; 195 if (abs(pos.y)<1e-5) then pos.y = 0 ; 196 if (abs(pos.z)<1e-5) then pos.z = 0 ; 197 198 format("\t\t\t\t\t\t<keyframe time=\"%\">\n") timef to: outFile ; 199 format("\t\t\t\t\t\t\t<translate x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to: outFile ; 200 format("\t\t\t\t\t\t\t<rotate angle=\"%\">\n") angle to:outFile ; 201 format("\t\t\t\t\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") (axis.x) (axis.y) (axis.z) to:outFile ; 202 format("\t\t\t\t\t\t\t</rotate>\n") to:outFile ; 203 format("\t\t\t\t\t\t</keyframe>\n") to:outFile ; 204 ) 205 ) 206 207 -- track end 208 format("\t\t\t\t\t</keyframes>\n") to:outFile ; 209 format("\t\t\t\t</track>\n") to: outFile ; 210) 211 212------------------------------------------------------------------------------------------------- 213------------------------------------------- WRITE SKELETON -------------------------------------- 214------------------------------------------------------------------------------------------------- 215 216------------------------------------- 217-- List of bones in the hierarchy 218------------------------------------- 219global BonesList=#() 220global RootsList=#() 221 222------------------------------------- 223-- helper functions to build skeleton 224------------------------------------- 225 226----------------------------------------------------------------- 227-- recursive function to build the list of bones for the skeleton 228----------------------------------------------------------------- 229function computeBList b sk phy exportHelpers = 230( 231 bname = b ; 232 bone = getNodeByName bname ; 233 if (findItem BonesList bname == 0) then 234 if (isKindOf bone BoneGeometry or iskindOf bone Biped_Object or (exportHelpers and (isPartOfModifier bone sk phy)) ) then 235 append BonesList bname ; 236 childrenArray = bone.children ; 237 for i=1 to childrenArray.count do 238 ( 239 if (isKindOf bone BoneGeometry or iskindOf bone Biped_Object or (exportHelpers and (isPartOfModifier bone sk phy))) then 240 computeBList (replaceSpaces childrenArray[i].name) sk phy exportHelpers; 241 ) 242) 243 244function addHelpersToHierarchy phy sk = 245( 246 if (sk != undefined) then 247 ( 248 for i=1 to (skinOps.GetNumberBones sk) do 249 ( 250 bname = skinOps.GetBoneName sk i 1 ; 251 replaceSpaces bname ; 252 d = getNodeByName bname ; 253 254 if (iskindof d helper) then 255 append BonesList bname ; 256 ) 257 ) 258 else if (phy != undefined) then 259 ( 260 for i=1 to (physiqueOps.GetBoneCount $) do 261 ( 262 bname = (physiqueOps.GetBones $)[i].name; 263 replaceSpaces bname ; 264 d = getNodeByName bname ; 265 266 if (iskindof d helper) then 267 append BonesList bname ; 268 ) 269 ) 270) 271 272----------------------------------------------------------------- 273-- find the root(s) of the rhierarchy 274----------------------------------------------------------------- 275function getHierarchyRoots phy sk exportHelpers = 276( 277 local rootstab=#(); 278 279 if (sk != undefined) then 280 ( 281 for i=1 to (skinOps.GetNumberBones sk) do 282 ( 283 bname= skinOps.GetBoneName sk i 1 ; 284 replaceSpaces bname ; 285 d = getNodeByName bname ; 286 287 while (d.parent!=undefined and (iskindof d.parent BoneGeometry or iskindOf d.parent Biped_Object or (exportHelpers and (isPartOfModifier d.parent sk phy)) )) do 288 ( 289 d = d.parent 290 ) 291 trouve = 0; 292 --format("new potential root bone \"%\"\n") (replaceSpaces d.name) ; 293 for j=1 to rootstab.count do 294 ( 295 if (rootstab[j]!=undefined and (rootstab[j]==(replaceSpaces d.name)))then 296 ( 297 trouve = 1; 298 exit; 299 ) 300 ) 301 if trouve==0 then 302 ( 303 if (iskindof d BoneGeometry or iskindOf d Biped_Object or (exportHelpers and (isPartOfModifier d sk phy)) ) then 304 ( 305 if (not g_MAX) then 306 format("new root bone \"%\"\n") (replaceSpaces d.name) ; 307 rootstab[rootstab.count+1] = (replaceSpaces d.name) ; 308 ) 309 ) 310 ) 311 ) 312 else if (phy != undefined) then -- physique modifier 313 ( 314 for i=1 to (physiqueOps.GetBoneCount $) do 315 ( 316 bname = (physiqueOps.GetBones $)[i].name; 317 replaceSpaces bname ; 318 d = getNodeByName bname ; 319 320 while (d.parent!=undefined and (iskindof d.parent BoneGeometry or iskindOf d.parent Biped_Object or (exportHelpers and (isPartOfModifier d.parent sk phy)) )) do 321 ( 322 d = d.parent 323 ) 324 trouve = 0; 325 --format("new potential root bone \"%\"\n") (replaceSpaces d.name) ; 326 for j=1 to rootstab.count do 327 ( 328 if (rootstab[j]!=undefined and (rootstab[j]==(replaceSpaces d.name)))then 329 ( 330 trouve = 1; 331 exit; 332 ) 333 ) 334 if trouve==0 then 335 ( 336 if (iskindof d BoneGeometry or iskindOf d Biped_Object or (exportHelpers and (isPartOfModifier d sk phy))) then 337 ( 338 if (not g_MAX) then 339 format("new root bone \"%\"\n") (replaceSpaces d.name) ; 340 rootstab[rootstab.count+1] = (replaceSpaces d.name) ; 341 ) 342 ) 343 ) 344 ) 345 346 rootstab; 347) 348 349----------------------------------------------------------------- 350-- function to build the list of bones for the skeleton 351----------------------------------------------------------------- 352function computeBonesList phy sk exportHelpers = 353( 354 RootsList = getHierarchyRoots phy sk exportHelpers; -- find the roots of the current hierarchy 355 print RootsList; 356 for b in RootsList do 357 ( 358 computeBList b sk phy exportHelpers; 359 ) 360 361 -- add the nodes that are parts of the skin (or physique) modifier but are neither biped_object nor standard bones 362 -- only helpers (Point, Dummy) at the moment... 363 -- addHelpersToHierarchy phy sk ; 364) 365 366------------------ 367-- write <bones /> 368------------------ 369function writeB bone_name id scale flipYZ outFile = 370( 371 -- gets bone acording to the parameter boneId 372 bname = bone_name ; 373 replaceSpaces bname; 374 d = getNodeByName bname ; 375 376 -- gets initial transform at frame 0f 377 format("\t\t<bone id=\"%\" name=\"%\">\n") (id-1) bname to:outFile ; 378 379 slidertime = 0f ; 380 Tform = d.transform ; 381 if (not isRootUniversal2 d) then ( 382 mparent = d.parent.transform ; 383 Tform = Tform*inverse(mparent) ; 384 ) 385 386 Tform.pos = Tform.pos * scale ; 387 388 if ((isRootUniversal2 d) and flipYZ) then ( 389 if (not g_MAX) then 390 format "- Flipping root... \n" ; 391 Tform = flipYZTransform Tform ; 392 ) 393 394 pos = Tform.pos ; 395 --rot = Tform.rotation as angleaxis ; 396 --angle = - degToRad (rot.angle) ; -- don't know why there must be this minus :(((((( 397 rot = toAngleAxis Tform.rotation ; 398 angle = - rot.angle ; 399 400 -- if (abs(pos.x)<1e-5) then pos.x = 0 ; 401 -- if (abs(pos.y)<1e-5) then pos.y = 0 ; 402 -- if (abs(pos.z)<1e-5) then pos.z = 0 ; 403 404 -- Only object.transform was taken into account, but when mirror is applied 405 -- object.scale is modified and become [-1,-1,-1] that's why we do what follows: 406 if ((d.parent != undefined) and (hasproperty d.parent "scale")) then ( 407 pos = pos * d.parent.scale ; 408 ) 409 410 format("\t\t\t<position x=\"%\" y=\"%\" z=\"%\" />\n") pos.x pos.y pos.z to:outFile ; 411 format("\t\t\t<rotation angle=\"%\">\n") angle to:outFile ; 412 format("\t\t\t\t<axis x=\"%\" y=\"%\" z=\"%\" />\n") rot.axis.x rot.axis.y rot.axis.z to:outFile ; 413 format("\t\t\t</rotation>\n") to:outFile ; 414 format("\t\t</bone>\n") to:outFile ; 415) 416 417----------------------------- 418-- write Bones (using writeB) 419----------------------------- 420function writeBones phy sk scale flipYZ exportHelpers outFile = 421( 422 local i ; 423 424 OgreExportObject.exportProgress.value = 0; 425 426 if (BonesList.count == 0) then 427 computeBonesList phy sk exportHelpers; 428 429 format("\t<bones>\n") to:outFile; 430 431 i = 0 ; 432 433 for i=1 to BonesList.count do 434 ( 435 OgreExportObject.exportProgress.value = (100.0*i/BonesList.count); 436 437 writeB BonesList[i] i scale flipYZ outFile ; 438 ) 439 440 format("\t</bones>\n") to:outFile; 441 442 OgreExportObject.exportProgress.value = 100; 443) 444 445-------------------------- 446-- write <bonehierarchy /> 447-------------------------- 448function writeH b outFile = 449( 450 if (not isRootUniversal2 b) then 451 ( 452 p = b.parent ; 453 format("\t\t<boneparent bone=\"%\" parent=\"%\" />\n") (replaceSpaces b.name) (replaceSpaces p.name) to:outFile ; 454 ) 455) 456 457function writeHierarchy outFile = 458( 459 OgreExportObject.exportProgress.value = 0; 460 461 local bname,pelvis 462 format("\t<bonehierarchy>\n") to:outFile ; 463 464 for i=1 to BonesList.count do 465 ( 466 OgreExportObject.exportProgress.value = (100.0*i/BonesList.count); 467 468 b = getNodeByName BonesList[i] ; 469 writeH b outFile ; 470 ) 471 472 format("\t</bonehierarchy>\n") to:outFile ; 473 474 OgreExportObject.exportProgress.value = 100; 475) 476 477----------------------- 478-- write <animations /> 479----------------------- 480function writeAnim Anims samplerate IKsamplerate scale flipYZ outFile = 481( 482 local i,n ; 483 484 OgreExportObject.exportProgress.value = 0; 485 486 format("\t<animations>\n") to: outFile ; 487 488 for anm=1 to Anims.names.count do 489 ( 490 format("\t\t<animation name=\"%\" length=\"%\">\n") Anims.names[anm] Anims.lengths[anm] to:outFile ; 491 format("\t\t\t<tracks>\n") to:outFile 492 493 n = BonesList.count ; 494 for i = 1 to n do 495 ( 496 OgreExportObject.exportProgress.value = (100.0*i/BonesList.count)/Anims.names.count; 497 498 writeTrack BonesList[i] i Anims.startframes[anm] Anims.endframes[anm] samplerate IKsamplerate Anims.lengths[anm] scale flipYZ outFile ; 499 ) 500 501 format("\t\t\t</tracks>\n") to:outFile 502 format("\t\t</animation>\n") to: outFile ; 503 ) 504 505 format("\t</animations>\n") to: outFile ; 506 507 OgreExportObject.exportProgress.value = 100; 508) 509 510------------------------------------------------------------- 511-- write <skeleton /> main function 512-- write the animation in the file out_name + ".skeleton.xml" 513-- between the frame firstFrame and lastFrame 514-- and scale time according to length 515------------------------------------------------------------- 516 517function writeSkeleton pmesh exportOptions Anims out_name = 518( 519 local sk,n,keys,initialKeys,messages,phy ; 520 521 sk = getSkin pmesh ; 522 phy = getPhysique pmesh ; 523 524 if (sk == undefined and phy == undefined) then 525 ( 526 MessageBox "There is no skin or physique modifier for this object" ; 527 return false; 528 ) 529 else 530 ( 531 -- in order to perform, skin should be opened 532 max modify mode ; 533 if (sk != undefined) then 534 modPanel.setCurrentObject pmesh.modifiers[#Skin] ; 535 else -- physique 536 modPanel.setCurrentObject pmesh.modifiers[#Physique] ; 537 538 if (not g_MAX) then 539 ( 540 format "------------------------------------------\n" 541 format "------ OGRE skeleton Exporter Log ------\n" 542 format "------------------------------------------\n" 543 544 format "Exporter options :\n" 545 for i=1 to Anims.names.count do 546 format "Anim % - firstFrame: % - lastFrame: %\n" Anims.names[i] Anims.startframes[i] Anims.endframes[i] ; 547 -- creates the output file 548 outFile = createfile (out_name + ".skeleton.xml") ; 549 ) 550 else 551 ( 552 if (g_MAX_use_listener) then 553 format("<ogrestartfile>%</ogrestartfile><ogrestartdata>\n") (outName + ".skeleton.xml"); 554 outFile = listener; 555 ) 556 557 -- writes header 558 format("<skeleton>\n") to:outFile ; 559 560 if (not g_MAX) then 561 format "Writing bones :\n" ; 562 writeBones phy sk exportOptions.scale exportOptions.flipYZ exportOptions.exportHelpers outFile ; 563 564 if (not g_MAX) then 565 format "Writing bone hierarchy.\n" ; 566 writeHierarchy outFile ; 567 568 if (not g_MAX) then 569 format "Writing bone tracks.\n" ; 570 writeAnim Anims exportOptions.sampleRate exportOptions.ikSampleRate exportOptions.scale exportOptions.flipYZ outFile ; 571 572 -- ecriture, fin des balises 573 format("</skeleton>\n") to: outFile ; 574 575 if (not g_MAX) then 576 ( 577 format "------------------------------------------\n" 578 format "---------- END ---------\n" 579 format "------------------------------------------\n" 580 581 close outFile ; 582 ) 583 else 584 ( 585 if (g_MAX_use_listener) then 586 format("</ogrestartdata>\n") to: outFile; 587 ) 588 589 messageBox "Exporting skeleton successful !" 590 return true; 591 ) 592) 593 594 595 596