tsAnimate.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  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. // Decompose transforms for nodes affected by the transition. Only need to do
  263. // for blended or scale-animated nodes, as all others are already up to date
  264. for (S32 i=transitionNodes.start(); i<MAX_TS_SET_SIZE; transitionNodes.next(i))
  265. {
  266. if (smNodeLocalTransformDirty.test(i))
  267. {
  268. if (scaleCurrentlyAnimated())
  269. {
  270. // @todo:No support for scale yet => need to do proper affine decomposition here
  271. smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition();
  272. smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]);
  273. }
  274. else
  275. {
  276. // Scale is identity => can do a cheap decomposition
  277. smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition();
  278. smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]);
  279. }
  280. }
  281. }
  282. }
  283. void TSShapeInstance::handleTransitionNodes(S32 a, S32 b)
  284. {
  285. TSIntegerSet transitionNodes;
  286. updateTransitionNodeTransforms(transitionNodes);
  287. S32 nodeIndex;
  288. S32 start = mTransitionRotationNodes.start();
  289. S32 end = b;
  290. for (nodeIndex=start; nodeIndex<end; mTransitionRotationNodes.next(nodeIndex))
  291. {
  292. if (nodeIndex<a)
  293. continue;
  294. TSThread * thread = smRotationThreads[nodeIndex];
  295. thread = thread && thread->transitionData.inTransition ? thread : NULL;
  296. if (!thread)
  297. {
  298. // if not controlled by a sequence in transition then there must be
  299. // some other thread out there that used to control us that is in
  300. // transition now...use that thread to control interpolation
  301. for (S32 i=0; i<mTransitionThreads.size(); i++)
  302. {
  303. if (mTransitionThreads[i]->transitionData.oldRotationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->rotationMatters.test(nodeIndex))
  304. {
  305. thread = mTransitionThreads[i];
  306. break;
  307. }
  308. }
  309. AssertFatal(thread!=NULL,"TSShapeInstance::handleRotTransitionNodes (rotation)");
  310. }
  311. QuatF tmpQ;
  312. TSTransform::interpolate(mNodeReferenceRotations[nodeIndex].getQuatF(&tmpQ),smNodeCurrentRotations[nodeIndex],thread->transitionData.pos,&smNodeCurrentRotations[nodeIndex]);
  313. }
  314. // then translation
  315. start = mTransitionTranslationNodes.start();
  316. end = b;
  317. for (nodeIndex=start; nodeIndex<end; mTransitionTranslationNodes.next(nodeIndex))
  318. {
  319. TSThread * thread = smTranslationThreads[nodeIndex];
  320. thread = thread && thread->transitionData.inTransition ? thread : NULL;
  321. if (!thread)
  322. {
  323. // if not controlled by a sequence in transition then there must be
  324. // some other thread out there that used to control us that is in
  325. // transition now...use that thread to control interpolation
  326. for (S32 i=0; i<mTransitionThreads.size(); i++)
  327. {
  328. if (mTransitionThreads[i]->transitionData.oldTranslationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->translationMatters.test(nodeIndex))
  329. {
  330. thread = mTransitionThreads[i];
  331. break;
  332. }
  333. }
  334. AssertFatal(thread!=NULL,"TSShapeInstance::handleTransitionNodes (translation).");
  335. }
  336. Point3F & p = smNodeCurrentTranslations[nodeIndex];
  337. Point3F & p1 = mNodeReferenceTranslations[nodeIndex];
  338. Point3F & p2 = p;
  339. F32 k = thread->transitionData.pos;
  340. p.x = p1.x + k * (p2.x-p1.x);
  341. p.y = p1.y + k * (p2.y-p1.y);
  342. p.z = p1.z + k * (p2.z-p1.z);
  343. }
  344. // then scale...
  345. if (scaleCurrentlyAnimated())
  346. {
  347. start = mTransitionScaleNodes.start();
  348. end = b;
  349. for (nodeIndex=start; nodeIndex<end; mTransitionScaleNodes.next(nodeIndex))
  350. {
  351. TSThread * thread = smScaleThreads[nodeIndex];
  352. thread = thread && thread->transitionData.inTransition ? thread : NULL;
  353. if (!thread)
  354. {
  355. // if not controlled by a sequence in transition then there must be
  356. // some other thread out there that used to control us that is in
  357. // transition now...use that thread to control interpolation
  358. for (S32 i=0; i<mTransitionThreads.size(); i++)
  359. {
  360. if (mTransitionThreads[i]->transitionData.oldScaleNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->scaleMatters.test(nodeIndex))
  361. {
  362. thread = mTransitionThreads[i];
  363. break;
  364. }
  365. }
  366. AssertFatal(thread!=NULL,"TSShapeInstance::handleTransitionNodes (scale).");
  367. }
  368. if (animatesUniformScale())
  369. smNodeCurrentUniformScales[nodeIndex] += thread->transitionData.pos * (mNodeReferenceUniformScales[nodeIndex]-smNodeCurrentUniformScales[nodeIndex]);
  370. else if (animatesAlignedScale())
  371. TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentAlignedScales[nodeIndex],thread->transitionData.pos,&smNodeCurrentAlignedScales[nodeIndex]);
  372. else
  373. {
  374. QuatF q;
  375. TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentArbitraryScales[nodeIndex].mScale,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mScale);
  376. TSTransform::interpolate(mNodeReferenceArbitraryScaleRots[nodeIndex].getQuatF(&q),smNodeCurrentArbitraryScales[nodeIndex].mRotate,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mRotate);
  377. }
  378. }
  379. }
  380. // update transforms for transition nodes
  381. start = transitionNodes.start();
  382. end = b;
  383. for (nodeIndex=start; nodeIndex<end; transitionNodes.next(nodeIndex))
  384. {
  385. TSTransform::setMatrix(smNodeCurrentRotations[nodeIndex], smNodeCurrentTranslations[nodeIndex], &smNodeLocalTransforms[nodeIndex]);
  386. if (scaleCurrentlyAnimated())
  387. {
  388. if (animatesUniformScale())
  389. TSTransform::applyScale(smNodeCurrentUniformScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]);
  390. else if (animatesAlignedScale())
  391. TSTransform::applyScale(smNodeCurrentAlignedScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]);
  392. else
  393. TSTransform::applyScale(smNodeCurrentArbitraryScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]);
  394. }
  395. }
  396. }
  397. void TSShapeInstance::handleNodeScale(S32 a, S32 b)
  398. {
  399. if (animatesUniformScale())
  400. {
  401. for (S32 i=a; i<b; i++)
  402. if (!mHandsOffNodes.test(i))
  403. TSTransform::applyScale(smNodeCurrentUniformScales[i],&smNodeLocalTransforms[i]);
  404. }
  405. else if (animatesAlignedScale())
  406. {
  407. for (S32 i=a; i<b; i++)
  408. if (!mHandsOffNodes.test(i))
  409. TSTransform::applyScale(smNodeCurrentAlignedScales[i],&smNodeLocalTransforms[i]);
  410. }
  411. else
  412. {
  413. for (S32 i=a; i<b; i++)
  414. if (!mHandsOffNodes.test(i))
  415. TSTransform::applyScale(smNodeCurrentArbitraryScales[i],&smNodeLocalTransforms[i]);
  416. }
  417. TSIntegerSet scaledNodes;
  418. scaledNodes.difference(mHandsOffNodes);
  419. smNodeLocalTransformDirty.overlap(scaledNodes);
  420. }
  421. void TSShapeInstance::handleAnimatedScale(TSThread * thread, S32 a, S32 b, TSIntegerSet & scaleBeenSet)
  422. {
  423. S32 j=0;
  424. S32 start = thread->getSequence()->scaleMatters.start();
  425. S32 end = b;
  426. // code the scale conversion (might need to "upgrade" from uniform to arbitrary, e.g.)
  427. // code uniform, aligned, and arbitrary as 0,1, and 2, respectively,
  428. // with sequence coding in first two bits, shape coding in next two bits
  429. S32 code = 0;
  430. if (thread->getSequence()->animatesAlignedScale())
  431. code += 1;
  432. else if (thread->getSequence()->animatesArbitraryScale())
  433. code += 2;
  434. if (animatesAlignedScale())
  435. code += 4;
  436. if (animatesArbitraryScale())
  437. code += 8;
  438. F32 uniformScale = 1.0f;
  439. Point3F alignedScale(0.0f, 0.0f, 0.0f);
  440. TSScale arbitraryScale;
  441. for (S32 nodeIndex=start; nodeIndex<end; thread->getSequence()->scaleMatters.next(nodeIndex), j++)
  442. {
  443. if (nodeIndex<a)
  444. continue;
  445. if (!scaleBeenSet.test(nodeIndex))
  446. {
  447. // compute scale in sequence format
  448. switch (code)
  449. { // Sequence Shape
  450. case 0: // uniform -> uniform
  451. case 4: // uniform -> aligned
  452. case 8: // uniform -> arbitrary
  453. {
  454. F32 s1 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum1,j);
  455. F32 s2 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum2,j);
  456. uniformScale = TSTransform::interpolate(s1,s2,thread->keyPos);
  457. alignedScale.set(uniformScale,uniformScale,uniformScale);
  458. break;
  459. }
  460. case 5: // aligned -> aligned
  461. case 9: // aligned -> arbitrary
  462. {
  463. const Point3F & s1 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum1,j);
  464. const Point3F & s2 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum2,j);
  465. TSTransform::interpolate(s1,s2,thread->keyPos,&alignedScale);
  466. break;
  467. }
  468. case 10: // arbitrary -> arbitary
  469. {
  470. TSScale s1,s2;
  471. mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum1,j,&s1);
  472. mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum2,j,&s2);
  473. TSTransform::interpolate(s1,s2,thread->keyPos,&arbitraryScale);
  474. break;
  475. }
  476. default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break;
  477. }
  478. switch (code)
  479. {
  480. case 0: // uniform -> uniform
  481. {
  482. smNodeCurrentUniformScales[nodeIndex] = uniformScale;
  483. break;
  484. }
  485. case 4: // uniform -> aligned
  486. case 5: // aligned -> aligned
  487. smNodeCurrentAlignedScales[nodeIndex] = alignedScale;
  488. break;
  489. case 8: // uniform -> arbitrary
  490. case 9: // aligned -> arbitrary
  491. {
  492. smNodeCurrentArbitraryScales[nodeIndex].identity();
  493. smNodeCurrentArbitraryScales[nodeIndex].mScale = alignedScale;
  494. break;
  495. }
  496. case 10: // arbitrary -> arbitary
  497. {
  498. smNodeCurrentArbitraryScales[nodeIndex] = arbitraryScale;
  499. break;
  500. }
  501. default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break;
  502. }
  503. smScaleThreads[nodeIndex] = thread;
  504. scaleBeenSet.set(nodeIndex);
  505. }
  506. }
  507. }
  508. void TSShapeInstance::handleMaskedPositionNode(TSThread * th, S32 nodeIndex, S32 offset)
  509. {
  510. const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,offset);
  511. const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,offset);
  512. Point3F p;
  513. TSTransform::interpolate(p1,p2,th->keyPos,&p);
  514. if (!mMaskPosXNodes.test(nodeIndex))
  515. smNodeCurrentTranslations[nodeIndex].x = p.x;
  516. if (!mMaskPosYNodes.test(nodeIndex))
  517. smNodeCurrentTranslations[nodeIndex].y = p.y;
  518. if (!mMaskPosZNodes.test(nodeIndex))
  519. smNodeCurrentTranslations[nodeIndex].z = p.z;
  520. }
  521. void TSShapeInstance::handleBlendSequence(TSThread * thread, S32 a, S32 b)
  522. {
  523. S32 jrot=0;
  524. S32 jtrans=0;
  525. S32 jscale=0;
  526. TSIntegerSet nodeMatters = thread->getSequence()->translationMatters;
  527. nodeMatters.overlap(thread->getSequence()->rotationMatters);
  528. nodeMatters.overlap(thread->getSequence()->scaleMatters);
  529. S32 start = nodeMatters.start();
  530. S32 end = b;
  531. for (S32 nodeIndex=start; nodeIndex<end; nodeMatters.next(nodeIndex))
  532. {
  533. // skip nodes outside of this detail
  534. if (start<a || mDisableBlendNodes.test(nodeIndex))
  535. {
  536. if (thread->getSequence()->rotationMatters.test(nodeIndex))
  537. jrot++;
  538. if (thread->getSequence()->translationMatters.test(nodeIndex))
  539. jtrans++;
  540. if (thread->getSequence()->scaleMatters.test(nodeIndex))
  541. jscale++;
  542. continue;
  543. }
  544. MatrixF mat(true);
  545. if (thread->getSequence()->rotationMatters.test(nodeIndex))
  546. {
  547. QuatF q1,q2;
  548. mShape->getRotation(*thread->getSequence(),thread->keyNum1,jrot,&q1);
  549. mShape->getRotation(*thread->getSequence(),thread->keyNum2,jrot,&q2);
  550. QuatF quat;
  551. TSTransform::interpolate(q1,q2,thread->keyPos,&quat);
  552. TSTransform::setMatrix(quat,&mat);
  553. jrot++;
  554. }
  555. if (thread->getSequence()->translationMatters.test(nodeIndex))
  556. {
  557. const Point3F & p1 = mShape->getTranslation(*thread->getSequence(),thread->keyNum1,jtrans);
  558. const Point3F & p2 = mShape->getTranslation(*thread->getSequence(),thread->keyNum2,jtrans);
  559. Point3F p;
  560. TSTransform::interpolate(p1,p2,thread->keyPos,&p);
  561. mat.setColumn(3,p);
  562. jtrans++;
  563. }
  564. if (thread->getSequence()->scaleMatters.test(nodeIndex))
  565. {
  566. if (thread->getSequence()->animatesUniformScale())
  567. {
  568. F32 s1 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum1,jscale);
  569. F32 s2 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum2,jscale);
  570. F32 scale = TSTransform::interpolate(s1,s2,thread->keyPos);
  571. TSTransform::applyScale(scale,&mat);
  572. }
  573. else if (animatesAlignedScale())
  574. {
  575. Point3F s1 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum1,jscale);
  576. Point3F s2 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum2,jscale);
  577. Point3F scale;
  578. TSTransform::interpolate(s1,s2,thread->keyPos,&scale);
  579. TSTransform::applyScale(scale,&mat);
  580. }
  581. else
  582. {
  583. TSScale s1,s2;
  584. mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum1,jscale,&s1);
  585. mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum2,jscale,&s2);
  586. TSScale scale;
  587. TSTransform::interpolate(s1,s2,thread->keyPos,&scale);
  588. TSTransform::applyScale(scale,&mat);
  589. }
  590. jscale++;
  591. }
  592. // apply blend transform
  593. smNodeLocalTransforms[nodeIndex].mul(mat);
  594. smNodeLocalTransformDirty.set(nodeIndex);
  595. }
  596. }
  597. //-------------------------------------------------------------------------------------
  598. // Other Animation:
  599. //-------------------------------------------------------------------------------------
  600. void TSShapeInstance::animateVisibility(S32 ss)
  601. {
  602. PROFILE_SCOPE( TSShapeInstance_animateVisibility );
  603. S32 i;
  604. if (!mMeshObjects.size())
  605. return;
  606. // find out who needs default values set
  607. TSIntegerSet beenSet;
  608. beenSet.setAll(mMeshObjects.size());
  609. for (i=0; i<mThreadList.size(); i++)
  610. beenSet.takeAway(mThreadList[i]->getSequence()->visMatters);
  611. // set defaults
  612. S32 a = mShape->subShapeFirstObject[ss];
  613. S32 b = a + mShape->subShapeNumObjects[ss];
  614. for (i=a; i<b; i++)
  615. {
  616. if (beenSet.test(i))
  617. mMeshObjects[i].visible = mShape->objectStates[i].vis;
  618. }
  619. // go through each thread and set visibility on those objects that
  620. // are not set yet and are controlled by that thread
  621. for (i=0; i<mThreadList.size(); i++)
  622. {
  623. TSThread * th = mThreadList[i];
  624. // For better or worse, object states are stored together (frame,
  625. // matFrame, visibility all in one structure). Thus, indexing into
  626. // object state array for animation for any of these attributes needs to
  627. // take into account whether or not the other attributes are also animated.
  628. // The object states should eventually be separated (like the node states were)
  629. // in order to save memory and save the following step.
  630. TSIntegerSet objectMatters = th->getSequence()->frameMatters;
  631. objectMatters.overlap(th->getSequence()->matFrameMatters);
  632. objectMatters.overlap(th->getSequence()->visMatters);
  633. // skip to beginning of this sub-shape
  634. S32 j=0;
  635. S32 start = objectMatters.start();
  636. S32 end = b;
  637. for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
  638. {
  639. if (!beenSet.test(objectIndex) && th->getSequence()->visMatters.test(objectIndex))
  640. {
  641. F32 state1 = mShape->getObjectState(*th->getSequence(),th->keyNum1,j).vis;
  642. F32 state2 = mShape->getObjectState(*th->getSequence(),th->keyNum2,j).vis;
  643. if ((state1-state2) * (state1-state2) > 0.99f)
  644. // goes from 0 to 1 -- discreet jump
  645. mMeshObjects[objectIndex].visible = th->keyPos<0.5f ? state1 : state2;
  646. else
  647. // interpolate between keyframes when visibility change is gradual
  648. mMeshObjects[objectIndex].visible = (1.0f-th->keyPos) * state1 + th->keyPos * state2;
  649. // record change so that later threads don't over-write us...
  650. beenSet.set(objectIndex);
  651. }
  652. }
  653. }
  654. }
  655. void TSShapeInstance::animateFrame(S32 ss)
  656. {
  657. PROFILE_SCOPE( TSShapeInstance_animateFrame );
  658. S32 i;
  659. if (!mMeshObjects.size())
  660. return;
  661. // find out who needs default values set
  662. TSIntegerSet beenSet;
  663. beenSet.setAll(mMeshObjects.size());
  664. for (i=0; i<mThreadList.size(); i++)
  665. beenSet.takeAway(mThreadList[i]->getSequence()->frameMatters);
  666. // set defaults
  667. S32 a = mShape->subShapeFirstObject[ss];
  668. S32 b = a + mShape->subShapeNumObjects[ss];
  669. for (i=a; i<b; i++)
  670. if (beenSet.test(i))
  671. mMeshObjects[i].frame = mShape->objectStates[i].frameIndex;
  672. // go through each thread and set frame on those objects that
  673. // are not set yet and are controlled by that thread
  674. for (i=0; i<mThreadList.size(); i++)
  675. {
  676. TSThread * th = mThreadList[i];
  677. // For better or worse, object states are stored together (frame,
  678. // matFrame, visibility all in one structure). Thus, indexing into
  679. // object state array for animation for any of these attributes needs to
  680. // take into account whether or not the other attributes are also animated.
  681. // The object states should eventually be separated (like the node states were)
  682. // in order to save memory and save the following step.
  683. TSIntegerSet objectMatters = th->getSequence()->frameMatters;
  684. objectMatters.overlap(th->getSequence()->matFrameMatters);
  685. objectMatters.overlap(th->getSequence()->visMatters);
  686. // skip to beginning of this sub-shape
  687. S32 j=0;
  688. S32 start = objectMatters.start();
  689. S32 end = b;
  690. for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
  691. {
  692. if (!beenSet.test(objectIndex) && th->getSequence()->frameMatters.test(objectIndex))
  693. {
  694. S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2;
  695. mMeshObjects[objectIndex].frame = mShape->getObjectState(*th->getSequence(),key,j).frameIndex;
  696. // record change so that later threads don't over-write us...
  697. beenSet.set(objectIndex);
  698. }
  699. }
  700. }
  701. }
  702. void TSShapeInstance::animateMatFrame(S32 ss)
  703. {
  704. PROFILE_SCOPE( TSShapeInstance_animateMatFrame );
  705. S32 i;
  706. if (!mMeshObjects.size())
  707. return;
  708. // find out who needs default values set
  709. TSIntegerSet beenSet;
  710. beenSet.setAll(mMeshObjects.size());
  711. for (i=0; i<mThreadList.size(); i++)
  712. beenSet.takeAway(mThreadList[i]->getSequence()->matFrameMatters);
  713. // set defaults
  714. S32 a = mShape->subShapeFirstObject[ss];
  715. S32 b = a + mShape->subShapeNumObjects[ss];
  716. for (i=a; i<b; i++)
  717. if (beenSet.test(i))
  718. mMeshObjects[i].matFrame = mShape->objectStates[i].matFrameIndex;
  719. // go through each thread and set matFrame on those objects that
  720. // are not set yet and are controlled by that thread
  721. for (i=0; i<mThreadList.size(); i++)
  722. {
  723. TSThread * th = mThreadList[i];
  724. // For better or worse, object states are stored together (frame,
  725. // matFrame, visibility all in one structure). Thus, indexing into
  726. // object state array for animation for any of these attributes needs to
  727. // take into account whether or not the other attributes are also animated.
  728. // The object states should eventually be separated (like the node states were)
  729. // in order to save memory and save the following step.
  730. TSIntegerSet objectMatters = th->getSequence()->frameMatters;
  731. objectMatters.overlap(th->getSequence()->matFrameMatters);
  732. objectMatters.overlap(th->getSequence()->visMatters);
  733. // skip to beginining of this sub-shape
  734. S32 j=0;
  735. S32 start = objectMatters.start();
  736. S32 end = b;
  737. for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
  738. {
  739. if (!beenSet.test(objectIndex) && th->getSequence()->matFrameMatters.test(objectIndex))
  740. {
  741. S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2;
  742. mMeshObjects[objectIndex].matFrame = mShape->getObjectState(*th->getSequence(),key,j).matFrameIndex;
  743. // record change so that later threads don't over-write us...
  744. beenSet.set(objectIndex);
  745. }
  746. }
  747. }
  748. }
  749. //-------------------------------------------------------------------------------------
  750. // Animate (and initialize detail levels)
  751. //-------------------------------------------------------------------------------------
  752. void TSShapeInstance::animate(S32 dl)
  753. {
  754. PROFILE_SCOPE( TSShapeInstance_animate );
  755. if (dl==-1)
  756. // nothing to do
  757. return;
  758. S32 ss = mShape->details[dl].subShapeNum;
  759. // this is a billboard detail...
  760. if (ss<0)
  761. return;
  762. U32 dirtyFlags = mDirtyFlags[ss];
  763. if (dirtyFlags & ThreadDirty)
  764. sortThreads();
  765. // animate nodes?
  766. if (dirtyFlags & TransformDirty)
  767. animateNodes(ss);
  768. // animate objects?
  769. if (dirtyFlags & VisDirty)
  770. animateVisibility(ss);
  771. if (dirtyFlags & FrameDirty)
  772. animateFrame(ss);
  773. if (dirtyFlags & MatFrameDirty)
  774. animateMatFrame(ss);
  775. mDirtyFlags[ss] = 0;
  776. }
  777. void TSShapeInstance::animateNodeSubtrees(bool forceFull)
  778. {
  779. // animate all the nodes for all the detail levels...
  780. if (forceFull)
  781. // force transforms to animate
  782. setDirty(TransformDirty);
  783. for (S32 i=0; i<mShape->subShapeNumNodes.size(); i++)
  784. {
  785. if (mDirtyFlags[i] & TransformDirty)
  786. {
  787. animateNodes(i);
  788. mDirtyFlags[i] &= ~TransformDirty;
  789. }
  790. }
  791. }
  792. void TSShapeInstance::animateSubtrees(bool forceFull)
  793. {
  794. // animate all the subtrees
  795. if (forceFull)
  796. // force full animate
  797. setDirty(AllDirtyMask);
  798. for (S32 i=0; i<mShape->subShapeNumNodes.size(); i++)
  799. {
  800. if (mDirtyFlags[i] & TransformDirty)
  801. {
  802. animate(i);
  803. mDirtyFlags[i] = 0;
  804. }
  805. }
  806. }
  807. void TSShapeInstance::addPath(TSThread *gt, F32 start, F32 end, MatrixF *mat)
  808. {
  809. // never get here while in transition...
  810. AssertFatal(!gt->transitionData.inTransition,"TSShapeInstance::addPath");
  811. if (!mat)
  812. mat = &mGroundTransform;
  813. MatrixF startInvM;
  814. gt->getGround(start,&startInvM);
  815. startInvM.inverse();
  816. MatrixF endM;
  817. gt->getGround(end,&endM);
  818. MatrixF addM;
  819. addM.mul(startInvM,endM);
  820. endM.mul(*mat,addM);
  821. *mat = endM;
  822. }
  823. bool TSShapeInstance::initGround()
  824. {
  825. for (S32 i=0; i<mThreadList.size(); i++)
  826. {
  827. TSThread * th = mThreadList[i];
  828. if (!th->transitionData.inTransition && th->getSequence()->numGroundFrames>0)
  829. {
  830. mGroundThread = th;
  831. return true;
  832. }
  833. }
  834. return false;
  835. }
  836. void TSShapeInstance::animateGround()
  837. {
  838. mGroundTransform.identity();
  839. // pick thread which controlls ground transform
  840. // if we haven't already...
  841. if (!mGroundThread && !initGround())
  842. return;
  843. S32 & loop = mGroundThread->path.loop;
  844. F32 & start = mGroundThread->path.start;
  845. F32 & end = mGroundThread->path.end;
  846. // accumulate path transform
  847. if (loop>0)
  848. {
  849. addPath(mGroundThread,start,1.0f);
  850. while (--loop)
  851. addPath(mGroundThread,0.0f,1.0f);
  852. addPath(mGroundThread,0.0f,end);
  853. }
  854. else if (loop<0)
  855. {
  856. addPath(mGroundThread,start,0.0f);
  857. while (++loop)
  858. addPath(mGroundThread,1.0f,0.0f);
  859. addPath(mGroundThread,1.0f,end);
  860. }
  861. else
  862. addPath(mGroundThread,start,end);
  863. start = end; // in case user tries to animateGround twice
  864. }
  865. void TSShapeInstance::deltaGround(TSThread * thread, F32 start, F32 end, MatrixF * mat)
  866. {
  867. if (!mat)
  868. mat = &mGroundTransform;
  869. mat->identity();
  870. if (thread->transitionData.inTransition)
  871. return;
  872. F32 invDuration = 1.0f / thread->getDuration();
  873. start *= invDuration;
  874. end *= invDuration;
  875. addPath(thread,start,end,mat);
  876. }
  877. // Simple case of above- get ground delta at given position in unit range
  878. void TSShapeInstance::deltaGround1(TSThread * thread, F32 start, F32 end, MatrixF& mat)
  879. {
  880. mat.identity();
  881. if (thread->transitionData.inTransition)
  882. return;
  883. addPath(thread, start, end, &mat);
  884. }
  885. void TSShapeInstance::setTriggerState(U32 stateNum, bool on)
  886. {
  887. AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::setTriggerState: state index out of range");
  888. stateNum--; // stateNum externally 1..32, internally 0..31
  889. U32 bit = 1 << stateNum;
  890. if (on)
  891. mTriggerStates |= bit;
  892. else
  893. mTriggerStates &= ~bit;
  894. }
  895. void TSShapeInstance::setTriggerStateBit(U32 stateBit, bool on)
  896. {
  897. if (on)
  898. mTriggerStates |= stateBit;
  899. else
  900. mTriggerStates &= ~stateBit;
  901. }
  902. bool TSShapeInstance::getTriggerState(U32 stateNum, bool clearState)
  903. {
  904. AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::getTriggerState: state index out of range");
  905. stateNum--; // stateNum externally 1..32, internally 0..31
  906. U32 bit = 1 << stateNum;
  907. bool ret = ((mTriggerStates & bit)!=0);
  908. if (clearState)
  909. mTriggerStates &= ~bit;
  910. return ret;
  911. }
  912. void TSShapeInstance::setNodeAnimationState(S32 nodeIndex, U32 animationState, TSCallback * callback)
  913. {
  914. AssertFatal((animationState & ~(MaskNodeAll|MaskNodeHandsOff|MaskNodeCallback)) == 0,"TSShapeInstance::setNodeAnimationState (1)");
  915. // don't handle callback nodes in this method
  916. if (callback)
  917. animationState |= MaskNodeCallback;
  918. else
  919. animationState &= ~MaskNodeCallback;
  920. // hands-off takes precedance
  921. if (animationState & MaskNodeHandsOff)
  922. animationState = MaskNodeHandsOff | MaskNodeBlend;
  923. else if (animationState & MaskNodeCallback)
  924. animationState = MaskNodeCallback | MaskNodeBlend;
  925. // if we're not changing anything then get out of here now
  926. if (animationState == getNodeAnimationState(nodeIndex))
  927. return;
  928. setDirty(AllDirtyMask);
  929. if (animationState & MaskNodeAllButBlend)
  930. {
  931. if (animationState & MaskNodeRotation)
  932. mMaskRotationNodes.set(nodeIndex);
  933. if (animationState & MaskNodePosX)
  934. mMaskPosXNodes.set(nodeIndex);
  935. if (animationState & MaskNodePosY)
  936. mMaskPosYNodes.set(nodeIndex);
  937. if (animationState & MaskNodePosZ)
  938. mMaskPosZNodes.set(nodeIndex);
  939. }
  940. else
  941. {
  942. // no masking clear out all the masking lists
  943. mMaskRotationNodes.clear(nodeIndex);
  944. mMaskPosXNodes.clear(nodeIndex);
  945. mMaskPosYNodes.clear(nodeIndex);
  946. mMaskPosZNodes.clear(nodeIndex);
  947. }
  948. if (animationState & MaskNodeBlend)
  949. mDisableBlendNodes.set(nodeIndex);
  950. else
  951. mDisableBlendNodes.clear(nodeIndex);
  952. if (animationState & MaskNodeHandsOff)
  953. mHandsOffNodes.set(nodeIndex);
  954. else
  955. mHandsOffNodes.clear(nodeIndex);
  956. // clear out of node callbacks
  957. for (S32 i=0; i<mNodeCallbacks.size(); i++)
  958. {
  959. if (mNodeCallbacks[i].nodeIndex == nodeIndex)
  960. {
  961. mNodeCallbacks.erase_fast(i);
  962. break;
  963. }
  964. }
  965. if (animationState & MaskNodeCallback)
  966. {
  967. mCallbackNodes.set(nodeIndex);
  968. mNodeCallbacks.increment();
  969. mNodeCallbacks.last().callback = callback;
  970. mNodeCallbacks.last().nodeIndex = nodeIndex;
  971. }
  972. else
  973. mCallbackNodes.clear(nodeIndex);
  974. }
  975. U32 TSShapeInstance::getNodeAnimationState(S32 nodeIndex)
  976. {
  977. U32 ret = 0;
  978. if (mMaskRotationNodes.test(nodeIndex))
  979. ret |= MaskNodeRotation;
  980. if (mMaskPosXNodes.test(nodeIndex))
  981. ret |= MaskNodePosX;
  982. if (mMaskPosYNodes.test(nodeIndex))
  983. ret |= MaskNodePosY;
  984. if (mMaskPosZNodes.test(nodeIndex))
  985. ret |= MaskNodePosZ;
  986. if (mDisableBlendNodes.test(nodeIndex))
  987. ret |= MaskNodeBlend;
  988. if (mHandsOffNodes.test(nodeIndex))
  989. ret |= MaskNodeHandsOff;
  990. if (mCallbackNodes.test(nodeIndex))
  991. ret |= MaskNodeCallback;
  992. return ret;
  993. }