tsAnimate.cpp
Engine/source/ts/tsAnimate.cpp
Public Functions
compareThreads(const void * e1, const void * e2)
Detailed Description
Public Functions
compareThreads(const void * e1, const void * e2)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "ts/tsShapeInstance.h" 25 26//---------------------------------------------------------------------------------- 27// some utility functions 28//------------------------------------------------------------------------------------- 29 30S32 QSORT_CALLBACK FN_CDECL compareThreads( const void* e1, const void* e2) 31{ 32 const TSThread * th1 = *(const TSThread**)e1; 33 const TSThread * th2 = *(const TSThread**)e2; 34 return (*th1 < *th2); 35} 36 37void TSShapeInstance::sortThreads() 38{ 39 PROFILE_SCOPE( TSShapeInstance_sortThreads ); 40 dQsort(mThreadList.address(),mThreadList.size(),sizeof(TSThread*),compareThreads); 41 dQsort(mTransitionThreads.address(),mTransitionThreads.size(),sizeof(TSThread*),compareThreads); 42} 43 44void TSShapeInstance::setDirty(U32 dirty) 45{ 46 AssertFatal((dirty & AllDirtyMask) == dirty,"TSShapeInstance::setDirty: illegal dirty flags"); 47 for (S32 i=0; i<mShape->subShapeFirstNode.size(); i++) 48 mDirtyFlags[i] |= dirty; 49} 50 51void TSShapeInstance::clearDirty(U32 dirty) 52{ 53 AssertFatal((dirty & AllDirtyMask) == dirty,"TSShapeInstance::clearDirty: illegal dirty flags"); 54 for (S32 i=0; i<mShape->subShapeFirstNode.size(); i++) 55 mDirtyFlags[i] &= ~dirty; 56} 57 58//------------------------------------------------------------------------------------- 59// Animate nodes 60//------------------------------------------------------------------------------------- 61 62void TSShapeInstance::animateNodes(S32 ss) 63{ 64 PROFILE_SCOPE( TSShapeInstance_animateNodes ); 65 66 if (!mShape->nodes.size()) 67 return; 68 69 // @todo: When a node is added, we need to make sure to resize the nodeTransforms array as well 70 mNodeTransforms.setSize(mShape->nodes.size()); 71 72 // temporary storage for node transforms 73 smNodeCurrentRotations.setSize(mShape->nodes.size()); 74 smNodeCurrentTranslations.setSize(mShape->nodes.size()); 75 smNodeLocalTransforms.setSize(mShape->nodes.size()); 76 smRotationThreads.setSize(mShape->nodes.size()); 77 smTranslationThreads.setSize(mShape->nodes.size()); 78 79 TSIntegerSet rotBeenSet; 80 TSIntegerSet tranBeenSet; 81 TSIntegerSet scaleBeenSet; 82 rotBeenSet.setAll(mShape->nodes.size()); 83 tranBeenSet.setAll(mShape->nodes.size()); 84 scaleBeenSet.setAll(mShape->nodes.size()); 85 smNodeLocalTransformDirty.clearAll(); 86 87 S32 i,j,nodeIndex,a,b,start,end,firstBlend = mThreadList.size(); 88 for (i=0; i<mThreadList.size(); i++) 89 { 90 TSThread * th = mThreadList[i]; 91 92 const TSShape::Sequence* threadSequence = th->getSequence(); 93 94 if (threadSequence->isBlend()) 95 { 96 // blend sequences need default (if not set by other sequence) 97 // break rather than continue because the rest will be blends too 98 firstBlend = i; 99 break; 100 } 101 rotBeenSet.takeAway(threadSequence->rotationMatters); 102 tranBeenSet.takeAway(threadSequence->translationMatters); 103 scaleBeenSet.takeAway(threadSequence->scaleMatters); 104 } 105 rotBeenSet.takeAway(mCallbackNodes); 106 rotBeenSet.takeAway(mHandsOffNodes); 107 rotBeenSet.overlap(mMaskRotationNodes); 108 109 TSIntegerSet maskPosNodes=<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1a754a6ea85f253ed7d1fd9c46e4a69ab4">mMaskPosXNodes</a>; 110 maskPosNodes.overlap(mMaskPosYNodes); 111 maskPosNodes.overlap(mMaskPosZNodes); 112 tranBeenSet.overlap(maskPosNodes); 113 114 tranBeenSet.takeAway(mCallbackNodes); 115 tranBeenSet.takeAway(mHandsOffNodes); 116 // can't add masked nodes since x, y, & z masked separately... 117 // we'll set default regardless of mask status 118 119 // all the nodes marked above need to have the default transform 120 a = mShape->subShapeFirstNode[ss]; 121 b = a + mShape->subShapeNumNodes[ss]; 122 for (i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 123 { 124 if (rotBeenSet.test(i)) 125 { 126 mShape->defaultRotations[i].getQuatF(&smNodeCurrentRotations[i]); 127 smRotationThreads[i] = NULL; 128 } 129 if (tranBeenSet.test(i)) 130 { 131 smNodeCurrentTranslations[i] = mShape->defaultTranslations[i]; 132 smTranslationThreads[i] = NULL; 133 } 134 } 135 136 // don't want a transform in these cases... 137 rotBeenSet.overlap(mHandsOffNodes); 138 rotBeenSet.overlap(mCallbackNodes); 139 tranBeenSet.takeAway(maskPosNodes); 140 tranBeenSet.overlap(mHandsOffNodes); 141 tranBeenSet.overlap(mCallbackNodes); 142 143 // default scale 144 if (scaleCurrentlyAnimated()) 145 handleDefaultScale(a,b,scaleBeenSet); 146 147 // handle non-blend sequences 148 for (i=0; i<firstBlend; i++) 149 { 150 TSThread * th = mThreadList[i]; 151 152 j=0; 153 start = th->getSequence()->rotationMatters.start(); 154 end = b; 155 for (nodeIndex=start; nodeIndex<end; th->getSequence()->rotationMatters.next(nodeIndex), j++) 156 { 157 // skip nodes outside of this detail 158 if (nodeIndex<a) 159 continue; 160 if (!rotBeenSet.test(nodeIndex)) 161 { 162 QuatF q1,q2; 163 mShape->getRotation(*th->getSequence(),th->keyNum1,j,&q1); 164 mShape->getRotation(*th->getSequence(),th->keyNum2,j,&q2); 165 TSTransform::interpolate(q1,q2,th->keyPos,&smNodeCurrentRotations[nodeIndex]); 166 rotBeenSet.set(nodeIndex); 167 smRotationThreads[nodeIndex] = th; 168 } 169 } 170 171 j=0; 172 start = th->getSequence()->translationMatters.start(); 173 end = b; 174 for (nodeIndex=start; nodeIndex<end; th->getSequence()->translationMatters.next(nodeIndex), j++) 175 { 176 if (nodeIndex<a) 177 continue; 178 if (!tranBeenSet.test(nodeIndex)) 179 { 180 if (maskPosNodes.test(nodeIndex)) 181 handleMaskedPositionNode(th,nodeIndex,j); 182 else 183 { 184 const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,j); 185 const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,j); 186 TSTransform::interpolate(p1,p2,th->keyPos,&smNodeCurrentTranslations[nodeIndex]); 187 smTranslationThreads[nodeIndex] = th; 188 } 189 tranBeenSet.set(nodeIndex); 190 } 191 } 192 193 if (scaleCurrentlyAnimated()) 194 handleAnimatedScale(th,a,b,scaleBeenSet); 195 } 196 197 // compute transforms 198 for (i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 199 { 200 if (!mHandsOffNodes.test(i)) 201 TSTransform::setMatrix(smNodeCurrentRotations[i],smNodeCurrentTranslations[i],&smNodeLocalTransforms[i]); 202 else 203 smNodeLocalTransforms[i] = mNodeTransforms[i]; // in case mNodeTransform was changed externally 204 } 205 206 // add scale onto transforms 207 if (scaleCurrentlyAnimated()) 208 handleNodeScale(a,b); 209 210 // get callbacks... 211 start = getMax(mCallbackNodes.start(),a); 212 end = getMin(mCallbackNodes.end(),b); 213 for (i=0; i<mNodeCallbacks.size(); i++) 214 { 215 AssertFatal(mNodeCallbacks[i].callback, "No callback method defined"); 216 S32 nodeIdx = mNodeCallbacks[i].nodeIndex; 217 if (nodeIdx >=start && nodeIdx<end) 218 { 219 mNodeCallbacks[i].callback->setNodeTransform(this, nodeIdx, smNodeLocalTransforms[nodeIdx]); 220 smNodeLocalTransformDirty.set(nodeIdx); 221 } 222 } 223 224 // handle blend sequences 225 for (i=firstBlend; i<mThreadList.size(); i++) 226 { 227 TSThread * th = mThreadList[i]; 228 if (th->blendDisabled) 229 continue; 230 231 handleBlendSequence(th,a,b); 232 } 233 234 // transitions... 235 if (inTransition()) 236 handleTransitionNodes(a,b); 237 238 // multiply transforms... 239 for (i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 240 { 241 S32 parentIdx = mShape->nodes[i].parentIndex; 242 if (parentIdx < 0) 243 mNodeTransforms[i] = smNodeLocalTransforms[i]; 244 else 245 mNodeTransforms[i].mul(mNodeTransforms[parentIdx],smNodeLocalTransforms[i]); 246 } 247} 248 249void TSShapeInstance::handleDefaultScale(S32 a, S32 b, TSIntegerSet & scaleBeenSet) 250{ 251 // set default scale values (i.e., identity) and do any initialization 252 // relating to animated scale (since scale normally not animated) 253 254 smScaleThreads.setSize(mShape->nodes.size()); 255 scaleBeenSet.takeAway(mCallbackNodes); 256 scaleBeenSet.takeAway(mHandsOffNodes); 257 if (animatesUniformScale()) 258 { 259 smNodeCurrentUniformScales.setSize(mShape->nodes.size()); 260 for (S32 i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 261 if (scaleBeenSet.test(i)) 262 { 263 smNodeCurrentUniformScales[i] = 1.0f; 264 smScaleThreads[i] = NULL; 265 } 266 } 267 else if (animatesAlignedScale()) 268 { 269 smNodeCurrentAlignedScales.setSize(mShape->nodes.size()); 270 for (S32 i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 271 if (scaleBeenSet.test(i)) 272 { 273 smNodeCurrentAlignedScales[i].set(1.0f,1.0f,1.0f); 274 smScaleThreads[i] = NULL; 275 } 276 } 277 else 278 { 279 smNodeCurrentArbitraryScales.setSize(mShape->nodes.size()); 280 for (S32 i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 281 if (scaleBeenSet.test(i)) 282 { 283 smNodeCurrentArbitraryScales[i].identity(); 284 smScaleThreads[i] = NULL; 285 } 286 } 287 288 scaleBeenSet.overlap(mHandsOffNodes); 289 scaleBeenSet.overlap(mCallbackNodes); 290} 291 292void TSShapeInstance::updateTransitionNodeTransforms(TSIntegerSet& transitionNodes) 293{ 294 // handle transitions 295 transitionNodes.clearAll(); 296 transitionNodes.overlap(mTransitionRotationNodes); 297 transitionNodes.overlap(mTransitionTranslationNodes); 298 transitionNodes.overlap(mTransitionScaleNodes); 299 transitionNodes.takeAway(mHandsOffNodes); 300 301 // Decompose transforms for nodes affected by the transition. Only need to do 302 // for blended or scale-animated nodes, as all others are already up to date 303 for (S32 i=transitionNodes.start(); i<MAX_TS_SET_SIZE; transitionNodes.next(i)) 304 { 305 if (smNodeLocalTransformDirty.test(i)) 306 { 307 if (scaleCurrentlyAnimated()) 308 { 309 // @todo:No support for scale yet => need to do proper affine decomposition here 310 smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition(); 311 smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]); 312 } 313 else 314 { 315 // Scale is identity => can do a cheap decomposition 316 smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition(); 317 smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]); 318 } 319 } 320 } 321} 322 323void TSShapeInstance::handleTransitionNodes(S32 a, S32 b) 324{ 325 TSIntegerSet transitionNodes; 326 updateTransitionNodeTransforms(transitionNodes); 327 328 S32 nodeIndex; 329 S32 start = mTransitionRotationNodes.start(); 330 S32 end = b; 331 for (nodeIndex=start; nodeIndex<end; mTransitionRotationNodes.next(nodeIndex)) 332 { 333 if (nodeIndex<a) 334 continue; 335 TSThread * thread = smRotationThreads[nodeIndex]; 336 thread = thread && thread->transitionData.inTransition ? thread : NULL; 337 if (!thread) 338 { 339 // if not controlled by a sequence in transition then there must be 340 // some other thread out there that used to control us that is in 341 // transition now...use that thread to control interpolation 342 for (S32 i=0; i<mTransitionThreads.size(); i++) 343 { 344 if (mTransitionThreads[i]->transitionData.oldRotationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->rotationMatters.test(nodeIndex)) 345 { 346 thread = mTransitionThreads[i]; 347 break; 348 } 349 } 350 AssertFatal(thread!=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>,"TSShapeInstance::handleRotTransitionNodes (rotation)"); 351 } 352 QuatF tmpQ; 353 TSTransform::interpolate(mNodeReferenceRotations[nodeIndex].getQuatF(&tmpQ),smNodeCurrentRotations[nodeIndex],thread->transitionData.pos,&smNodeCurrentRotations[nodeIndex]); 354 } 355 356 // then translation 357 start = mTransitionTranslationNodes.start(); 358 end = b; 359 for (nodeIndex=start; nodeIndex<end; mTransitionTranslationNodes.next(nodeIndex)) 360 { 361 TSThread * thread = smTranslationThreads[nodeIndex]; 362 thread = thread && thread->transitionData.inTransition ? thread : NULL; 363 if (!thread) 364 { 365 // if not controlled by a sequence in transition then there must be 366 // some other thread out there that used to control us that is in 367 // transition now...use that thread to control interpolation 368 for (S32 i=0; i<mTransitionThreads.size(); i++) 369 { 370 if (mTransitionThreads[i]->transitionData.oldTranslationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->translationMatters.test(nodeIndex)) 371 { 372 thread = mTransitionThreads[i]; 373 break; 374 } 375 } 376 AssertFatal(thread!=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>,"TSShapeInstance::handleTransitionNodes (translation)."); 377 } 378 Point3F & p = smNodeCurrentTranslations[nodeIndex]; 379 Point3F & p1 = mNodeReferenceTranslations[nodeIndex]; 380 Point3F & p2 = p; 381 F32 k = thread->transitionData.pos; 382 p.x = p1.x + k * (p2.x-p1.x); 383 p.y = p1.y + k * (p2.y-p1.y); 384 p.z = p1.z + k * (p2.z-p1.z); 385 } 386 387 // then scale... 388 if (scaleCurrentlyAnimated()) 389 { 390 start = mTransitionScaleNodes.start(); 391 end = b; 392 for (nodeIndex=start; nodeIndex<end; mTransitionScaleNodes.next(nodeIndex)) 393 { 394 TSThread * thread = smScaleThreads[nodeIndex]; 395 thread = thread && thread->transitionData.inTransition ? thread : NULL; 396 if (!thread) 397 { 398 // if not controlled by a sequence in transition then there must be 399 // some other thread out there that used to control us that is in 400 // transition now...use that thread to control interpolation 401 for (S32 i=0; i<mTransitionThreads.size(); i++) 402 { 403 if (mTransitionThreads[i]->transitionData.oldScaleNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->scaleMatters.test(nodeIndex)) 404 { 405 thread = mTransitionThreads[i]; 406 break; 407 } 408 } 409 AssertFatal(thread!=<a href="/coding/file/types_8lint_8h/#types_8lint_8h_1a070d2ce7b6bb7e5c05602aa8c308d0c4">NULL</a>,"TSShapeInstance::handleTransitionNodes (scale)."); 410 } 411 if (animatesUniformScale()) 412 smNodeCurrentUniformScales[nodeIndex] += thread->transitionData.pos * (mNodeReferenceUniformScales[nodeIndex]-smNodeCurrentUniformScales[nodeIndex]); 413 else if (animatesAlignedScale()) 414 TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentAlignedScales[nodeIndex],thread->transitionData.pos,&smNodeCurrentAlignedScales[nodeIndex]); 415 else 416 { 417 QuatF q; 418 TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentArbitraryScales[nodeIndex].mScale,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mScale); 419 TSTransform::interpolate(mNodeReferenceArbitraryScaleRots[nodeIndex].getQuatF(&q),smNodeCurrentArbitraryScales[nodeIndex].mRotate,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mRotate); 420 } 421 } 422 } 423 424 // update transforms for transition nodes 425 start = transitionNodes.start(); 426 end = b; 427 for (nodeIndex=start; nodeIndex<end; transitionNodes.next(nodeIndex)) 428 { 429 TSTransform::setMatrix(smNodeCurrentRotations[nodeIndex], smNodeCurrentTranslations[nodeIndex], &smNodeLocalTransforms[nodeIndex]); 430 if (scaleCurrentlyAnimated()) 431 { 432 if (animatesUniformScale()) 433 TSTransform::applyScale(smNodeCurrentUniformScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]); 434 else if (animatesAlignedScale()) 435 TSTransform::applyScale(smNodeCurrentAlignedScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]); 436 else 437 TSTransform::applyScale(smNodeCurrentArbitraryScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]); 438 } 439 } 440} 441 442void TSShapeInstance::handleNodeScale(S32 a, S32 b) 443{ 444 if (animatesUniformScale()) 445 { 446 for (S32 i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 447 if (!mHandsOffNodes.test(i)) 448 TSTransform::applyScale(smNodeCurrentUniformScales[i],&smNodeLocalTransforms[i]); 449 } 450 else if (animatesAlignedScale()) 451 { 452 for (S32 i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 453 if (!mHandsOffNodes.test(i)) 454 TSTransform::applyScale(smNodeCurrentAlignedScales[i],&smNodeLocalTransforms[i]); 455 } 456 else 457 { 458 for (S32 i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 459 if (!mHandsOffNodes.test(i)) 460 TSTransform::applyScale(smNodeCurrentArbitraryScales[i],&smNodeLocalTransforms[i]); 461 } 462 463 TSIntegerSet scaledNodes; 464 scaledNodes.difference(mHandsOffNodes); 465 smNodeLocalTransformDirty.overlap(scaledNodes); 466} 467 468void TSShapeInstance::handleAnimatedScale(TSThread * thread, S32 a, S32 b, TSIntegerSet & scaleBeenSet) 469{ 470 S32 j=0; 471 S32 start = thread->getSequence()->scaleMatters.start(); 472 S32 end = b; 473 474 // code the scale conversion (might need to "upgrade" from uniform to arbitrary, e.g.) 475 // code uniform, aligned, and arbitrary as 0,1, and 2, respectively, 476 // with sequence coding in first two bits, shape coding in next two bits 477 S32 code = 0; 478 if (thread->getSequence()->animatesAlignedScale()) 479 code += 1; 480 else if (thread->getSequence()->animatesArbitraryScale()) 481 code += 2; 482 if (animatesAlignedScale()) 483 code += 4; 484 if (animatesArbitraryScale()) 485 code += 8; 486 487 F32 uniformScale = 1.0f; 488 Point3F alignedScale(0.0f, 0.0f, 0.0f); 489 TSScale arbitraryScale; 490 for (S32 nodeIndex=start; nodeIndex<end; thread->getSequence()->scaleMatters.next(nodeIndex), j++) 491 { 492 if (nodeIndex<a) 493 continue; 494 495 if (!scaleBeenSet.test(nodeIndex)) 496 { 497 // compute scale in sequence format 498 switch (code) 499 { // Sequence Shape 500 case 0: // uniform -> uniform 501 case 4: // uniform -> aligned 502 case 8: // uniform -> arbitrary 503 { 504 F32 s1 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum1,j); 505 F32 s2 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum2,j); 506 uniformScale = TSTransform::interpolate(s1,s2,thread->keyPos); 507 alignedScale.set(uniformScale,uniformScale,uniformScale); 508 break; 509 } 510 case 5: // aligned -> aligned 511 case 9: // aligned -> arbitrary 512 { 513 const Point3F & s1 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum1,j); 514 const Point3F & s2 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum2,j); 515 TSTransform::interpolate(s1,s2,thread->keyPos,&alignedScale); 516 break; 517 } 518 case 10: // arbitrary -> arbitary 519 { 520 TSScale s1,s2; 521 mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum1,j,&s1); 522 mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum2,j,&s2); 523 TSTransform::interpolate(s1,s2,thread->keyPos,&arbitraryScale); 524 break; 525 } 526 default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break; 527 } 528 529 switch (code) 530 { 531 case 0: // uniform -> uniform 532 { 533 smNodeCurrentUniformScales[nodeIndex] = uniformScale; 534 break; 535 } 536 case 4: // uniform -> aligned 537 case 5: // aligned -> aligned 538 smNodeCurrentAlignedScales[nodeIndex] = alignedScale; 539 break; 540 case 8: // uniform -> arbitrary 541 case 9: // aligned -> arbitrary 542 { 543 smNodeCurrentArbitraryScales[nodeIndex].identity(); 544 smNodeCurrentArbitraryScales[nodeIndex].mScale = alignedScale; 545 break; 546 } 547 case 10: // arbitrary -> arbitary 548 { 549 smNodeCurrentArbitraryScales[nodeIndex] = arbitraryScale; 550 break; 551 } 552 default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break; 553 } 554 smScaleThreads[nodeIndex] = thread; 555 scaleBeenSet.set(nodeIndex); 556 } 557 } 558} 559 560void TSShapeInstance::handleMaskedPositionNode(TSThread * th, S32 nodeIndex, S32 offset) 561{ 562 const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,offset); 563 const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,offset); 564 Point3F p; 565 TSTransform::interpolate(p1,p2,th->keyPos,&p); 566 567 if (!mMaskPosXNodes.test(nodeIndex)) 568 smNodeCurrentTranslations[nodeIndex].x = p.x; 569 570 if (!mMaskPosYNodes.test(nodeIndex)) 571 smNodeCurrentTranslations[nodeIndex].y = p.y; 572 573 if (!mMaskPosZNodes.test(nodeIndex)) 574 smNodeCurrentTranslations[nodeIndex].z = p.z; 575} 576 577void TSShapeInstance::handleBlendSequence(TSThread * thread, S32 a, S32 b) 578{ 579 S32 jrot=0; 580 S32 jtrans=0; 581 S32 jscale=0; 582 583 const TSShape::Sequence* threadSequence = thread->getSequence(); 584 585 TSIntegerSet nodeMatters = threadSequence->translationMatters; 586 nodeMatters.overlap(threadSequence->rotationMatters); 587 nodeMatters.overlap(threadSequence->scaleMatters); 588 nodeMatters.takeAway(mHandsOffNodes); 589 S32 start = nodeMatters.start(); 590 S32 end = b; 591 for (S32 nodeIndex=start; nodeIndex<end; nodeMatters.next(nodeIndex)) 592 { 593 // skip nodes outside of this detail 594 if (start<a || mDisableBlendNodes.test(nodeIndex)) 595 { 596 if (threadSequence->rotationMatters.test(nodeIndex)) 597 jrot++; 598 if (threadSequence->translationMatters.test(nodeIndex)) 599 jtrans++; 600 if (threadSequence->scaleMatters.test(nodeIndex)) 601 jscale++; 602 continue; 603 } 604 605 MatrixF mat(true); 606 if (threadSequence->rotationMatters.test(nodeIndex)) 607 { 608 QuatF q1,q2; 609 mShape->getRotation(*threadSequence,thread->keyNum1,jrot,&q1); 610 mShape->getRotation(*threadSequence,thread->keyNum2,jrot,&q2); 611 QuatF quat; 612 TSTransform::interpolate(q1,q2,thread->keyPos,&quat); 613 TSTransform::setMatrix(quat,&mat); 614 jrot++; 615 } 616 617 if (threadSequence->translationMatters.test(nodeIndex)) 618 { 619 const Point3F & p1 = mShape->getTranslation(*threadSequence,thread->keyNum1,jtrans); 620 const Point3F & p2 = mShape->getTranslation(*threadSequence,thread->keyNum2,jtrans); 621 Point3F p; 622 TSTransform::interpolate(p1,p2,thread->keyPos,&p); 623 mat.setColumn(3,p); 624 jtrans++; 625 } 626 627 if (threadSequence->scaleMatters.test(nodeIndex)) 628 { 629 if (threadSequence->animatesUniformScale()) 630 { 631 F32 s1 = mShape->getUniformScale(*threadSequence,thread->keyNum1,jscale); 632 F32 s2 = mShape->getUniformScale(*threadSequence,thread->keyNum2,jscale); 633 F32 scale = TSTransform::interpolate(s1,s2,thread->keyPos); 634 TSTransform::applyScale(scale,&mat); 635 } 636 else if (animatesAlignedScale()) 637 { 638 Point3F s1 = mShape->getAlignedScale(*threadSequence,thread->keyNum1,jscale); 639 Point3F s2 = mShape->getAlignedScale(*threadSequence,thread->keyNum2,jscale); 640 Point3F scale; 641 TSTransform::interpolate(s1,s2,thread->keyPos,&scale); 642 TSTransform::applyScale(scale,&mat); 643 } 644 else 645 { 646 TSScale s1,s2; 647 mShape->getArbitraryScale(*threadSequence,thread->keyNum1,jscale,&s1); 648 mShape->getArbitraryScale(*threadSequence,thread->keyNum2,jscale,&s2); 649 TSScale scale; 650 TSTransform::interpolate(s1,s2,thread->keyPos,&scale); 651 TSTransform::applyScale(scale,&mat); 652 } 653 jscale++; 654 } 655 656 // apply blend transform 657 smNodeLocalTransforms[nodeIndex].mul(mat); 658 smNodeLocalTransformDirty.set(nodeIndex); 659 } 660} 661 662//------------------------------------------------------------------------------------- 663// Other Animation: 664//------------------------------------------------------------------------------------- 665 666void TSShapeInstance::animateVisibility(S32 ss) 667{ 668 PROFILE_SCOPE( TSShapeInstance_animateVisibility ); 669 670 S32 i; 671 if (!mMeshObjects.size()) 672 return; 673 674 // find out who needs default values set 675 TSIntegerSet beenSet; 676 beenSet.setAll(mMeshObjects.size()); 677 for (i=0; i<mThreadList.size(); i++) 678 beenSet.takeAway(mThreadList[i]->getSequence()->visMatters); 679 680 // set defaults 681 S32 a = mShape->subShapeFirstObject[ss]; 682 S32 b = a + mShape->subShapeNumObjects[ss]; 683 for (i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 684 { 685 if (beenSet.test(i)) 686 mMeshObjects[i].visible = mShape->objectStates[i].vis; 687 } 688 689 // go through each thread and set visibility on those objects that 690 // are not set yet and are controlled by that thread 691 for (i=0; i<mThreadList.size(); i++) 692 { 693 TSThread * th = mThreadList[i]; 694 695 const TSShape::Sequence* threadSequence = th->getSequence(); 696 697 // For better or worse, object states are stored together (frame, 698 // matFrame, visibility all in one structure). Thus, indexing into 699 // object state array for animation for any of these attributes needs to 700 // take into account whether or not the other attributes are also animated. 701 // The object states should eventually be separated (like the node states were) 702 // in order to save memory and save the following step. 703 TSIntegerSet objectMatters = threadSequence->frameMatters; 704 objectMatters.overlap(threadSequence->matFrameMatters); 705 objectMatters.overlap(threadSequence->visMatters); 706 707 // skip to beginning of this sub-shape 708 S32 j=0; 709 S32 start = objectMatters.start(); 710 S32 end = b; 711 for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++) 712 { 713 if (!beenSet.test(objectIndex) && threadSequence->visMatters.test(objectIndex)) 714 { 715 F32 state1 = mShape->getObjectState(*threadSequence,th->keyNum1,j).vis; 716 F32 state2 = mShape->getObjectState(*threadSequence,th->keyNum2,j).vis; 717 if ((state1-state2) * (state1-state2) > 0.99f) 718 // goes from 0 to 1 -- discreet jump 719 mMeshObjects[objectIndex].visible = th->keyPos<0.5f ? state1 : state2; 720 else 721 // interpolate between keyframes when visibility change is gradual 722 mMeshObjects[objectIndex].visible = (1.0f-th->keyPos) * state1 + th->keyPos * state2; 723 724 // record change so that later threads don't over-write us... 725 beenSet.set(objectIndex); 726 } 727 } 728 } 729} 730 731void TSShapeInstance::animateFrame(S32 ss) 732{ 733 PROFILE_SCOPE( TSShapeInstance_animateFrame ); 734 735 S32 i; 736 if (!mMeshObjects.size()) 737 return; 738 739 // find out who needs default values set 740 TSIntegerSet beenSet; 741 beenSet.setAll(mMeshObjects.size()); 742 for (i=0; i<mThreadList.size(); i++) 743 beenSet.takeAway(mThreadList[i]->getSequence()->frameMatters); 744 745 // set defaults 746 S32 a = mShape->subShapeFirstObject[ss]; 747 S32 b = a + mShape->subShapeNumObjects[ss]; 748 for (i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 749 if (beenSet.test(i)) 750 mMeshObjects[i].frame = mShape->objectStates[i].frameIndex; 751 752 // go through each thread and set frame on those objects that 753 // are not set yet and are controlled by that thread 754 for (i=0; i<mThreadList.size(); i++) 755 { 756 TSThread * th = mThreadList[i]; 757 758 const TSShape::Sequence* threadSequence = th->getSequence(); 759 760 // For better or worse, object states are stored together (frame, 761 // matFrame, visibility all in one structure). Thus, indexing into 762 // object state array for animation for any of these attributes needs to 763 // take into account whether or not the other attributes are also animated. 764 // The object states should eventually be separated (like the node states were) 765 // in order to save memory and save the following step. 766 TSIntegerSet objectMatters = threadSequence->frameMatters; 767 objectMatters.overlap(threadSequence->matFrameMatters); 768 objectMatters.overlap(threadSequence->visMatters); 769 770 // skip to beginning of this sub-shape 771 S32 j=0; 772 S32 start = objectMatters.start(); 773 S32 end = b; 774 for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++) 775 { 776 if (!beenSet.test(objectIndex) && threadSequence->frameMatters.test(objectIndex)) 777 { 778 S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2; 779 mMeshObjects[objectIndex].frame = mShape->getObjectState(*threadSequence,key,j).frameIndex; 780 781 // record change so that later threads don't over-write us... 782 beenSet.set(objectIndex); 783 } 784 } 785 } 786} 787 788void TSShapeInstance::animateMatFrame(S32 ss) 789{ 790 PROFILE_SCOPE( TSShapeInstance_animateMatFrame ); 791 792 S32 i; 793 if (!mMeshObjects.size()) 794 return; 795 796 // find out who needs default values set 797 TSIntegerSet beenSet; 798 beenSet.setAll(mMeshObjects.size()); 799 for (i=0; i<mThreadList.size(); i++) 800 beenSet.takeAway(mThreadList[i]->getSequence()->matFrameMatters); 801 802 // set defaults 803 S32 a = mShape->subShapeFirstObject[ss]; 804 S32 b = a + mShape->subShapeNumObjects[ss]; 805 for (i=<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a>; i<b; i++) 806 if (beenSet.test(i)) 807 mMeshObjects[i].matFrame = mShape->objectStates[i].matFrameIndex; 808 809 // go through each thread and set matFrame on those objects that 810 // are not set yet and are controlled by that thread 811 for (i=0; i<mThreadList.size(); i++) 812 { 813 TSThread * th = mThreadList[i]; 814 815 const TSShape::Sequence* threadSequence = th->getSequence(); 816 817 // For better or worse, object states are stored together (frame, 818 // matFrame, visibility all in one structure). Thus, indexing into 819 // object state array for animation for any of these attributes needs to 820 // take into account whether or not the other attributes are also animated. 821 // The object states should eventually be separated (like the node states were) 822 // in order to save memory and save the following step. 823 TSIntegerSet objectMatters = threadSequence->frameMatters; 824 objectMatters.overlap(threadSequence->matFrameMatters); 825 objectMatters.overlap(threadSequence->visMatters); 826 827 // skip to beginining of this sub-shape 828 S32 j=0; 829 S32 start = objectMatters.start(); 830 S32 end = b; 831 for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++) 832 { 833 if (!beenSet.test(objectIndex) && threadSequence->matFrameMatters.test(objectIndex)) 834 { 835 S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2; 836 mMeshObjects[objectIndex].matFrame = mShape->getObjectState(*threadSequence,key,j).matFrameIndex; 837 838 // record change so that later threads don't over-write us... 839 beenSet.set(objectIndex); 840 } 841 } 842 } 843} 844 845//------------------------------------------------------------------------------------- 846// Animate (and initialize detail levels) 847//------------------------------------------------------------------------------------- 848 849void TSShapeInstance::animate(S32 dl) 850{ 851 PROFILE_SCOPE( TSShapeInstance_animate ); 852 853 if (dl==-1) 854 // nothing to do 855 return; 856 857 S32 ss = mShape->details[dl].subShapeNum; 858 859 // this is a billboard detail... 860 if (ss<0) 861 return; 862 863 U32 dirtyFlags = mDirtyFlags[ss]; 864 865 if (dirtyFlags & ThreadDirty) 866 sortThreads(); 867 868 // animate nodes? 869 if (dirtyFlags & TransformDirty) 870 animateNodes(ss); 871 872 // animate objects? 873 if (dirtyFlags & VisDirty) 874 animateVisibility(ss); 875 876 if (dirtyFlags & FrameDirty) 877 animateFrame(ss); 878 879 if (dirtyFlags & MatFrameDirty) 880 animateMatFrame(ss); 881 882 mDirtyFlags[ss] = 0; 883} 884 885void TSShapeInstance::animateNodeSubtrees(bool forceFull) 886{ 887 // animate all the nodes for all the detail levels... 888 889 if (forceFull) 890 // force transforms to animate 891 setDirty(TransformDirty); 892 893 for (S32 i=0; i<mShape->subShapeNumNodes.size(); i++) 894 { 895 if (mDirtyFlags[i] & TransformDirty) 896 { 897 animateNodes(i); 898 mDirtyFlags[i] &= ~<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1a5c635115e4ba7f94af7ebf54eddf58f8a14de86c2c1b55f5273f2396eda15f308">TransformDirty</a>; 899 } 900 } 901} 902 903void TSShapeInstance::animateSubtrees(bool forceFull) 904{ 905 // animate all the subtrees 906 907 if (forceFull) 908 // force full animate 909 setDirty(AllDirtyMask); 910 911 for (S32 i=0; i<mShape->subShapeNumNodes.size(); i++) 912 { 913 if (mDirtyFlags[i] & TransformDirty) 914 { 915 animate(i); 916 mDirtyFlags[i] = 0; 917 } 918 } 919} 920 921void TSShapeInstance::addPath(TSThread *gt, F32 start, F32 end, MatrixF *mat) 922{ 923 // never get here while in transition... 924 AssertFatal(!gt->transitionData.inTransition,"TSShapeInstance::addPath"); 925 926 if (!mat) 927 mat = &mGroundTransform; 928 929 MatrixF startInvM; 930 gt->getGround(start,&startInvM); 931 startInvM.inverse(); 932 933 MatrixF endM; 934 gt->getGround(end,&endM); 935 936 MatrixF addM; 937 addM.mul(startInvM,endM); 938 endM.mul(*mat,addM); 939 *mat = endM; 940} 941 942bool TSShapeInstance::initGround() 943{ 944 for (S32 i=0; i<mThreadList.size(); i++) 945 { 946 TSThread * th = mThreadList[i]; 947 if (!th->transitionData.inTransition && th->getSequence()->numGroundFrames>0) 948 { 949 mGroundThread = th; 950 return true; 951 } 952 } 953 return false; 954} 955 956void TSShapeInstance::animateGround() 957{ 958 mGroundTransform.identity(); 959 960 // pick thread which controlls ground transform 961 // if we haven't already... 962 if (!mGroundThread && !initGround()) 963 return; 964 965 S32 & loop = mGroundThread->path.loop; 966 F32 & start = mGroundThread->path.start; 967 F32 & end = mGroundThread->path.end; 968 969 // accumulate path transform 970 if (loop>0) 971 { 972 addPath(mGroundThread,start,1.0f); 973 while (--loop) 974 addPath(mGroundThread,0.0f,1.0f); 975 addPath(mGroundThread,0.0f,end); 976 } 977 else if (loop<0) 978 { 979 addPath(mGroundThread,start,0.0f); 980 while (++loop) 981 addPath(mGroundThread,1.0f,0.0f); 982 addPath(mGroundThread,1.0f,end); 983 } 984 else 985 addPath(mGroundThread,start,end); 986 start = end; // in case user tries to animateGround twice 987} 988 989void TSShapeInstance::deltaGround(TSThread * thread, F32 start, F32 end, MatrixF * mat) 990{ 991 if (!mat) 992 mat = &mGroundTransform; 993 994 mat->identity(); 995 if (thread->transitionData.inTransition) 996 return; 997 998 F32 invDuration = 1.0f / thread->getDuration(); 999 start *= invDuration; 1000 end *= invDuration; 1001 1002 addPath(thread,start,end,mat); 1003} 1004 1005// Simple case of above- get ground delta at given position in unit range 1006void TSShapeInstance::deltaGround1(TSThread * thread, F32 start, F32 end, MatrixF& mat) 1007{ 1008 mat.identity(); 1009 if (thread->transitionData.inTransition) 1010 return; 1011 addPath(thread, start, end, &mat); 1012} 1013 1014void TSShapeInstance::setTriggerState(U32 stateNum, bool on) 1015{ 1016 AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::setTriggerState: state index out of range"); 1017 1018 stateNum--; // stateNum externally 1..32, internally 0..31 1019 U32 bit = 1 << stateNum; 1020 if (on) 1021 mTriggerStates |= bit; 1022 else 1023 mTriggerStates &= ~bit; 1024} 1025 1026void TSShapeInstance::setTriggerStateBit(U32 stateBit, bool on) 1027{ 1028 if (on) 1029 mTriggerStates |= stateBit; 1030 else 1031 mTriggerStates &= ~stateBit; 1032} 1033 1034bool TSShapeInstance::getTriggerState(U32 stateNum, bool clearState) 1035{ 1036 AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::getTriggerState: state index out of range"); 1037 1038 stateNum--; // stateNum externally 1..32, internally 0..31 1039 U32 bit = 1 << stateNum; 1040 bool ret = ((mTriggerStates & bit)!=0); 1041 if (clearState) 1042 mTriggerStates &= ~bit; 1043 return ret; 1044} 1045 1046void TSShapeInstance::setNodeAnimationState(S32 nodeIndex, U32 animationState, TSCallback * callback) 1047{ 1048 AssertFatal((animationState & ~(MaskNodeAll</a>|<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1ad57ffe66d6e2961b25536ba911f2af1ca6a449dfb45b124cd8a1778b6f5509bce">MaskNodeHandsOff</a>|<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1ad57ffe66d6e2961b25536ba911f2af1ca87ae243de67ef16d4968813882818ea4">MaskNodeCallback)) == 0,"TSShapeInstance::setNodeAnimationState (1)"); 1049 1050 // don't handle callback nodes in this method 1051 if (callback) 1052 animationState |= MaskNodeCallback; 1053 else 1054 animationState &= ~<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1ad57ffe66d6e2961b25536ba911f2af1ca87ae243de67ef16d4968813882818ea4">MaskNodeCallback</a>; 1055 1056 // hands-off takes precedance 1057 if (animationState & MaskNodeHandsOff) 1058 animationState = MaskNodeHandsOff | MaskNodeBlend; 1059 else if (animationState & MaskNodeCallback) 1060 animationState = MaskNodeCallback | MaskNodeBlend; 1061 1062 // if we're not changing anything then get out of here now 1063 if (animationState == getNodeAnimationState(nodeIndex)) 1064 return; 1065 1066 setDirty(AllDirtyMask); 1067 1068 if (animationState & MaskNodeAllButBlend) 1069 { 1070 if (animationState & MaskNodeRotation) 1071 mMaskRotationNodes.set(nodeIndex); 1072 if (animationState & MaskNodePosX) 1073 mMaskPosXNodes.set(nodeIndex); 1074 if (animationState & MaskNodePosY) 1075 mMaskPosYNodes.set(nodeIndex); 1076 if (animationState & MaskNodePosZ) 1077 mMaskPosZNodes.set(nodeIndex); 1078 } 1079 else 1080 { 1081 // no masking clear out all the masking lists 1082 mMaskRotationNodes.clear(nodeIndex); 1083 mMaskPosXNodes.clear(nodeIndex); 1084 mMaskPosYNodes.clear(nodeIndex); 1085 mMaskPosZNodes.clear(nodeIndex); 1086 } 1087 1088 if (animationState & MaskNodeBlend) 1089 mDisableBlendNodes.set(nodeIndex); 1090 else 1091 mDisableBlendNodes.clear(nodeIndex); 1092 1093 if (animationState & MaskNodeHandsOff) 1094 mHandsOffNodes.set(nodeIndex); 1095 else 1096 mHandsOffNodes.clear(nodeIndex); 1097 1098 // clear out of node callbacks 1099 for (S32 i=0; i<mNodeCallbacks.size(); i++) 1100 { 1101 if (mNodeCallbacks[i].nodeIndex == nodeIndex) 1102 { 1103 mNodeCallbacks.erase_fast(i); 1104 break; 1105 } 1106 } 1107 1108 if (animationState & MaskNodeCallback) 1109 { 1110 mCallbackNodes.set(nodeIndex); 1111 mNodeCallbacks.increment(); 1112 mNodeCallbacks.last().callback = callback; 1113 mNodeCallbacks.last().nodeIndex = nodeIndex; 1114 } 1115 else 1116 mCallbackNodes.clear(nodeIndex); 1117} 1118 1119U32 TSShapeInstance::getNodeAnimationState(S32 nodeIndex) 1120{ 1121 U32 ret = 0; 1122 if (mMaskRotationNodes.test(nodeIndex)) 1123 ret |= MaskNodeRotation; 1124 if (mMaskPosXNodes.test(nodeIndex)) 1125 ret |= MaskNodePosX; 1126 if (mMaskPosYNodes.test(nodeIndex)) 1127 ret |= MaskNodePosY; 1128 if (mMaskPosZNodes.test(nodeIndex)) 1129 ret |= MaskNodePosZ; 1130 if (mDisableBlendNodes.test(nodeIndex)) 1131 ret |= MaskNodeBlend; 1132 if (mHandsOffNodes.test(nodeIndex)) 1133 ret |= MaskNodeHandsOff; 1134 if (mCallbackNodes.test(nodeIndex)) 1135 ret |= MaskNodeCallback; 1136 return ret; 1137} 1138