tsThread.cpp

Engine/source/ts/tsThread.cpp

More...

Detailed Description

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "platform/platform.h"
 25#include "ts/tsShapeInstance.h"
 26
 27
 28//-------------------------------------------------------------------------------------
 29// This file contains the shape instance thread class (defined in tsShapeInstance.h)
 30// and the tsShapeInstance functions to interface with the thread class.
 31//-------------------------------------------------------------------------------------
 32
 33//-------------------------------------------------------------------------------------
 34// Thread class
 35//-------------------------------------------------------------------------------------
 36
 37// given a position on the thread, choose correct keyframes
 38// slight difference between one-shot and cyclic sequences -- see comments below for details
 39void TSThread::selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos)
 40{
 41   S32 numKF = seq->numKeyframes;
 42   F32 kf;
 43
 44   if (seq->isCyclic())
 45   {
 46      // cyclic sequence:
 47      // pos=0 and pos=1 are equivalent, so we don't have a keyframe at pos=1
 48      // last keyframe corresponds to pos=n/(n-1) up to (not including) pos=1
 49      // (where n == num keyframes)
 50
 51      AssertFatal(pos>=0.0f && pos<1.0f,"TSThread::selectKeyframes");
 52
 53      kf = pos * (F32) (numKF);
 54
 55      // set keyPos
 56      if (kpos)
 57         *kpos = kf - (S32) kf;
 58
 59      // make sure compiler doing what we want...
 60      AssertFatal(*kpos>=0.0f && *kpos<1.0f,"TSThread::selectKeyframes");
 61
 62      S32 kfIdx1 = (S32) kf;
 63
 64      // following assert could happen if pos1<1 && pos1==1...paradoxically...
 65      AssertFatal(kfIdx1<=seq->numKeyframes,"TSThread::selectKeyframes");
 66
 67      S32 kfIdx2 = (kfIdx1==seq->numKeyframes-1) ? 0 : kfIdx1+1;
 68
 69      if (k1)
 70         *k1 = kfIdx1;
 71      if (k2)
 72         *k2 = kfIdx2;
 73   }
 74   else
 75   {
 76      // one-shot sequence:
 77      // pos=0 and pos=1 are now different, so we have a keyframe at pos=1
 78      // last keyframe corresponds to pos=1
 79      // rest of the keyframes are equally spaced (so 1/(n-1) pos units long)
 80      // (where n == num keyframes)
 81
 82      AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::selectKeyframes");
 83
 84      if (pos==1.0f)
 85      {
 86         if (kpos)
 87            *kpos = 0.0f;
 88         if (k1)
 89            *k1 = seq->numKeyframes-1;
 90         if (k2)
 91            *k2 = seq->numKeyframes-1;
 92      }
 93      else
 94      {
 95         kf = pos * (F32) (numKF-1);
 96
 97         // set keyPos
 98         if (kpos)
 99            *kpos = kf - (S32) kf;
100
101         S32 kfIdx1 = (S32) kf;
102
103         // following assert could happen if pos1<1 && pos1==1...paradoxically...
104         AssertFatal(kfIdx1<seq->numKeyframes,"TSThread::selectKeyFrames: invalid keyframe!");
105
106         S32 kfIdx2 = kfIdx1+1;
107
108         if (k1)
109            *k1 = kfIdx1;
110         if (k2)
111            *k2 = kfIdx2;
112      }
113   }
114}
115
116void TSThread::getGround(F32 t, MatrixF * pMat)
117{
118   static const QuatF unitRotation(0,0,0,1);
119   static const Point3F unitTranslation = Point3F::Zero;
120
121   const QuatF * q1, * q2;
122   QuatF rot1,rot2;
123   const Point3F * p1, * p2;
124
125   // if N = sequence->numGroundFrames, then there are N+1 positions we
126   // interpolate betweeen:  0/N, 1/N ... N/N
127   // we need to convert the p passed to us into 2 ground keyframes:
128
129   // the 0.99999f is in case 'p' is exactly 1.0f, which is legal but 'kf'
130   // needs to be strictly less than 'sequence->numGroundFrames'
131   F32 kf = 0.999999f * t * (F32) getSequence()->numGroundFrames;
132
133   // get frame number and interp param (kpos)
134   S32 frame = (S32)kf;
135   F32 kpos = kf - (F32)frame;
136
137   // now point pT1 and pT2 at transforms for keyframes 'frame' and 'frame+1'
138
139   // following a little strange:  first ground keyframe (0/N in comment above) is
140   // assumed to be ident. and not found in the list.
141   if (frame)
142   {
143      p1 = &mShapeInstance->mShape->groundTranslations[getSequence()->firstGroundFrame + frame - 1];
144      q1 = &mShapeInstance->mShape->groundRotations[getSequence()->firstGroundFrame + frame - 1].getQuatF(&rot1);
145   }
146   else
147   {
148      p1 = &unitTranslation;
149      q1 = &unitRotation;
150   }
151
152   // similar to above, ground keyframe number 'frame+1' is actually offset by 'frame'
153   p2 = &mShapeInstance->mShape->groundTranslations[getSequence()->firstGroundFrame + frame];
154   q2 = &mShapeInstance->mShape->groundRotations[getSequence()->firstGroundFrame + frame].getQuatF(&rot2);
155
156   QuatF q;
157   Point3F p;
158   TSTransform::interpolate(*q1,*q2,kpos,&q);
159   TSTransform::interpolate(*p1,*p2,kpos,&p);
160   TSTransform::setMatrix(q,p,pMat);
161}
162
163void TSThread::setSequence(S32 seq, F32 toPos)
164{
165   const TSShape * shape = mShapeInstance->mShape;
166
167   AssertFatal(shape && shape->sequences.size()>seq && toPos>=0.0f && toPos<=1.0f,
168      "TSThread::setSequence: invalid shape handle, sequence number, or position.");
169
170   mShapeInstance->clearTransition(this);
171
172   sequence = seq;
173   priority = getSequence()->priority;
174   mSeqPos = toPos;
175   makePath = getSequence()->makePath();
176   path.start = path.end = 0;
177   path.loop = 0;
178
179   // 1.0f doesn't exist on cyclic sequences
180   if (mSeqPos>0.9999f && getSequence()->isCyclic())
181      mSeqPos = 0.9999f;
182
183   // select keyframes
184   selectKeyframes(mSeqPos,getSequence(),&keyNum1,&keyNum2,&keyPos);
185}
186
187void TSThread::transitionToSequence(S32 seq, F32 toPos, F32 duration, bool continuePlay)
188{
189   AssertFatal(duration>=0.0f,"TSThread::transitionToSequence: negative duration not allowed");
190
191   // make sure these nodes are smoothly interpolated to new positions...
192   // basically, any node we controlled just prior to transition, or at any stage
193   // of the transition is interpolated.  If we start to transtion from A to B,
194   // but before reaching B we transtion to C, we interpolate all nodes controlled
195   // by A, B, or C to their new position.
196   if (transitionData.inTransition)
197   {
198      transitionData.oldRotationNodes.overlap(getSequence()->rotationMatters);
199      transitionData.oldTranslationNodes.overlap(getSequence()->translationMatters);
200      transitionData.oldScaleNodes.overlap(getSequence()->scaleMatters);
201   }
202   else
203   {
204      transitionData.oldRotationNodes = getSequence()->rotationMatters;
205      transitionData.oldTranslationNodes = getSequence()->translationMatters;
206      transitionData.oldScaleNodes = getSequence()->scaleMatters;
207   }
208
209   // set time characteristics of transition
210   transitionData.oldSequence = sequence;
211   transitionData.oldPos = mSeqPos;
212   transitionData.duration = duration;
213   transitionData.pos = 0.0f;
214   transitionData.direction = timeScale>0.0f ? 1.0f : -1.0f;
215   transitionData.targetScale = continuePlay ? 1.0f : 0.0f;
216
217   // in transition...
218   transitionData.inTransition = true;
219
220   // set target sequence data
221   sequence = seq;
222   priority = getSequence()->priority;
223   mSeqPos = toPos;
224   makePath = getSequence()->makePath();
225   path.start = path.end = 0;
226   path.loop = 0;
227
228   // 1.0f doesn't exist on cyclic sequences
229   if (mSeqPos>0.9999f && getSequence()->isCyclic())
230      mSeqPos = 0.9999f;
231
232   // select keyframes
233   selectKeyframes(mSeqPos,getSequence(),&keyNum1,&keyNum2,&keyPos);
234}
235
236bool TSThread::isInTransition()
237{
238   return transitionData.inTransition;
239}
240
241void TSThread::animateTriggers()
242{
243   if (!getSequence()->numTriggers)
244      return;
245
246   switch (path.loop)
247   {
248      case -1 :
249         activateTriggers(path.start,0);
250         activateTriggers(1,path.end);
251         break;
252      case  0 :
253         activateTriggers(path.start,path.end);
254         break;
255      case  1 :
256         activateTriggers(path.start,1);
257         activateTriggers(0,path.end);
258         break;
259      default:
260      {
261         if (path.loop>0)
262         {
263            activateTriggers(path.end,1);
264            activateTriggers(0,path.end);
265         }
266         else
267         {
268            activateTriggers(path.end,0);
269            activateTriggers(1,path.end);
270         }
271      }
272   }
273}
274
275void TSThread::activateTriggers(F32 a, F32 b)
276{
277   S32 i;
278   const TSShape * shape = mShapeInstance->mShape;
279   S32 firstTrigger = getSequence()->firstTrigger;
280   S32 numTriggers = getSequence()->numTriggers;
281
282   // first find triggers at position a and b
283   // we assume there aren't many triggers, so
284   // search is linear
285   F32 lastPos = -1.0f;
286   S32 aIndex  = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers
287   S32 bIndex  = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers
288   for (i=firstTrigger; i<numTriggers+firstTrigger; i++)
289   {
290      TSShape::Trigger currentTrigger = shape->triggers[i];
291
292      // is a between this trigger and previous one...
293      if (a>lastPos && a <= currentTrigger.pos)
294         aIndex = i;
295      // is b between this trigger and previous one...
296      if (b>lastPos && b <= currentTrigger.pos)
297         bIndex = i;
298      lastPos = currentTrigger.pos;
299   }
300
301   // activate triggers between aIndex and bIndex (depends on direction)
302   if (aIndex<=bIndex)
303   {
304      for (i=aIndex; i<bIndex; i++)
305      {
306         U32 state = shape->triggers[i].state;
307         bool on = (state & TSShape::Trigger::StateOn)!=0;
308         mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on);
309      }
310   }
311   else
312   {
313      for (i=aIndex-1; i>=bIndex; i--)
314      {
315         U32 state = shape->triggers[i].state;
316         bool on = (state & TSShape::Trigger::StateOn)!=0;
317         if (state & TSShape::Trigger::InvertOnReverse)
318            on = !on;
319         mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on);
320      }
321   }
322}
323
324F32 TSThread::getPos()
325{
326   return transitionData.inTransition ? transitionData.pos : mSeqPos;
327}
328
329F32 TSThread::getTime()
330{
331   return transitionData.inTransition ? transitionData.pos * transitionData.duration : mSeqPos * getSequence()->duration;
332}
333
334F32 TSThread::getDuration()
335{
336   return transitionData.inTransition ? transitionData.duration : getSequence()->duration;
337}
338
339F32 TSThread::getScaledDuration()
340{
341   return getDuration() / mFabs(timeScale);
342}
343
344F32 TSThread::getTimeScale()
345{
346   return timeScale;
347}
348
349void TSThread::setTimeScale(F32 ts)
350{
351   timeScale = ts;
352}
353
354void TSThread::advancePos(F32 delta)
355{
356   if (mFabs(delta)>0.00001f)
357   {
358      // make dirty what this thread changes
359      U32 dirtyFlags = getSequence()->dirtyFlags | (transitionData.inTransition ? TSShapeInstance::TransformDirty : 0);
360      for (S32 i=0; i<mShapeInstance->getShape()->subShapeFirstNode.size(); i++)
361         mShapeInstance->mDirtyFlags[i] |= dirtyFlags;
362   }
363
364   if (transitionData.inTransition)
365   {
366      transitionData.pos += transitionData.direction * delta;
367      if (transitionData.pos<0 || transitionData.pos>=1.0f)
368      {
369         mShapeInstance->clearTransition(this);
370         if (transitionData.pos<0.0f)
371            // return to old sequence
372            mShapeInstance->setSequence(this,transitionData.oldSequence,transitionData.oldPos);
373      }
374      // re-adjust delta to be correct time-wise
375      delta *= transitionData.targetScale * transitionData.duration / getSequence()->duration;
376   }
377
378   // even if we are in a transition, keep playing the sequence
379
380   if (makePath)
381   {
382      path.start = mSeqPos;
383     mSeqPos += delta;
384      if (!getSequence()->isCyclic())
385      {
386        mSeqPos = mClampF(mSeqPos, 0.0f, 1.0f);
387         path.loop = 0;
388      }
389      else
390      {
391         path.loop = (S32)mSeqPos;
392         if (mSeqPos < 0.0f)
393            path.loop--;
394       mSeqPos -= path.loop;
395         // following necessary because of floating point roundoff errors
396         if (mSeqPos < 0.0f) mSeqPos += 1.0f;
397         if (mSeqPos >= 1.0f) mSeqPos -= 1.0f;
398      }
399      path.end = mSeqPos;
400
401      animateTriggers(); // do this automatically...no need for user to call it
402
403      AssertFatal(mSeqPos >=0.0f && mSeqPos <=1.0f,"TSThread::advancePos (1)");
404      AssertFatal(!getSequence()->isCyclic() || mSeqPos<1.0f,"TSThread::advancePos (2)");
405   }
406   else
407   {
408      mSeqPos += delta;
409      if (!getSequence()->isCyclic())
410        mSeqPos = mClampF(mSeqPos, 0.0f, 1.0f);
411      else
412      {
413        mSeqPos -= S32(mSeqPos);
414         // following necessary because of floating point roundoff errors
415         if (mSeqPos < 0.0f) mSeqPos += 1.0f;
416         if (mSeqPos >= 1.0f) mSeqPos -= 1.0f;
417      }
418      AssertFatal(mSeqPos >=0.0f && mSeqPos <=1.0f,"TSThread::advancePos (3)");
419      AssertFatal(!getSequence()->isCyclic() || mSeqPos<1.0f,"TSThread::advancePos (4)");
420   }
421
422   // select keyframes
423   selectKeyframes(mSeqPos,getSequence(),&keyNum1,&keyNum2,&keyPos);
424}
425
426void TSThread::advanceTime(F32 delta)
427{
428   advancePos(timeScale * delta / getDuration());
429}
430
431void TSThread::setPos(F32 pos)
432{
433   advancePos(pos-getPos());
434}
435
436void TSThread::setTime(F32 time)
437{
438   setPos(timeScale * time/getDuration());
439}
440
441S32 TSThread::getKeyframeCount()
442{
443   AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeCount: not while in transition");
444
445   return getSequence()->numKeyframes + 1;
446}
447
448S32 TSThread::getKeyframeNumber()
449{
450   AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeNumber: not while in transition");
451
452   return keyNum1;
453}
454
455void TSThread::setKeyframeNumber(S32 kf)
456{
457   AssertFatal(kf>=0 && kf<= getSequence()->numKeyframes,
458      "TSThread::setKeyframeNumber: invalid frame specified.");
459   AssertFatal(!transitionData.inTransition,"TSThread::setKeyframeNumber: not while in transition");
460
461   keyNum1 = keyNum2 = kf;
462   keyPos = 0;
463   mSeqPos = 0;
464}
465
466TSThread::TSThread(TSShapeInstance * _shapeInst)
467{
468   timeScale = 1.0f;
469   mShapeInstance = _shapeInst;
470   transitionData.inTransition = false;
471   blendDisabled = false;
472   setSequence(0,0.0f);
473}
474
475S32 TSThread::operator<(const TSThread & th2) const
476{
477   if (getSequence()->isBlend() == th2.getSequence()->isBlend())
478   {
479      // both blend or neither blend, sort based on priority only -- higher priority first
480      S32 ret = 0; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction
481      if (priority > th2.priority)
482         ret = -1;
483      if (th2.priority > priority)
484         ret = 1;
485      return ret;
486   }
487   else
488   {
489      // one is blend, the other is not...sort based on blend -- non-blended first
490      AssertFatal(!getSequence()->isBlend() || !th2.getSequence()->isBlend(),"compareThreads: unequal 'trues'");
491
492      S32 ret = -1; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction
493      if (getSequence()->isBlend())
494         ret = 1;
495      return ret;
496   }
497}
498
499
500//-------------------------------------------------------------------------------------
501// TSShapeInstance Thread Interface -- more implemented in header file
502//-------------------------------------------------------------------------------------
503
504TSThread * TSShapeInstance::addThread()
505{
506   if (mShape->sequences.empty())
507      return NULL;
508
509   mThreadList.increment();
510   mThreadList.last() = new TSThread(this);
511   setDirty(AllDirtyMask);
512   return mThreadList.last();
513}
514
515TSThread * TSShapeInstance::getThread(S32 threadNumber)
516{
517   AssertFatal(threadNumber < mThreadList.size() && threadNumber>=0,"TSShapeInstance::getThread: threadNumber out of bounds.");
518   return mThreadList[threadNumber];
519}
520
521void TSShapeInstance::destroyThread(TSThread * thread)
522{
523   if (!thread)
524      return;
525
526   clearTransition(thread);
527
528   S32 i;
529   for (i=0; i<mThreadList.size(); i++)
530      if (thread==<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1acfe296d847c5366bdcaa62acec9b69d4">mThreadList</a>[i])
531         break;
532
533   AssertFatal(i<mThreadList.size(),"TSShapeInstance::destroyThread was requested to destroy a thread that this instance doesn't own!");
534
535   delete mThreadList[i];
536   mThreadList.erase(i);
537   setDirty(AllDirtyMask);
538   checkScaleCurrentlyAnimated();
539}
540
541U32 TSShapeInstance::threadCount()
542{
543   return mThreadList.size();
544}
545
546void TSShapeInstance::setSequence(TSThread * thread, S32 seq, F32 pos)
547{
548   if ( (thread->transitionData.inTransition && mTransitionThreads.size()>1) || mTransitionThreads.size()>0)
549   {
550      // if we have transitions, make sure transforms are up to date...
551      sortThreads();
552      animateNodeSubtrees();
553   }
554
555   thread->setSequence(seq,pos);
556   setDirty(AllDirtyMask);
557   mGroundThread = NULL;
558
559   if (mScaleCurrentlyAnimated && !thread->getSequence()->animatesScale())
560      checkScaleCurrentlyAnimated();
561   else if (!mScaleCurrentlyAnimated && thread->getSequence()->animatesScale())
562      mScaleCurrentlyAnimated=true;
563
564   updateTransitions();
565}
566
567U32 TSShapeInstance::getSequence(TSThread * thread)
568{
569   //AssertFatal( thread->sequence >= 0, "TSShapeInstance::getSequence: range error A");
570   //AssertFatal( thread->sequence < mShape->sequences.size(), "TSShapeInstance::getSequence: range error B");
571   return (U32)thread->sequence;
572}
573
574void TSShapeInstance::transitionToSequence(TSThread * thread, S32 seq, F32 pos, F32 duration, bool continuePlay)
575{
576   // make sure all transforms on all detail levels are accurate
577   sortThreads();
578   animateNodeSubtrees();
579
580   thread->transitionToSequence(seq,pos,duration,continuePlay);
581   setDirty(AllDirtyMask);
582   mGroundThread = NULL;
583
584   const TSShape::Sequence* threadSequence = thread->getSequence();
585
586   if (mScaleCurrentlyAnimated && !threadSequence->animatesScale())
587      checkScaleCurrentlyAnimated();
588   else if (!mScaleCurrentlyAnimated && threadSequence->animatesScale())
589      mScaleCurrentlyAnimated=true;
590
591   mTransitionRotationNodes.overlap(thread->transitionData.oldRotationNodes);
592   mTransitionRotationNodes.overlap(threadSequence->rotationMatters);
593
594   mTransitionTranslationNodes.overlap(thread->transitionData.oldTranslationNodes);
595   mTransitionTranslationNodes.overlap(threadSequence->translationMatters);
596
597   mTransitionScaleNodes.overlap(thread->transitionData.oldScaleNodes);
598   mTransitionScaleNodes.overlap(threadSequence->scaleMatters);
599
600   // if we aren't already in the list of transition threads, add us now
601   S32 i;
602   for (i=0; i<mTransitionThreads.size(); i++)
603      if (mTransitionThreads[i]==thread)
604         break;
605   if (i==<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1a41e103db7b3568e3169777ac7cb65cf5">mTransitionThreads</a>.size())
606      mTransitionThreads.push_back(thread);
607
608   updateTransitions();
609}
610
611void TSShapeInstance::clearTransition(TSThread * thread)
612{
613   if (!thread->transitionData.inTransition)
614      return;
615
616   // if other transitions are still playing,
617   // make sure transforms are up to date
618   if (mTransitionThreads.size()>1)
619      animateNodeSubtrees();
620
621   // turn off transition...
622   thread->transitionData.inTransition = false;
623
624   // remove us from transition list
625   S32 i;
626   if (mTransitionThreads.size() != 0) {
627      for (i=0; i<mTransitionThreads.size(); i++)
628    if (mTransitionThreads[i]==thread)
629       break;
630      AssertFatal(i!=<a href="/coding/class/classtsshapeinstance/#classtsshapeinstance_1a41e103db7b3568e3169777ac7cb65cf5">mTransitionThreads</a>.size(),"TSShapeInstance::clearTransition");
631      mTransitionThreads.erase(i);
632   }
633
634   // recompute transitionNodes
635   mTransitionRotationNodes.clearAll();
636   mTransitionTranslationNodes.clearAll();
637   mTransitionScaleNodes.clearAll();
638   for (i=0; i<mTransitionThreads.size(); i++)
639   {
640      mTransitionRotationNodes.overlap(mTransitionThreads[i]->transitionData.oldRotationNodes);
641      mTransitionRotationNodes.overlap(mTransitionThreads[i]->getSequence()->rotationMatters);
642
643      mTransitionTranslationNodes.overlap(mTransitionThreads[i]->transitionData.oldTranslationNodes);
644      mTransitionTranslationNodes.overlap(mTransitionThreads[i]->getSequence()->translationMatters);
645
646      mTransitionScaleNodes.overlap(mTransitionThreads[i]->transitionData.oldScaleNodes);
647      mTransitionScaleNodes.overlap(mTransitionThreads[i]->getSequence()->scaleMatters);
648   }
649
650   setDirty(ThreadDirty);
651
652   updateTransitions();
653}
654
655void TSShapeInstance::updateTransitions()
656{
657   if (mTransitionThreads.empty())
658      return;
659
660   TSIntegerSet transitionNodes;
661   updateTransitionNodeTransforms(transitionNodes);
662
663   S32 i;
664   mNodeReferenceRotations.setSize(mShape->nodes.size());
665   mNodeReferenceTranslations.setSize(mShape->nodes.size());
666   for (i=0; i<mShape->nodes.size(); i++)
667   {
668      if (mTransitionRotationNodes.test(i))
669         mNodeReferenceRotations[i].set(smNodeCurrentRotations[i]);
670      if (mTransitionTranslationNodes.test(i))
671         mNodeReferenceTranslations[i] = smNodeCurrentTranslations[i];
672   }
673
674   if (animatesScale())
675   {
676      // Make sure smNodeXXXScale arrays have been resized
677      TSIntegerSet dummySet;
678      handleDefaultScale(0, 0, dummySet);
679
680      if (animatesUniformScale())
681      {
682         mNodeReferenceUniformScales.setSize(mShape->nodes.size());
683         for (i=0; i<mShape->nodes.size(); i++)
684         {
685            if (mTransitionScaleNodes.test(i))
686               mNodeReferenceUniformScales[i] = smNodeCurrentUniformScales[i];
687         }
688      }
689      else if (animatesAlignedScale())
690      {
691         mNodeReferenceScaleFactors.setSize(mShape->nodes.size());
692         for (i=0; i<mShape->nodes.size(); i++)
693         {
694            if (mTransitionScaleNodes.test(i))
695               mNodeReferenceScaleFactors[i] = smNodeCurrentAlignedScales[i];
696         }
697      }
698      else
699      {
700         mNodeReferenceScaleFactors.setSize(mShape->nodes.size());
701         mNodeReferenceArbitraryScaleRots.setSize(mShape->nodes.size());
702         for (i=0; i<mShape->nodes.size(); i++)
703         {
704            if (mTransitionScaleNodes.test(i))
705            {
706               mNodeReferenceScaleFactors[i] = smNodeCurrentArbitraryScales[i].mScale;
707               mNodeReferenceArbitraryScaleRots[i].set(smNodeCurrentArbitraryScales[i].mRotate);
708            }
709         }
710      }
711   }
712
713   // reset transition durations to account for new reference transforms
714   for (i=0; i<mTransitionThreads.size(); i++)
715   {
716      TSThread * th = mTransitionThreads[i];
717      if (th->transitionData.inTransition)
718      {
719         th->transitionData.duration *= 1.0f - th->transitionData.pos;
720         th->transitionData.pos = 0.0f;
721      }
722   }
723}
724
725void TSShapeInstance::checkScaleCurrentlyAnimated()
726{
727   mScaleCurrentlyAnimated=true;
728   for (S32 i=0; i<mThreadList.size(); i++)
729      if (mThreadList[i]->getSequence()->animatesScale())
730         return;
731   mScaleCurrentlyAnimated=false;
732}
733
734void TSShapeInstance::setBlendEnabled(TSThread * thread, bool blendOn)
735{
736   thread->blendDisabled = !blendOn;
737}
738
739bool TSShapeInstance::getBlendEnabled(TSThread * thread)
740{
741   return !thread->blendDisabled;
742}
743
744void TSShapeInstance::setPriority(TSThread * thread, F32 priority)
745{
746   thread->priority = priority;
747}
748
749F32 TSShapeInstance::getPriority(TSThread * thread)
750{
751   return thread->priority;
752}
753
754F32 TSShapeInstance::getTime(TSThread * thread)
755{
756   return thread->getTime();
757}
758
759F32 TSShapeInstance::getPos(TSThread * thread)
760{
761   return thread->getPos();
762}
763
764void TSShapeInstance::setTime(TSThread * thread, F32 time)
765{
766   thread->setTime(time);
767}
768
769void TSShapeInstance::setPos(TSThread * thread, F32 pos)
770{
771   thread->setPos(pos);
772}
773
774bool TSShapeInstance::isInTransition(TSThread * thread)
775{
776   return thread->isInTransition();
777}
778
779F32 TSShapeInstance::getTimeScale(TSThread * thread)
780{
781   return thread->getTimeScale();
782}
783
784void TSShapeInstance::setTimeScale(TSThread * thread, F32 timeScale)
785{
786   thread->setTimeScale(timeScale);
787}
788
789F32 TSShapeInstance::getDuration(TSThread * thread)
790{
791   return thread->getDuration();
792}
793
794F32 TSShapeInstance::getScaledDuration(TSThread * thread)
795{
796   return thread->getScaledDuration();
797}
798
799S32 TSShapeInstance::getKeyframeCount(TSThread * thread)
800{
801   return thread->getKeyframeCount();
802}
803
804S32 TSShapeInstance::getKeyframeNumber(TSThread * thread)
805{
806   return thread->getKeyframeNumber();
807}
808
809void TSShapeInstance::setKeyframeNumber(TSThread * thread, S32 kf)
810{
811   thread->setKeyframeNumber(kf);
812}
813
814
815// advance time on a particular thread
816void TSShapeInstance::advanceTime(F32 delta, TSThread * thread)
817{
818   thread->advanceTime(delta);
819}
820
821// advance time on all threads
822void TSShapeInstance::advanceTime(F32 delta)
823{
824   for (S32 i=0; i<mThreadList.size(); i++)
825      mThreadList[i]->advanceTime(delta);
826}
827
828// advance pos on a particular thread
829void TSShapeInstance::advancePos(F32 delta, TSThread * thread)
830{
831   thread->advancePos(delta);
832}
833
834// advance pos on all threads
835void TSShapeInstance::advancePos(F32 delta)
836{
837   for (S32 i=0; i<mThreadList.size(); i++)
838      mThreadList[i]->advancePos(delta);
839}
840
841