tsAnimate.cpp 38 KB

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