12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "ts/tsShapeInstance.h"
- //----------------------------------------------------------------------------------
- // some utility functions
- //-------------------------------------------------------------------------------------
- S32 QSORT_CALLBACK FN_CDECL compareThreads( const void* e1, const void* e2)
- {
- const TSThread * th1 = *(const TSThread**)e1;
- const TSThread * th2 = *(const TSThread**)e2;
- return (*th1 < *th2);
- }
- void TSShapeInstance::sortThreads()
- {
- PROFILE_SCOPE( TSShapeInstance_sortThreads );
- dQsort(mThreadList.address(),mThreadList.size(),sizeof(TSThread*),compareThreads);
- dQsort(mTransitionThreads.address(),mTransitionThreads.size(),sizeof(TSThread*),compareThreads);
- }
- void TSShapeInstance::setDirty(U32 dirty)
- {
- AssertFatal((dirty & AllDirtyMask) == dirty,"TSShapeInstance::setDirty: illegal dirty flags");
- for (S32 i=0; i<mShape->subShapeFirstNode.size(); i++)
- mDirtyFlags[i] |= dirty;
- }
- void TSShapeInstance::clearDirty(U32 dirty)
- {
- AssertFatal((dirty & AllDirtyMask) == dirty,"TSShapeInstance::clearDirty: illegal dirty flags");
- for (S32 i=0; i<mShape->subShapeFirstNode.size(); i++)
- mDirtyFlags[i] &= ~dirty;
- }
- //-------------------------------------------------------------------------------------
- // Animate nodes
- //-------------------------------------------------------------------------------------
- void TSShapeInstance::animateNodes(S32 ss)
- {
- PROFILE_SCOPE( TSShapeInstance_animateNodes );
- if (!mShape->nodes.size())
- return;
- // @todo: When a node is added, we need to make sure to resize the nodeTransforms array as well
- mNodeTransforms.setSize(mShape->nodes.size());
- // temporary storage for node transforms
- smNodeCurrentRotations.setSize(mShape->nodes.size());
- smNodeCurrentTranslations.setSize(mShape->nodes.size());
- smNodeLocalTransforms.setSize(mShape->nodes.size());
- smRotationThreads.setSize(mShape->nodes.size());
- smTranslationThreads.setSize(mShape->nodes.size());
- TSIntegerSet rotBeenSet;
- TSIntegerSet tranBeenSet;
- TSIntegerSet scaleBeenSet;
- rotBeenSet.setAll(mShape->nodes.size());
- tranBeenSet.setAll(mShape->nodes.size());
- scaleBeenSet.setAll(mShape->nodes.size());
- smNodeLocalTransformDirty.clearAll();
- S32 i,j,nodeIndex,a,b,start,end,firstBlend = mThreadList.size();
- for (i=0; i<mThreadList.size(); i++)
- {
- TSThread * th = mThreadList[i];
- const TSShape::Sequence* threadSequence = th->getSequence();
- if (threadSequence->isBlend())
- {
- // blend sequences need default (if not set by other sequence)
- // break rather than continue because the rest will be blends too
- firstBlend = i;
- break;
- }
- rotBeenSet.takeAway(threadSequence->rotationMatters);
- tranBeenSet.takeAway(threadSequence->translationMatters);
- scaleBeenSet.takeAway(threadSequence->scaleMatters);
- }
- rotBeenSet.takeAway(mCallbackNodes);
- rotBeenSet.takeAway(mHandsOffNodes);
- rotBeenSet.overlap(mMaskRotationNodes);
- TSIntegerSet maskPosNodes=mMaskPosXNodes;
- maskPosNodes.overlap(mMaskPosYNodes);
- maskPosNodes.overlap(mMaskPosZNodes);
- tranBeenSet.overlap(maskPosNodes);
- tranBeenSet.takeAway(mCallbackNodes);
- tranBeenSet.takeAway(mHandsOffNodes);
- // can't add masked nodes since x, y, & z masked separately...
- // we'll set default regardless of mask status
- // all the nodes marked above need to have the default transform
- a = mShape->subShapeFirstNode[ss];
- b = a + mShape->subShapeNumNodes[ss];
- for (i=a; i<b; i++)
- {
- if (rotBeenSet.test(i))
- {
- mShape->defaultRotations[i].getQuatF(&smNodeCurrentRotations[i]);
- smRotationThreads[i] = NULL;
- }
- if (tranBeenSet.test(i))
- {
- smNodeCurrentTranslations[i] = mShape->defaultTranslations[i];
- smTranslationThreads[i] = NULL;
- }
- }
- // don't want a transform in these cases...
- rotBeenSet.overlap(mHandsOffNodes);
- rotBeenSet.overlap(mCallbackNodes);
- tranBeenSet.takeAway(maskPosNodes);
- tranBeenSet.overlap(mHandsOffNodes);
- tranBeenSet.overlap(mCallbackNodes);
- // default scale
- if (scaleCurrentlyAnimated())
- handleDefaultScale(a,b,scaleBeenSet);
- // handle non-blend sequences
- for (i=0; i<firstBlend; i++)
- {
- TSThread * th = mThreadList[i];
- j=0;
- start = th->getSequence()->rotationMatters.start();
- end = b;
- for (nodeIndex=start; nodeIndex<end; th->getSequence()->rotationMatters.next(nodeIndex), j++)
- {
- // skip nodes outside of this detail
- if (nodeIndex<a)
- continue;
- if (!rotBeenSet.test(nodeIndex))
- {
- QuatF q1,q2;
- mShape->getRotation(*th->getSequence(),th->keyNum1,j,&q1);
- mShape->getRotation(*th->getSequence(),th->keyNum2,j,&q2);
- TSTransform::interpolate(q1,q2,th->keyPos,&smNodeCurrentRotations[nodeIndex]);
- rotBeenSet.set(nodeIndex);
- smRotationThreads[nodeIndex] = th;
- }
- }
- j=0;
- start = th->getSequence()->translationMatters.start();
- end = b;
- for (nodeIndex=start; nodeIndex<end; th->getSequence()->translationMatters.next(nodeIndex), j++)
- {
- if (nodeIndex<a)
- continue;
- if (!tranBeenSet.test(nodeIndex))
- {
- if (maskPosNodes.test(nodeIndex))
- handleMaskedPositionNode(th,nodeIndex,j);
- else
- {
- const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,j);
- const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,j);
- TSTransform::interpolate(p1,p2,th->keyPos,&smNodeCurrentTranslations[nodeIndex]);
- smTranslationThreads[nodeIndex] = th;
- }
- tranBeenSet.set(nodeIndex);
- }
- }
- if (scaleCurrentlyAnimated())
- handleAnimatedScale(th,a,b,scaleBeenSet);
- }
- // compute transforms
- for (i=a; i<b; i++)
- {
- if (!mHandsOffNodes.test(i))
- TSTransform::setMatrix(smNodeCurrentRotations[i],smNodeCurrentTranslations[i],&smNodeLocalTransforms[i]);
- else
- smNodeLocalTransforms[i] = mNodeTransforms[i]; // in case mNodeTransform was changed externally
- }
- // add scale onto transforms
- if (scaleCurrentlyAnimated())
- handleNodeScale(a,b);
- // get callbacks...
- start = getMax(mCallbackNodes.start(),a);
- end = getMin(mCallbackNodes.end(),b);
- for (i=0; i<mNodeCallbacks.size(); i++)
- {
- AssertFatal(mNodeCallbacks[i].callback, "No callback method defined");
- S32 nodeIdx = mNodeCallbacks[i].nodeIndex;
- if (nodeIdx >=start && nodeIdx<end)
- {
- mNodeCallbacks[i].callback->setNodeTransform(this, nodeIdx, smNodeLocalTransforms[nodeIdx]);
- smNodeLocalTransformDirty.set(nodeIdx);
- }
- }
- // handle blend sequences
- for (i=firstBlend; i<mThreadList.size(); i++)
- {
- TSThread * th = mThreadList[i];
- if (th->blendDisabled)
- continue;
- handleBlendSequence(th,a,b);
- }
- // transitions...
- if (inTransition())
- handleTransitionNodes(a,b);
- // multiply transforms...
- for (i=a; i<b; i++)
- {
- S32 parentIdx = mShape->nodes[i].parentIndex;
- if (parentIdx < 0)
- mNodeTransforms[i] = smNodeLocalTransforms[i];
- else
- mNodeTransforms[i].mul(mNodeTransforms[parentIdx],smNodeLocalTransforms[i]);
- }
- }
- void TSShapeInstance::handleDefaultScale(S32 a, S32 b, TSIntegerSet & scaleBeenSet)
- {
- // set default scale values (i.e., identity) and do any initialization
- // relating to animated scale (since scale normally not animated)
- smScaleThreads.setSize(mShape->nodes.size());
- scaleBeenSet.takeAway(mCallbackNodes);
- scaleBeenSet.takeAway(mHandsOffNodes);
- if (animatesUniformScale())
- {
- smNodeCurrentUniformScales.setSize(mShape->nodes.size());
- for (S32 i=a; i<b; i++)
- if (scaleBeenSet.test(i))
- {
- smNodeCurrentUniformScales[i] = 1.0f;
- smScaleThreads[i] = NULL;
- }
- }
- else if (animatesAlignedScale())
- {
- smNodeCurrentAlignedScales.setSize(mShape->nodes.size());
- for (S32 i=a; i<b; i++)
- if (scaleBeenSet.test(i))
- {
- smNodeCurrentAlignedScales[i].set(1.0f,1.0f,1.0f);
- smScaleThreads[i] = NULL;
- }
- }
- else
- {
- smNodeCurrentArbitraryScales.setSize(mShape->nodes.size());
- for (S32 i=a; i<b; i++)
- if (scaleBeenSet.test(i))
- {
- smNodeCurrentArbitraryScales[i].identity();
- smScaleThreads[i] = NULL;
- }
- }
- scaleBeenSet.overlap(mHandsOffNodes);
- scaleBeenSet.overlap(mCallbackNodes);
- }
- void TSShapeInstance::updateTransitionNodeTransforms(TSIntegerSet& transitionNodes)
- {
- // handle transitions
- transitionNodes.clearAll();
- transitionNodes.overlap(mTransitionRotationNodes);
- transitionNodes.overlap(mTransitionTranslationNodes);
- transitionNodes.overlap(mTransitionScaleNodes);
- transitionNodes.takeAway(mHandsOffNodes);
- // Decompose transforms for nodes affected by the transition. Only need to do
- // for blended or scale-animated nodes, as all others are already up to date
- for (S32 i=transitionNodes.start(); i<MAX_TS_SET_SIZE; transitionNodes.next(i))
- {
- if (smNodeLocalTransformDirty.test(i))
- {
- if (scaleCurrentlyAnimated())
- {
- // @todo:No support for scale yet => need to do proper affine decomposition here
- smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition();
- smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]);
- }
- else
- {
- // Scale is identity => can do a cheap decomposition
- smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition();
- smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]);
- }
- }
- }
- }
- void TSShapeInstance::handleTransitionNodes(S32 a, S32 b)
- {
- TSIntegerSet transitionNodes;
- updateTransitionNodeTransforms(transitionNodes);
- S32 nodeIndex;
- S32 start = mTransitionRotationNodes.start();
- S32 end = b;
- for (nodeIndex=start; nodeIndex<end; mTransitionRotationNodes.next(nodeIndex))
- {
- if (nodeIndex<a)
- continue;
- TSThread * thread = smRotationThreads[nodeIndex];
- thread = thread && thread->transitionData.inTransition ? thread : NULL;
- if (!thread)
- {
- // if not controlled by a sequence in transition then there must be
- // some other thread out there that used to control us that is in
- // transition now...use that thread to control interpolation
- for (S32 i=0; i<mTransitionThreads.size(); i++)
- {
- if (mTransitionThreads[i]->transitionData.oldRotationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->rotationMatters.test(nodeIndex))
- {
- thread = mTransitionThreads[i];
- break;
- }
- }
- AssertFatal(thread!=NULL,"TSShapeInstance::handleRotTransitionNodes (rotation)");
- }
- QuatF tmpQ;
- TSTransform::interpolate(mNodeReferenceRotations[nodeIndex].getQuatF(&tmpQ),smNodeCurrentRotations[nodeIndex],thread->transitionData.pos,&smNodeCurrentRotations[nodeIndex]);
- }
- // then translation
- start = mTransitionTranslationNodes.start();
- end = b;
- for (nodeIndex=start; nodeIndex<end; mTransitionTranslationNodes.next(nodeIndex))
- {
- TSThread * thread = smTranslationThreads[nodeIndex];
- thread = thread && thread->transitionData.inTransition ? thread : NULL;
- if (!thread)
- {
- // if not controlled by a sequence in transition then there must be
- // some other thread out there that used to control us that is in
- // transition now...use that thread to control interpolation
- for (S32 i=0; i<mTransitionThreads.size(); i++)
- {
- if (mTransitionThreads[i]->transitionData.oldTranslationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->translationMatters.test(nodeIndex))
- {
- thread = mTransitionThreads[i];
- break;
- }
- }
- AssertFatal(thread!=NULL,"TSShapeInstance::handleTransitionNodes (translation).");
- }
- Point3F & p = smNodeCurrentTranslations[nodeIndex];
- Point3F & p1 = mNodeReferenceTranslations[nodeIndex];
- Point3F & p2 = p;
- F32 k = thread->transitionData.pos;
- p.x = p1.x + k * (p2.x-p1.x);
- p.y = p1.y + k * (p2.y-p1.y);
- p.z = p1.z + k * (p2.z-p1.z);
- }
- // then scale...
- if (scaleCurrentlyAnimated())
- {
- start = mTransitionScaleNodes.start();
- end = b;
- for (nodeIndex=start; nodeIndex<end; mTransitionScaleNodes.next(nodeIndex))
- {
- TSThread * thread = smScaleThreads[nodeIndex];
- thread = thread && thread->transitionData.inTransition ? thread : NULL;
- if (!thread)
- {
- // if not controlled by a sequence in transition then there must be
- // some other thread out there that used to control us that is in
- // transition now...use that thread to control interpolation
- for (S32 i=0; i<mTransitionThreads.size(); i++)
- {
- if (mTransitionThreads[i]->transitionData.oldScaleNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->scaleMatters.test(nodeIndex))
- {
- thread = mTransitionThreads[i];
- break;
- }
- }
- AssertFatal(thread!=NULL,"TSShapeInstance::handleTransitionNodes (scale).");
- }
- if (animatesUniformScale())
- smNodeCurrentUniformScales[nodeIndex] += thread->transitionData.pos * (mNodeReferenceUniformScales[nodeIndex]-smNodeCurrentUniformScales[nodeIndex]);
- else if (animatesAlignedScale())
- TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentAlignedScales[nodeIndex],thread->transitionData.pos,&smNodeCurrentAlignedScales[nodeIndex]);
- else
- {
- QuatF q;
- TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentArbitraryScales[nodeIndex].mScale,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mScale);
- TSTransform::interpolate(mNodeReferenceArbitraryScaleRots[nodeIndex].getQuatF(&q),smNodeCurrentArbitraryScales[nodeIndex].mRotate,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mRotate);
- }
- }
- }
- // update transforms for transition nodes
- start = transitionNodes.start();
- end = b;
- for (nodeIndex=start; nodeIndex<end; transitionNodes.next(nodeIndex))
- {
- TSTransform::setMatrix(smNodeCurrentRotations[nodeIndex], smNodeCurrentTranslations[nodeIndex], &smNodeLocalTransforms[nodeIndex]);
- if (scaleCurrentlyAnimated())
- {
- if (animatesUniformScale())
- TSTransform::applyScale(smNodeCurrentUniformScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]);
- else if (animatesAlignedScale())
- TSTransform::applyScale(smNodeCurrentAlignedScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]);
- else
- TSTransform::applyScale(smNodeCurrentArbitraryScales[nodeIndex],&smNodeLocalTransforms[nodeIndex]);
- }
- }
- }
- void TSShapeInstance::handleNodeScale(S32 a, S32 b)
- {
- if (animatesUniformScale())
- {
- for (S32 i=a; i<b; i++)
- if (!mHandsOffNodes.test(i))
- TSTransform::applyScale(smNodeCurrentUniformScales[i],&smNodeLocalTransforms[i]);
- }
- else if (animatesAlignedScale())
- {
- for (S32 i=a; i<b; i++)
- if (!mHandsOffNodes.test(i))
- TSTransform::applyScale(smNodeCurrentAlignedScales[i],&smNodeLocalTransforms[i]);
- }
- else
- {
- for (S32 i=a; i<b; i++)
- if (!mHandsOffNodes.test(i))
- TSTransform::applyScale(smNodeCurrentArbitraryScales[i],&smNodeLocalTransforms[i]);
- }
- TSIntegerSet scaledNodes;
- scaledNodes.difference(mHandsOffNodes);
- smNodeLocalTransformDirty.overlap(scaledNodes);
- }
- void TSShapeInstance::handleAnimatedScale(TSThread * thread, S32 a, S32 b, TSIntegerSet & scaleBeenSet)
- {
- S32 j=0;
- S32 start = thread->getSequence()->scaleMatters.start();
- S32 end = b;
- // code the scale conversion (might need to "upgrade" from uniform to arbitrary, e.g.)
- // code uniform, aligned, and arbitrary as 0,1, and 2, respectively,
- // with sequence coding in first two bits, shape coding in next two bits
- S32 code = 0;
- if (thread->getSequence()->animatesAlignedScale())
- code += 1;
- else if (thread->getSequence()->animatesArbitraryScale())
- code += 2;
- if (animatesAlignedScale())
- code += 4;
- if (animatesArbitraryScale())
- code += 8;
- F32 uniformScale = 1.0f;
- Point3F alignedScale(0.0f, 0.0f, 0.0f);
- TSScale arbitraryScale;
- for (S32 nodeIndex=start; nodeIndex<end; thread->getSequence()->scaleMatters.next(nodeIndex), j++)
- {
- if (nodeIndex<a)
- continue;
- if (!scaleBeenSet.test(nodeIndex))
- {
- // compute scale in sequence format
- switch (code)
- { // Sequence Shape
- case 0: // uniform -> uniform
- case 4: // uniform -> aligned
- case 8: // uniform -> arbitrary
- {
- F32 s1 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum1,j);
- F32 s2 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum2,j);
- uniformScale = TSTransform::interpolate(s1,s2,thread->keyPos);
- alignedScale.set(uniformScale,uniformScale,uniformScale);
- break;
- }
- case 5: // aligned -> aligned
- case 9: // aligned -> arbitrary
- {
- const Point3F & s1 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum1,j);
- const Point3F & s2 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum2,j);
- TSTransform::interpolate(s1,s2,thread->keyPos,&alignedScale);
- break;
- }
- case 10: // arbitrary -> arbitary
- {
- TSScale s1,s2;
- mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum1,j,&s1);
- mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum2,j,&s2);
- TSTransform::interpolate(s1,s2,thread->keyPos,&arbitraryScale);
- break;
- }
- default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break;
- }
- switch (code)
- {
- case 0: // uniform -> uniform
- {
- smNodeCurrentUniformScales[nodeIndex] = uniformScale;
- break;
- }
- case 4: // uniform -> aligned
- case 5: // aligned -> aligned
- smNodeCurrentAlignedScales[nodeIndex] = alignedScale;
- break;
- case 8: // uniform -> arbitrary
- case 9: // aligned -> arbitrary
- {
- smNodeCurrentArbitraryScales[nodeIndex].identity();
- smNodeCurrentArbitraryScales[nodeIndex].mScale = alignedScale;
- break;
- }
- case 10: // arbitrary -> arbitary
- {
- smNodeCurrentArbitraryScales[nodeIndex] = arbitraryScale;
- break;
- }
- default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break;
- }
- smScaleThreads[nodeIndex] = thread;
- scaleBeenSet.set(nodeIndex);
- }
- }
- }
- void TSShapeInstance::handleMaskedPositionNode(TSThread * th, S32 nodeIndex, S32 offset)
- {
- const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,offset);
- const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,offset);
- Point3F p;
- TSTransform::interpolate(p1,p2,th->keyPos,&p);
- if (!mMaskPosXNodes.test(nodeIndex))
- smNodeCurrentTranslations[nodeIndex].x = p.x;
- if (!mMaskPosYNodes.test(nodeIndex))
- smNodeCurrentTranslations[nodeIndex].y = p.y;
- if (!mMaskPosZNodes.test(nodeIndex))
- smNodeCurrentTranslations[nodeIndex].z = p.z;
- }
- void TSShapeInstance::handleBlendSequence(TSThread * thread, S32 a, S32 b)
- {
- S32 jrot=0;
- S32 jtrans=0;
- S32 jscale=0;
- const TSShape::Sequence* threadSequence = thread->getSequence();
- TSIntegerSet nodeMatters = threadSequence->translationMatters;
- nodeMatters.overlap(threadSequence->rotationMatters);
- nodeMatters.overlap(threadSequence->scaleMatters);
- nodeMatters.takeAway(mHandsOffNodes);
- S32 start = nodeMatters.start();
- S32 end = b;
- for (S32 nodeIndex=start; nodeIndex<end; nodeMatters.next(nodeIndex))
- {
- // skip nodes outside of this detail
- if (start<a || mDisableBlendNodes.test(nodeIndex))
- {
- if (threadSequence->rotationMatters.test(nodeIndex))
- jrot++;
- if (threadSequence->translationMatters.test(nodeIndex))
- jtrans++;
- if (threadSequence->scaleMatters.test(nodeIndex))
- jscale++;
- continue;
- }
- MatrixF mat(true);
- if (threadSequence->rotationMatters.test(nodeIndex))
- {
- QuatF q1,q2;
- mShape->getRotation(*threadSequence,thread->keyNum1,jrot,&q1);
- mShape->getRotation(*threadSequence,thread->keyNum2,jrot,&q2);
- QuatF quat;
- TSTransform::interpolate(q1,q2,thread->keyPos,&quat);
- TSTransform::setMatrix(quat,&mat);
- jrot++;
- }
- if (threadSequence->translationMatters.test(nodeIndex))
- {
- const Point3F & p1 = mShape->getTranslation(*threadSequence,thread->keyNum1,jtrans);
- const Point3F & p2 = mShape->getTranslation(*threadSequence,thread->keyNum2,jtrans);
- Point3F p;
- TSTransform::interpolate(p1,p2,thread->keyPos,&p);
- mat.setColumn(3,p);
- jtrans++;
- }
- if (threadSequence->scaleMatters.test(nodeIndex))
- {
- if (threadSequence->animatesUniformScale())
- {
- F32 s1 = mShape->getUniformScale(*threadSequence,thread->keyNum1,jscale);
- F32 s2 = mShape->getUniformScale(*threadSequence,thread->keyNum2,jscale);
- F32 scale = TSTransform::interpolate(s1,s2,thread->keyPos);
- TSTransform::applyScale(scale,&mat);
- }
- else if (animatesAlignedScale())
- {
- Point3F s1 = mShape->getAlignedScale(*threadSequence,thread->keyNum1,jscale);
- Point3F s2 = mShape->getAlignedScale(*threadSequence,thread->keyNum2,jscale);
- Point3F scale;
- TSTransform::interpolate(s1,s2,thread->keyPos,&scale);
- TSTransform::applyScale(scale,&mat);
- }
- else
- {
- TSScale s1,s2;
- mShape->getArbitraryScale(*threadSequence,thread->keyNum1,jscale,&s1);
- mShape->getArbitraryScale(*threadSequence,thread->keyNum2,jscale,&s2);
- TSScale scale;
- TSTransform::interpolate(s1,s2,thread->keyPos,&scale);
- TSTransform::applyScale(scale,&mat);
- }
- jscale++;
- }
- // apply blend transform
- smNodeLocalTransforms[nodeIndex].mul(mat);
- smNodeLocalTransformDirty.set(nodeIndex);
- }
- }
- //-------------------------------------------------------------------------------------
- // Other Animation:
- //-------------------------------------------------------------------------------------
- void TSShapeInstance::animateVisibility(S32 ss)
- {
- PROFILE_SCOPE( TSShapeInstance_animateVisibility );
- S32 i;
- if (!mMeshObjects.size())
- return;
- // find out who needs default values set
- TSIntegerSet beenSet;
- beenSet.setAll(mMeshObjects.size());
- for (i=0; i<mThreadList.size(); i++)
- beenSet.takeAway(mThreadList[i]->getSequence()->visMatters);
- // set defaults
- S32 a = mShape->subShapeFirstObject[ss];
- S32 b = a + mShape->subShapeNumObjects[ss];
- for (i=a; i<b; i++)
- {
- if (beenSet.test(i))
- mMeshObjects[i].visible = mShape->objectStates[i].vis;
- }
- // go through each thread and set visibility on those objects that
- // are not set yet and are controlled by that thread
- for (i=0; i<mThreadList.size(); i++)
- {
- TSThread * th = mThreadList[i];
- const TSShape::Sequence* threadSequence = th->getSequence();
- // For better or worse, object states are stored together (frame,
- // matFrame, visibility all in one structure). Thus, indexing into
- // object state array for animation for any of these attributes needs to
- // take into account whether or not the other attributes are also animated.
- // The object states should eventually be separated (like the node states were)
- // in order to save memory and save the following step.
- TSIntegerSet objectMatters = threadSequence->frameMatters;
- objectMatters.overlap(threadSequence->matFrameMatters);
- objectMatters.overlap(threadSequence->visMatters);
- // skip to beginning of this sub-shape
- S32 j=0;
- S32 start = objectMatters.start();
- S32 end = b;
- for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
- {
- if (!beenSet.test(objectIndex) && threadSequence->visMatters.test(objectIndex))
- {
- F32 state1 = mShape->getObjectState(*threadSequence,th->keyNum1,j).vis;
- F32 state2 = mShape->getObjectState(*threadSequence,th->keyNum2,j).vis;
- if ((state1-state2) * (state1-state2) > 0.99f)
- // goes from 0 to 1 -- discreet jump
- mMeshObjects[objectIndex].visible = th->keyPos<0.5f ? state1 : state2;
- else
- // interpolate between keyframes when visibility change is gradual
- mMeshObjects[objectIndex].visible = (1.0f-th->keyPos) * state1 + th->keyPos * state2;
- // record change so that later threads don't over-write us...
- beenSet.set(objectIndex);
- }
- }
- }
- }
- void TSShapeInstance::animateFrame(S32 ss)
- {
- PROFILE_SCOPE( TSShapeInstance_animateFrame );
- S32 i;
- if (!mMeshObjects.size())
- return;
- // find out who needs default values set
- TSIntegerSet beenSet;
- beenSet.setAll(mMeshObjects.size());
- for (i=0; i<mThreadList.size(); i++)
- beenSet.takeAway(mThreadList[i]->getSequence()->frameMatters);
- // set defaults
- S32 a = mShape->subShapeFirstObject[ss];
- S32 b = a + mShape->subShapeNumObjects[ss];
- for (i=a; i<b; i++)
- if (beenSet.test(i))
- mMeshObjects[i].frame = mShape->objectStates[i].frameIndex;
- // go through each thread and set frame on those objects that
- // are not set yet and are controlled by that thread
- for (i=0; i<mThreadList.size(); i++)
- {
- TSThread * th = mThreadList[i];
- const TSShape::Sequence* threadSequence = th->getSequence();
- // For better or worse, object states are stored together (frame,
- // matFrame, visibility all in one structure). Thus, indexing into
- // object state array for animation for any of these attributes needs to
- // take into account whether or not the other attributes are also animated.
- // The object states should eventually be separated (like the node states were)
- // in order to save memory and save the following step.
- TSIntegerSet objectMatters = threadSequence->frameMatters;
- objectMatters.overlap(threadSequence->matFrameMatters);
- objectMatters.overlap(threadSequence->visMatters);
- // skip to beginning of this sub-shape
- S32 j=0;
- S32 start = objectMatters.start();
- S32 end = b;
- for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
- {
- if (!beenSet.test(objectIndex) && threadSequence->frameMatters.test(objectIndex))
- {
- S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2;
- mMeshObjects[objectIndex].frame = mShape->getObjectState(*threadSequence,key,j).frameIndex;
- // record change so that later threads don't over-write us...
- beenSet.set(objectIndex);
- }
- }
- }
- }
- void TSShapeInstance::animateMatFrame(S32 ss)
- {
- PROFILE_SCOPE( TSShapeInstance_animateMatFrame );
- S32 i;
- if (!mMeshObjects.size())
- return;
- // find out who needs default values set
- TSIntegerSet beenSet;
- beenSet.setAll(mMeshObjects.size());
- for (i=0; i<mThreadList.size(); i++)
- beenSet.takeAway(mThreadList[i]->getSequence()->matFrameMatters);
- // set defaults
- S32 a = mShape->subShapeFirstObject[ss];
- S32 b = a + mShape->subShapeNumObjects[ss];
- for (i=a; i<b; i++)
- if (beenSet.test(i))
- mMeshObjects[i].matFrame = mShape->objectStates[i].matFrameIndex;
- // go through each thread and set matFrame on those objects that
- // are not set yet and are controlled by that thread
- for (i=0; i<mThreadList.size(); i++)
- {
- TSThread * th = mThreadList[i];
- const TSShape::Sequence* threadSequence = th->getSequence();
- // For better or worse, object states are stored together (frame,
- // matFrame, visibility all in one structure). Thus, indexing into
- // object state array for animation for any of these attributes needs to
- // take into account whether or not the other attributes are also animated.
- // The object states should eventually be separated (like the node states were)
- // in order to save memory and save the following step.
- TSIntegerSet objectMatters = threadSequence->frameMatters;
- objectMatters.overlap(threadSequence->matFrameMatters);
- objectMatters.overlap(threadSequence->visMatters);
- // skip to beginining of this sub-shape
- S32 j=0;
- S32 start = objectMatters.start();
- S32 end = b;
- for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
- {
- if (!beenSet.test(objectIndex) && threadSequence->matFrameMatters.test(objectIndex))
- {
- S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2;
- mMeshObjects[objectIndex].matFrame = mShape->getObjectState(*threadSequence,key,j).matFrameIndex;
- // record change so that later threads don't over-write us...
- beenSet.set(objectIndex);
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------
- // Animate (and initialize detail levels)
- //-------------------------------------------------------------------------------------
- void TSShapeInstance::animate(S32 dl)
- {
- PROFILE_SCOPE( TSShapeInstance_animate );
- if (dl==-1)
- // nothing to do
- return;
- S32 ss = mShape->details[dl].subShapeNum;
- // this is a billboard detail...
- if (ss<0)
- return;
- U32 dirtyFlags = mDirtyFlags[ss];
- if (dirtyFlags & ThreadDirty)
- sortThreads();
- // animate nodes?
- if (dirtyFlags & TransformDirty)
- animateNodes(ss);
- // animate objects?
- if (dirtyFlags & VisDirty)
- animateVisibility(ss);
- if (dirtyFlags & FrameDirty)
- animateFrame(ss);
- if (dirtyFlags & MatFrameDirty)
- animateMatFrame(ss);
- mDirtyFlags[ss] = 0;
- }
- void TSShapeInstance::animateNodeSubtrees(bool forceFull)
- {
- // animate all the nodes for all the detail levels...
- if (forceFull)
- // force transforms to animate
- setDirty(TransformDirty);
- for (S32 i=0; i<mShape->subShapeNumNodes.size(); i++)
- {
- if (mDirtyFlags[i] & TransformDirty)
- {
- animateNodes(i);
- mDirtyFlags[i] &= ~TransformDirty;
- }
- }
- }
- void TSShapeInstance::animateSubtrees(bool forceFull)
- {
- // animate all the subtrees
- if (forceFull)
- // force full animate
- setDirty(AllDirtyMask);
- for (S32 i=0; i<mShape->subShapeNumNodes.size(); i++)
- {
- if (mDirtyFlags[i] & TransformDirty)
- {
- animate(i);
- mDirtyFlags[i] = 0;
- }
- }
- }
- void TSShapeInstance::addPath(TSThread *gt, F32 start, F32 end, MatrixF *mat)
- {
- // never get here while in transition...
- AssertFatal(!gt->transitionData.inTransition,"TSShapeInstance::addPath");
- if (!mat)
- mat = &mGroundTransform;
- MatrixF startInvM;
- gt->getGround(start,&startInvM);
- startInvM.inverse();
- MatrixF endM;
- gt->getGround(end,&endM);
- MatrixF addM;
- addM.mul(startInvM,endM);
- endM.mul(*mat,addM);
- *mat = endM;
- }
- bool TSShapeInstance::initGround()
- {
- for (S32 i=0; i<mThreadList.size(); i++)
- {
- TSThread * th = mThreadList[i];
- if (!th->transitionData.inTransition && th->getSequence()->numGroundFrames>0)
- {
- mGroundThread = th;
- return true;
- }
- }
- return false;
- }
- void TSShapeInstance::animateGround()
- {
- mGroundTransform.identity();
- // pick thread which controlls ground transform
- // if we haven't already...
- if (!mGroundThread && !initGround())
- return;
- S32 & loop = mGroundThread->path.loop;
- F32 & start = mGroundThread->path.start;
- F32 & end = mGroundThread->path.end;
- // accumulate path transform
- if (loop>0)
- {
- addPath(mGroundThread,start,1.0f);
- while (--loop)
- addPath(mGroundThread,0.0f,1.0f);
- addPath(mGroundThread,0.0f,end);
- }
- else if (loop<0)
- {
- addPath(mGroundThread,start,0.0f);
- while (++loop)
- addPath(mGroundThread,1.0f,0.0f);
- addPath(mGroundThread,1.0f,end);
- }
- else
- addPath(mGroundThread,start,end);
- start = end; // in case user tries to animateGround twice
- }
- void TSShapeInstance::deltaGround(TSThread * thread, F32 start, F32 end, MatrixF * mat)
- {
- if (!mat)
- mat = &mGroundTransform;
- mat->identity();
- if (thread->transitionData.inTransition)
- return;
- F32 invDuration = 1.0f / thread->getDuration();
- start *= invDuration;
- end *= invDuration;
- addPath(thread,start,end,mat);
- }
- // Simple case of above- get ground delta at given position in unit range
- void TSShapeInstance::deltaGround1(TSThread * thread, F32 start, F32 end, MatrixF& mat)
- {
- mat.identity();
- if (thread->transitionData.inTransition)
- return;
- addPath(thread, start, end, &mat);
- }
- void TSShapeInstance::setTriggerState(U32 stateNum, bool on)
- {
- AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::setTriggerState: state index out of range");
- stateNum--; // stateNum externally 1..32, internally 0..31
- U32 bit = 1 << stateNum;
- if (on)
- mTriggerStates |= bit;
- else
- mTriggerStates &= ~bit;
- }
- void TSShapeInstance::setTriggerStateBit(U32 stateBit, bool on)
- {
- if (on)
- mTriggerStates |= stateBit;
- else
- mTriggerStates &= ~stateBit;
- }
- bool TSShapeInstance::getTriggerState(U32 stateNum, bool clearState)
- {
- AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::getTriggerState: state index out of range");
- stateNum--; // stateNum externally 1..32, internally 0..31
- U32 bit = 1 << stateNum;
- bool ret = ((mTriggerStates & bit)!=0);
- if (clearState)
- mTriggerStates &= ~bit;
- return ret;
- }
- void TSShapeInstance::setNodeAnimationState(S32 nodeIndex, U32 animationState, TSCallback * callback)
- {
- AssertFatal((animationState & ~(MaskNodeAll|MaskNodeHandsOff|MaskNodeCallback)) == 0,"TSShapeInstance::setNodeAnimationState (1)");
- // don't handle callback nodes in this method
- if (callback)
- animationState |= MaskNodeCallback;
- else
- animationState &= ~MaskNodeCallback;
- // hands-off takes precedance
- if (animationState & MaskNodeHandsOff)
- animationState = MaskNodeHandsOff | MaskNodeBlend;
- else if (animationState & MaskNodeCallback)
- animationState = MaskNodeCallback | MaskNodeBlend;
- // if we're not changing anything then get out of here now
- if (animationState == getNodeAnimationState(nodeIndex))
- return;
- setDirty(AllDirtyMask);
- if (animationState & MaskNodeAllButBlend)
- {
- if (animationState & MaskNodeRotation)
- mMaskRotationNodes.set(nodeIndex);
- if (animationState & MaskNodePosX)
- mMaskPosXNodes.set(nodeIndex);
- if (animationState & MaskNodePosY)
- mMaskPosYNodes.set(nodeIndex);
- if (animationState & MaskNodePosZ)
- mMaskPosZNodes.set(nodeIndex);
- }
- else
- {
- // no masking clear out all the masking lists
- mMaskRotationNodes.clear(nodeIndex);
- mMaskPosXNodes.clear(nodeIndex);
- mMaskPosYNodes.clear(nodeIndex);
- mMaskPosZNodes.clear(nodeIndex);
- }
- if (animationState & MaskNodeBlend)
- mDisableBlendNodes.set(nodeIndex);
- else
- mDisableBlendNodes.clear(nodeIndex);
- if (animationState & MaskNodeHandsOff)
- mHandsOffNodes.set(nodeIndex);
- else
- mHandsOffNodes.clear(nodeIndex);
- // clear out of node callbacks
- for (S32 i=0; i<mNodeCallbacks.size(); i++)
- {
- if (mNodeCallbacks[i].nodeIndex == nodeIndex)
- {
- mNodeCallbacks.erase_fast(i);
- break;
- }
- }
- if (animationState & MaskNodeCallback)
- {
- mCallbackNodes.set(nodeIndex);
- mNodeCallbacks.increment();
- mNodeCallbacks.last().callback = callback;
- mNodeCallbacks.last().nodeIndex = nodeIndex;
- }
- else
- mCallbackNodes.clear(nodeIndex);
- }
- U32 TSShapeInstance::getNodeAnimationState(S32 nodeIndex)
- {
- U32 ret = 0;
- if (mMaskRotationNodes.test(nodeIndex))
- ret |= MaskNodeRotation;
- if (mMaskPosXNodes.test(nodeIndex))
- ret |= MaskNodePosX;
- if (mMaskPosYNodes.test(nodeIndex))
- ret |= MaskNodePosY;
- if (mMaskPosZNodes.test(nodeIndex))
- ret |= MaskNodePosZ;
- if (mDisableBlendNodes.test(nodeIndex))
- ret |= MaskNodeBlend;
- if (mHandsOffNodes.test(nodeIndex))
- ret |= MaskNodeHandsOff;
- if (mCallbackNodes.test(nodeIndex))
- ret |= MaskNodeCallback;
- return ret;
- }
|