tsAnimate.cpp 38 KB

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