|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
#include "plAnimEaseTypes.h"
|
|
|
|
#include "plAnimTimeConvert.h"
|
|
|
|
|
|
|
|
#include "hsTimer.h"
|
|
|
|
#include "hsStream.h"
|
|
|
|
|
|
|
|
#include "pnMessage/plEventCallbackMsg.h"
|
|
|
|
#include "plMessage/plAnimCmdMsg.h"
|
|
|
|
|
|
|
|
#include "pnNetCommon/plSDLTypes.h"
|
|
|
|
|
|
|
|
#include "hsResMgr.h"
|
|
|
|
#include "plgDispatch.h"
|
|
|
|
#include "plCreatableIndex.h"
|
|
|
|
|
|
|
|
|
|
|
|
plAnimTimeConvert::plAnimTimeConvert()
|
|
|
|
: fCurrentAnimTime(0),
|
|
|
|
fLastEvalWorldTime(0),
|
|
|
|
fBegin(0),
|
|
|
|
fEnd(0),
|
|
|
|
fLoopEnd(0),
|
|
|
|
fLoopBegin(0),
|
|
|
|
fSpeed(1.f),
|
|
|
|
fFlags(0),
|
|
|
|
fOwner(nil),
|
|
|
|
fEaseInCurve(nil),
|
|
|
|
fEaseOutCurve(nil),
|
|
|
|
fSpeedEaseCurve(nil),
|
|
|
|
fCurrentEaseCurve(nil),
|
|
|
|
fInitialBegin(0),
|
|
|
|
fInitialEnd(0),
|
|
|
|
fWrapTime(0)
|
|
|
|
//fDirtyNotifier(nil)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert::~plAnimTimeConvert()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < fCallbackMsgs.GetCount(); i++ )
|
|
|
|
hsRefCnt_SafeUnRef(fCallbackMsgs[i]);
|
|
|
|
|
|
|
|
delete fEaseInCurve;
|
|
|
|
delete fEaseOutCurve;
|
|
|
|
delete fSpeedEaseCurve;
|
|
|
|
//delete fDirtyNotifier;
|
|
|
|
|
|
|
|
IClearAllStates();
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// 0=nil, 1=easeIn, 2=easeOut, 3=speed
|
|
|
|
//
|
|
|
|
void plAnimTimeConvert::SetCurrentEaseCurve(int x)
|
|
|
|
{
|
|
|
|
switch(x)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
hsAssert(false, "invalid arg to SetCurrentEaseCurve");
|
|
|
|
break;
|
|
|
|
case kEaseNone:
|
|
|
|
fCurrentEaseCurve=nil;
|
|
|
|
break;
|
|
|
|
case kEaseIn:
|
|
|
|
fCurrentEaseCurve=fEaseInCurve;
|
|
|
|
break;
|
|
|
|
case kEaseOut:
|
|
|
|
fCurrentEaseCurve=fEaseOutCurve;
|
|
|
|
break;
|
|
|
|
case kEaseSpeed:
|
|
|
|
fCurrentEaseCurve=fSpeedEaseCurve;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int plAnimTimeConvert::GetCurrentEaseCurve() const
|
|
|
|
{
|
|
|
|
if (fCurrentEaseCurve==nil)
|
|
|
|
return kEaseNone;
|
|
|
|
if (fCurrentEaseCurve==fEaseInCurve)
|
|
|
|
return kEaseIn;
|
|
|
|
if (fCurrentEaseCurve==fEaseOutCurve)
|
|
|
|
return kEaseOut;
|
|
|
|
if (fCurrentEaseCurve==fSpeedEaseCurve)
|
|
|
|
return kEaseSpeed;
|
|
|
|
|
|
|
|
hsAssert(false, "unknown ease curve");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// set the number of ATCStates we have
|
|
|
|
//
|
|
|
|
void plAnimTimeConvert::ResizeStates(int cnt)
|
|
|
|
{
|
|
|
|
while (fStates.size() > cnt)
|
|
|
|
{
|
|
|
|
delete fStates.back();
|
|
|
|
fStates.pop_back();
|
|
|
|
}
|
|
|
|
|
|
|
|
while (cnt>fStates.size())
|
|
|
|
{
|
|
|
|
fStates.push_back(new plATCState);
|
|
|
|
}
|
|
|
|
|
|
|
|
hsAssert(fStates.size()==cnt, "state resize mismatch");
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::ResetWrap()
|
|
|
|
{
|
|
|
|
fBegin = fInitialBegin;
|
|
|
|
fEnd = fInitialEnd;
|
|
|
|
Forewards();
|
|
|
|
|
|
|
|
fFlags &= (~kWrap & ~kNeedsReset);
|
|
|
|
}
|
|
|
|
|
|
|
|
float plAnimTimeConvert::ICalcEaseTime(const plATCEaseCurve *curve, double start, double end)
|
|
|
|
{
|
|
|
|
start -= curve->fBeginWorldTime;
|
|
|
|
end -= curve->fBeginWorldTime;
|
|
|
|
|
|
|
|
// Clamp to the curve's range
|
|
|
|
if (start < 0)
|
|
|
|
start = 0;
|
|
|
|
if (end > curve->fLength)
|
|
|
|
end = curve->fLength;
|
|
|
|
|
|
|
|
|
|
|
|
float delSecs = 0;
|
|
|
|
|
|
|
|
if (start < curve->fLength)
|
|
|
|
{
|
|
|
|
// Redundant eval... but only when easing.
|
|
|
|
delSecs = curve->PositionGivenTime((float)end) - curve->PositionGivenTime((float)start);
|
|
|
|
}
|
|
|
|
return delSecs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::IClearSpeedEase()
|
|
|
|
{
|
|
|
|
|
|
|
|
if (fCurrentEaseCurve == fSpeedEaseCurve)
|
|
|
|
fCurrentEaseCurve = nil;
|
|
|
|
|
|
|
|
delete fSpeedEaseCurve;
|
|
|
|
fSpeedEaseCurve = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::ICheckTimeCallbacks(float frameStart, float frameStop)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kTime)
|
|
|
|
{
|
|
|
|
if( ITimeInFrame(fCallbackMsgs[i]->fEventTime, frameStart, frameStop) )
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
else if (fCallbackMsgs[i]->fEvent == kBegin && ITimeInFrame(fBegin, frameStart, frameStop) )
|
|
|
|
ISendCallback(i);
|
|
|
|
else if (fCallbackMsgs[i]->fEvent == kEnd && ITimeInFrame(fEnd, frameStart, frameStop) )
|
|
|
|
ISendCallback(i);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plAnimTimeConvert::ITimeInFrame(float secs, float start, float stop)
|
|
|
|
{
|
|
|
|
if (secs == start && secs == stop)
|
|
|
|
return true;
|
|
|
|
if( IsBackwards() )
|
|
|
|
{
|
|
|
|
if( start < stop )
|
|
|
|
{
|
|
|
|
// We've just wrapped. Careful to exclude markers outside current loop.
|
|
|
|
if( ((secs <= start) && (secs >= fLoopBegin))
|
|
|
|
|| ((secs >= stop) && (secs <= fLoopEnd)) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( (secs <= start) && (secs >= stop) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( start > stop )
|
|
|
|
{
|
|
|
|
// We've just wrapped. Careful to exclude markers outside current loop.
|
|
|
|
if( ((secs >= start) && (secs <= fLoopEnd))
|
|
|
|
|| ((secs <= stop) && (secs >= fLoopBegin)) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( (secs >= start) && (secs <= stop) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::ISendCallback(int i)
|
|
|
|
{
|
|
|
|
// Check if callbacks are disabled this frame (i.e. when we're loading in state)
|
|
|
|
if (fFlags & kNoCallbacks)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// send callback if msg is local or if we are the local master
|
|
|
|
if (!fCallbackMsgs[i]->HasBCastFlag(plMessage::kNetPropagate) ||
|
|
|
|
!fOwner || fOwner->IsLocallyOwned()==plSynchedObject::kYes)
|
|
|
|
{
|
|
|
|
plEventCallbackMsg *temp = fCallbackMsgs[i];
|
|
|
|
|
|
|
|
fCallbackMsgs[i]->SetSender(fOwner ? fOwner->GetKey() : nil);
|
|
|
|
|
|
|
|
hsRefCnt_SafeRef(fCallbackMsgs[i]);
|
|
|
|
plgDispatch::MsgSend(fCallbackMsgs[i]);
|
|
|
|
|
|
|
|
// No more repeats, remove this callback from our list
|
|
|
|
if (fCallbackMsgs[i]->fRepeats == 0)
|
|
|
|
{
|
|
|
|
hsRefCnt_SafeUnRef(fCallbackMsgs[i]);
|
|
|
|
fCallbackMsgs.Remove(i);
|
|
|
|
}
|
|
|
|
// If this isn't infinite, decrement the number of repeats
|
|
|
|
else if (fCallbackMsgs[i]->fRepeats > 0)
|
|
|
|
fCallbackMsgs[i]->fRepeats--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::IStop(double time, float animTime)
|
|
|
|
{
|
|
|
|
if( IsStopped() )
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
IClearSpeedEase(); // If we had one queued up, clear it. It will automatically take effect when we start
|
|
|
|
SetFlag(kEasingIn, false);
|
|
|
|
SetFlag(kStopped, true);
|
|
|
|
if (fFlags & kNeedsReset)
|
|
|
|
ResetWrap();
|
|
|
|
|
|
|
|
IProcessStateChange(time, animTime);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kStop)
|
|
|
|
{
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::IProcessStateChange(double worldTime, float animTime /* = -1 */)
|
|
|
|
{
|
|
|
|
if (fStates.size() > 0 && worldTime < fStates.front()->fStartWorldTime)
|
|
|
|
return *this; // Sorry... state saves only work in the forward direction
|
|
|
|
|
|
|
|
fLastStateChange = worldTime;
|
|
|
|
plATCState *state = new plATCState;
|
|
|
|
|
|
|
|
state->fStartWorldTime = fLastStateChange;
|
|
|
|
state->fStartAnimTime = (animTime < 0 ? WorldToAnimTimeNoUpdate(fLastStateChange) : animTime);
|
|
|
|
state->fFlags = (uint8_t)fFlags;
|
|
|
|
state->fBegin = fBegin;
|
|
|
|
state->fEnd = fEnd;
|
|
|
|
state->fLoopBegin = fLoopBegin;
|
|
|
|
state->fLoopEnd = fLoopEnd;
|
|
|
|
state->fSpeed = fSpeed;
|
|
|
|
state->fWrapTime = fWrapTime;
|
|
|
|
state->fEaseCurve = (fCurrentEaseCurve == nil ? nil : fCurrentEaseCurve->Clone());
|
|
|
|
|
|
|
|
fStates.push_front(state);
|
|
|
|
IFlushOldStates();
|
|
|
|
|
|
|
|
const char* sdlName = nullptr;
|
|
|
|
|
|
|
|
// This is a huge hack, but avoids circular linking problems :(
|
|
|
|
if (fOwner->GetInterface(CLASS_INDEX_SCOPED(plLayerAnimation)))
|
|
|
|
sdlName=kSDLLayer;
|
|
|
|
else
|
|
|
|
if (fOwner->GetInterface(CLASS_INDEX_SCOPED(plAGMasterMod)))
|
|
|
|
sdlName=kSDLAGMaster;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsAssert(false, "unknown sdl owner");
|
|
|
|
}
|
|
|
|
fOwner->DirtySynchState(sdlName, 0); // Send SDL state update to server
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove any out-of-date plATCStates
|
|
|
|
// Where "out-of-date" means, "More than 1 frame old"
|
|
|
|
void plAnimTimeConvert::IFlushOldStates()
|
|
|
|
{
|
|
|
|
plATCState *state;
|
|
|
|
plATCStateList::const_iterator i = fStates.begin();
|
|
|
|
uint32_t count = 0;
|
|
|
|
|
|
|
|
for (; i != fStates.end(); i++)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
state = *i;
|
|
|
|
if (fLastEvalWorldTime - hsTimer::GetDelSysSeconds() >= state->fStartWorldTime)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fStates.size() > count)
|
|
|
|
{
|
|
|
|
delete fStates.back();
|
|
|
|
fStates.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::IClearAllStates()
|
|
|
|
{
|
|
|
|
while (fStates.size() > 0)
|
|
|
|
{
|
|
|
|
delete fStates.back();
|
|
|
|
fStates.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plATCState *plAnimTimeConvert::IGetState(double wSecs) const
|
|
|
|
{
|
|
|
|
plATCState *state;
|
|
|
|
plATCStateList::const_iterator i = fStates.begin();
|
|
|
|
|
|
|
|
for (; i != fStates.end(); i++)
|
|
|
|
{
|
|
|
|
state = *i;
|
|
|
|
if (wSecs >= state->fStartWorldTime)
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
plATCState *plAnimTimeConvert::IGetLatestState() const
|
|
|
|
{
|
|
|
|
return fStates.front();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::SetOwner(plSynchedObject* o)
|
|
|
|
{
|
|
|
|
fOwner = o;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plAnimTimeConvert::IIsStoppedAt(const double &wSecs, const uint32_t &flags,
|
|
|
|
const plATCEaseCurve *curve) const
|
|
|
|
{
|
|
|
|
if (flags & kStopped)
|
|
|
|
return !(flags & kForcedMove); // If someone called SetCurrentAnimTime(), we need to say we moved.
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plAnimTimeConvert::IsStoppedAt(double wSecs) const
|
|
|
|
{
|
|
|
|
if (wSecs > fLastStateChange)
|
|
|
|
return IIsStoppedAt(wSecs, fFlags, fCurrentEaseCurve);
|
|
|
|
|
|
|
|
plATCState *state = IGetState(wSecs);
|
|
|
|
|
|
|
|
if ( !state )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return IIsStoppedAt(wSecs, state->fFlags, state->fEaseCurve);
|
|
|
|
}
|
|
|
|
|
|
|
|
float plAnimTimeConvert::WorldToAnimTime(double wSecs)
|
|
|
|
{
|
|
|
|
//hsAssert(wSecs >= fLastEvalWorldTime, "Tried to eval a time that's earlier than the last eval time.");
|
|
|
|
double d = wSecs - fLastEvalWorldTime;
|
|
|
|
float f = fCurrentAnimTime;
|
|
|
|
|
|
|
|
if (wSecs < fLastStateChange)
|
|
|
|
{
|
|
|
|
fCurrentAnimTime = IWorldToAnimTimeBeforeState(wSecs);
|
|
|
|
fLastEvalWorldTime = wSecs;
|
|
|
|
return fCurrentAnimTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fLastEvalWorldTime <= fLastStateChange) // Crossing into the latest state
|
|
|
|
{
|
|
|
|
fLastEvalWorldTime = fLastStateChange;
|
|
|
|
fCurrentAnimTime = IGetLatestState()->fStartAnimTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (fFlags & kStopped) || (wSecs == fLastEvalWorldTime) )
|
|
|
|
{
|
|
|
|
if (fFlags & kForcedMove)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kSingleFrameEval)
|
|
|
|
{
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fFlags &= ~kForcedMove;
|
|
|
|
fLastEvalWorldTime = wSecs;
|
|
|
|
|
|
|
|
return fCurrentAnimTime;
|
|
|
|
}
|
|
|
|
float note = fCurrentAnimTime - f;
|
|
|
|
float secs = 0, delSecs = 0;
|
|
|
|
|
|
|
|
if (fCurrentEaseCurve != nil)
|
|
|
|
{
|
|
|
|
delSecs += ICalcEaseTime(fCurrentEaseCurve, fLastEvalWorldTime, wSecs);
|
|
|
|
if (wSecs > fCurrentEaseCurve->GetEndWorldTime())
|
|
|
|
{
|
|
|
|
if (fFlags & kEasingIn)
|
|
|
|
delSecs += float(wSecs - fCurrentEaseCurve->GetEndWorldTime()) * fSpeed;
|
|
|
|
|
|
|
|
IClearSpeedEase();
|
|
|
|
|
|
|
|
fCurrentEaseCurve = nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// The easy case... playing the animation at a constant speed.
|
|
|
|
delSecs = float(wSecs - fLastEvalWorldTime) * fSpeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fFlags & kBackwards)
|
|
|
|
delSecs = -delSecs;
|
|
|
|
|
|
|
|
secs = fCurrentAnimTime + delSecs;
|
|
|
|
// At this point, "secs" is the pre-wrapped (before looping) anim time.
|
|
|
|
// "delSecs" is the change in anim time
|
|
|
|
|
|
|
|
// if our speed is < 0, then checking for the kBackwards flag isn't enough
|
|
|
|
// so we base our decision on the direction of the actual change we've computed.
|
|
|
|
bool forewards = delSecs >= 0;
|
|
|
|
|
|
|
|
if (fFlags & kLoop)
|
|
|
|
{
|
|
|
|
bool wrapped = false;
|
|
|
|
|
|
|
|
if (forewards)
|
|
|
|
{
|
|
|
|
if (IGetLatestState()->fStartAnimTime > fLoopEnd)
|
|
|
|
{
|
|
|
|
// Our animation started past the loop. Play to the end.
|
|
|
|
if (secs > fEnd)
|
|
|
|
{
|
|
|
|
secs = fEnd;
|
|
|
|
IStop(wSecs, secs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (secs > fLoopEnd)
|
|
|
|
{
|
|
|
|
secs = fmodf(secs - fLoopBegin, fLoopEnd - fLoopBegin) + fLoopBegin;
|
|
|
|
wrapped = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (IGetLatestState()->fStartAnimTime < fLoopBegin)
|
|
|
|
{
|
|
|
|
if (secs < fBegin)
|
|
|
|
{
|
|
|
|
secs = fBegin;
|
|
|
|
IStop(wSecs, secs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (secs < fLoopBegin)
|
|
|
|
{
|
|
|
|
secs = fLoopEnd - fmodf(fLoopEnd - secs, fLoopEnd - fLoopBegin);
|
|
|
|
wrapped = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fFlags & kWrap)
|
|
|
|
{
|
|
|
|
// possible options, representing each line:
|
|
|
|
// 1. We wrapped around the the beginning of the anim, so stop at the wrap point if we're past it.
|
|
|
|
// 2. Same as #1, but in the backwards case.
|
|
|
|
// 3. We started before the wrap point, now we're after it. Stop.
|
|
|
|
// 4. Same as #3, backwards.
|
|
|
|
if (((wrapped && (forewards && secs >= fWrapTime)) ||
|
|
|
|
(!forewards && secs <= fWrapTime)) ||
|
|
|
|
(forewards && fCurrentAnimTime < fWrapTime && secs >= fWrapTime) ||
|
|
|
|
(!forewards && fCurrentAnimTime > fWrapTime && secs <= fWrapTime))
|
|
|
|
{
|
|
|
|
secs = fWrapTime;
|
|
|
|
IStop(wSecs, secs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else // Not looping
|
|
|
|
{
|
|
|
|
if ((secs < fBegin) || (secs > fEnd))
|
|
|
|
{
|
|
|
|
secs = forewards ? fEnd : fBegin;
|
|
|
|
IStop(wSecs, secs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ICheckTimeCallbacks(fCurrentAnimTime, secs);
|
|
|
|
|
|
|
|
fLastEvalWorldTime = wSecs;
|
|
|
|
if (fEaseOutCurve != nil && !(fFlags & kEasingIn) && wSecs >= fEaseOutCurve->GetEndWorldTime())
|
|
|
|
IStop(wSecs, secs);
|
|
|
|
|
|
|
|
return fCurrentAnimTime = secs;
|
|
|
|
}
|
|
|
|
|
|
|
|
float plAnimTimeConvert::WorldToAnimTimeNoUpdate(double wSecs) const
|
|
|
|
{
|
|
|
|
return IWorldToAnimTimeNoUpdate(wSecs, IGetState(wSecs));
|
|
|
|
}
|
|
|
|
|
|
|
|
float plAnimTimeConvert::IWorldToAnimTimeNoUpdate(double wSecs, plATCState *state)
|
|
|
|
{
|
|
|
|
//hsAssert(wSecs >= fLastEvalWorldTime, "Tried to eval a time that's earlier than the last eval time.");
|
|
|
|
if (state == nil)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (state->fFlags & kStopped)
|
|
|
|
return state->fStartAnimTime;
|
|
|
|
|
|
|
|
float secs = 0, delSecs = 0;
|
|
|
|
|
|
|
|
if (state->fEaseCurve != nil)
|
|
|
|
{
|
|
|
|
delSecs += ICalcEaseTime(state->fEaseCurve, state->fStartWorldTime, wSecs);
|
|
|
|
if (wSecs > state->fEaseCurve->GetEndWorldTime())
|
|
|
|
{
|
|
|
|
if (state->fFlags & kEasingIn)
|
|
|
|
delSecs += float(wSecs - state->fEaseCurve->GetEndWorldTime()) * state->fSpeed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// The easy case... playing the animation at a constant speed.
|
|
|
|
delSecs = float(wSecs - state->fStartWorldTime) * state->fSpeed;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->fFlags & kBackwards)
|
|
|
|
delSecs = -delSecs;
|
|
|
|
|
|
|
|
secs = state->fStartAnimTime + delSecs;
|
|
|
|
// At this point, "secs" is the pre-wrapped (before looping) anim time.
|
|
|
|
// "delSecs" is the change in anim time
|
|
|
|
bool forewards = delSecs >= 0;
|
|
|
|
|
|
|
|
if (state->fFlags & kLoop)
|
|
|
|
{
|
|
|
|
bool wrapped = false;
|
|
|
|
|
|
|
|
if (forewards)
|
|
|
|
{
|
|
|
|
if (state->fStartAnimTime > state->fLoopEnd)
|
|
|
|
{
|
|
|
|
// Our animation started past the loop. Play to the end.
|
|
|
|
if (secs > state->fEnd)
|
|
|
|
{
|
|
|
|
secs = state->fEnd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (secs > state->fLoopEnd)
|
|
|
|
{
|
|
|
|
secs = fmodf(secs - state->fLoopBegin, state->fLoopEnd - state->fLoopBegin) + state->fLoopBegin;
|
|
|
|
wrapped = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (state->fStartAnimTime < state->fLoopBegin)
|
|
|
|
{
|
|
|
|
if (secs < state->fBegin)
|
|
|
|
{
|
|
|
|
secs = state->fBegin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (secs < state->fLoopBegin)
|
|
|
|
{
|
|
|
|
secs = state->fLoopEnd - fmodf(state->fLoopEnd - secs, state->fLoopEnd - state->fLoopBegin);
|
|
|
|
wrapped = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->fFlags & kWrap)
|
|
|
|
{
|
|
|
|
if (((wrapped && (forewards && secs >= state->fWrapTime)) ||
|
|
|
|
(!forewards && secs <= state->fWrapTime)) ||
|
|
|
|
(forewards && state->fStartAnimTime < state->fWrapTime && secs >= state->fWrapTime) ||
|
|
|
|
(!forewards && state->fStartAnimTime > state->fWrapTime && secs <= state->fWrapTime))
|
|
|
|
{
|
|
|
|
secs = state->fWrapTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((secs < state->fBegin) || (secs > state->fEnd))
|
|
|
|
{
|
|
|
|
secs = forewards ? state->fEnd : state->fBegin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return secs;
|
|
|
|
}
|
|
|
|
|
|
|
|
float plAnimTimeConvert::IWorldToAnimTimeBeforeState(double wSecs) const
|
|
|
|
{
|
|
|
|
return IWorldToAnimTimeNoUpdate(wSecs, IGetState(wSecs));
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::SetCurrentAnimTime(float s, bool jump /* = false */)
|
|
|
|
{
|
|
|
|
// We're setting the anim value for whenever we last evaluated.
|
|
|
|
fFlags |= kForcedMove;
|
|
|
|
if (!jump)
|
|
|
|
ICheckTimeCallbacks(fCurrentAnimTime, s);
|
|
|
|
fCurrentAnimTime = s;
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kSingleFrameAdjust)
|
|
|
|
{
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::SetEase(bool easeIn, uint8_t type, float minLength, float maxLength, float normLength)
|
|
|
|
{
|
|
|
|
if (easeIn)
|
|
|
|
{
|
|
|
|
delete fEaseInCurve;
|
|
|
|
fEaseInCurve = plATCEaseCurve::CreateEaseCurve(type, minLength, maxLength, normLength, 0, fSpeed);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete fEaseOutCurve;
|
|
|
|
fEaseOutCurve = plATCEaseCurve::CreateEaseCurve(type, minLength, maxLength, normLength, fSpeed, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float plAnimTimeConvert::GetBestStopDist(float min, float max, float norm, float time) const
|
|
|
|
{
|
|
|
|
float bestTime = -1;
|
|
|
|
float bestDist = -1;
|
|
|
|
if (fStopPoints.GetCount() == 0)
|
|
|
|
return norm;
|
|
|
|
|
|
|
|
float curTime;
|
|
|
|
float curDist;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < fStopPoints.GetCount(); i++)
|
|
|
|
{
|
|
|
|
float stop = fStopPoints.Get(i);
|
|
|
|
|
|
|
|
if (IsLooped())
|
|
|
|
{
|
|
|
|
float loopDist;
|
|
|
|
if (IsBackwards())
|
|
|
|
{
|
|
|
|
if ((time >= fLoopBegin && stop < fLoopBegin) ||
|
|
|
|
(time < fLoopBegin && stop > fLoopBegin))
|
|
|
|
continue;
|
|
|
|
loopDist = -(fLoopEnd - fLoopBegin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((time <= fLoopEnd && stop > fLoopEnd) ||
|
|
|
|
(time > fLoopEnd && stop < fLoopEnd))
|
|
|
|
continue; // we'll never reach it.
|
|
|
|
loopDist = fLoopEnd - fLoopBegin;
|
|
|
|
}
|
|
|
|
if (stop <= fLoopEnd && stop >= fLoopBegin)
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
curTime = stop - time;
|
|
|
|
if (IsBackwards())
|
|
|
|
curTime = -curTime;
|
|
|
|
|
|
|
|
if (curTime > max)
|
|
|
|
break;
|
|
|
|
|
|
|
|
curDist = curTime - norm;
|
|
|
|
if (curDist < 0)
|
|
|
|
curDist = -curDist;
|
|
|
|
|
|
|
|
if (curTime >= min && curTime <= max && (bestDist == -1 || bestDist > curDist))
|
|
|
|
{
|
|
|
|
bestDist = curDist;
|
|
|
|
bestTime = curTime;
|
|
|
|
}
|
|
|
|
stop += loopDist;
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
curTime = stop - time;
|
|
|
|
if (IsBackwards())
|
|
|
|
curTime = -curTime;
|
|
|
|
|
|
|
|
curDist = curTime - norm;
|
|
|
|
if (curDist < 0)
|
|
|
|
curDist = -curDist;
|
|
|
|
|
|
|
|
if (curTime >= min && curTime <= max && (bestDist == -1 || bestDist > curDist))
|
|
|
|
{
|
|
|
|
bestDist = curDist;
|
|
|
|
bestTime = curTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hsStatusMessageF("found stop point %f\n", bestTime);
|
|
|
|
|
|
|
|
if (bestTime == -1)
|
|
|
|
bestTime = norm;
|
|
|
|
return bestTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Passing in a rate of zero specifies an immediate change.
|
|
|
|
void plAnimTimeConvert::SetSpeed(float goal, float rate /* = 0 */)
|
|
|
|
{
|
|
|
|
float curSpeed = fSpeed;
|
|
|
|
fSpeed = goal;
|
|
|
|
|
|
|
|
|
|
|
|
if (rate == 0)
|
|
|
|
{
|
|
|
|
IClearSpeedEase();
|
|
|
|
fCurrentEaseCurve = nil;
|
|
|
|
|
|
|
|
}
|
|
|
|
// Skip if we're either stopped or stopping. We'll take the new speed into account next time we start up.
|
|
|
|
else if ((fFlags & kEasingIn))
|
|
|
|
{
|
|
|
|
double curTime = hsTimer::GetSysSeconds();
|
|
|
|
if (fCurrentEaseCurve != nil)
|
|
|
|
{
|
|
|
|
double easeTime = curTime - fCurrentEaseCurve->fBeginWorldTime;
|
|
|
|
curSpeed = fCurrentEaseCurve->VelocityGivenTime((float)easeTime);
|
|
|
|
}
|
|
|
|
if (fSpeedEaseCurve != nil)
|
|
|
|
{
|
|
|
|
fSpeedEaseCurve->RecalcToSpeed(curSpeed, goal);
|
|
|
|
fSpeedEaseCurve->SetLengthOnRate(rate);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float length;
|
|
|
|
length = (goal - curSpeed) / rate;
|
|
|
|
if (length < 0)
|
|
|
|
length = -length;
|
|
|
|
|
|
|
|
fSpeedEaseCurve = plATCEaseCurve::CreateEaseCurve(plAnimEaseTypes::kConstAccel, length, length, length,
|
|
|
|
curSpeed, goal);
|
|
|
|
}
|
|
|
|
|
|
|
|
fSpeedEaseCurve->fBeginWorldTime = curTime;
|
|
|
|
fCurrentEaseCurve = fSpeedEaseCurve;
|
|
|
|
}
|
|
|
|
|
|
|
|
IProcessStateChange(hsTimer::GetSysSeconds());
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::Read(hsStream* s, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plCreatable::Read(s, mgr);
|
|
|
|
|
|
|
|
fFlags = (uint16_t)(s->ReadLE32());
|
|
|
|
|
|
|
|
fBegin = fInitialBegin = s->ReadLEScalar();
|
|
|
|
fEnd = fInitialEnd = s->ReadLEScalar();
|
|
|
|
fLoopEnd = s->ReadLEScalar();
|
|
|
|
fLoopBegin = s->ReadLEScalar();
|
|
|
|
fSpeed = s->ReadLEScalar();
|
|
|
|
|
|
|
|
fEaseInCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s));
|
|
|
|
fEaseOutCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s));
|
|
|
|
fSpeedEaseCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s));
|
|
|
|
|
|
|
|
fCurrentAnimTime = s->ReadLEScalar();
|
|
|
|
fLastEvalWorldTime = s->ReadLEDouble();
|
|
|
|
|
|
|
|
// load other non-synched data;
|
|
|
|
int count = s->ReadLE32();
|
|
|
|
fCallbackMsgs.SetCountAndZero(count);
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
plEventCallbackMsg* msg = plEventCallbackMsg::ConvertNoRef(mgr->ReadCreatable(s));
|
|
|
|
fCallbackMsgs[i] = msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
count = s->ReadLE32();
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
fStopPoints.Append(s->ReadLEScalar());
|
|
|
|
}
|
|
|
|
IProcessStateChange(0, fBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::Write(hsStream* s, hsResMgr* mgr)
|
|
|
|
{
|
|
|
|
plCreatable::Write(s, mgr);
|
|
|
|
|
|
|
|
s->WriteLE32(fFlags);
|
|
|
|
|
|
|
|
s->WriteLEScalar(fBegin);
|
|
|
|
s->WriteLEScalar(fEnd);
|
|
|
|
s->WriteLEScalar(fLoopEnd);
|
|
|
|
s->WriteLEScalar(fLoopBegin);
|
|
|
|
s->WriteLEScalar(fSpeed);
|
|
|
|
|
|
|
|
mgr->WriteCreatable(s, fEaseInCurve);
|
|
|
|
mgr->WriteCreatable(s, fEaseOutCurve);
|
|
|
|
mgr->WriteCreatable(s, fSpeedEaseCurve);
|
|
|
|
|
|
|
|
s->WriteLEScalar(fCurrentAnimTime);
|
|
|
|
s->WriteLEDouble(fLastEvalWorldTime);
|
|
|
|
|
|
|
|
// save out other non-synched important data
|
|
|
|
s->WriteLE32(fCallbackMsgs.Count());
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < fCallbackMsgs.Count(); i++)
|
|
|
|
mgr->WriteCreatable(s, fCallbackMsgs[i]);
|
|
|
|
|
|
|
|
s->WriteLE32(fStopPoints.GetCount());
|
|
|
|
for (i = 0; i < fStopPoints.GetCount(); i++)
|
|
|
|
{
|
|
|
|
s->WriteLEScalar(fStopPoints.Get(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::InitStop()
|
|
|
|
{
|
|
|
|
return IStop(hsTimer::GetSysSeconds(), fCurrentAnimTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Stop(bool on)
|
|
|
|
{
|
|
|
|
if( on )
|
|
|
|
return Stop();
|
|
|
|
else
|
|
|
|
return Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Stop(double stopTime)
|
|
|
|
{
|
|
|
|
if( IsStopped() || (fEaseOutCurve != nil && !(fFlags & kEasingIn)) )
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
if (stopTime < 0)
|
|
|
|
stopTime = hsTimer::GetSysSeconds();
|
|
|
|
float stopAnimTime = WorldToAnimTimeNoUpdate(stopTime);
|
|
|
|
|
|
|
|
SetFlag(kEasingIn, false);
|
|
|
|
|
|
|
|
if( fEaseOutCurve == nil )
|
|
|
|
{
|
|
|
|
return IStop(stopTime, fCurrentAnimTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
float currSpeed;
|
|
|
|
if (fCurrentEaseCurve == nil || stopTime >= fCurrentEaseCurve->GetEndWorldTime())
|
|
|
|
currSpeed = fSpeed;
|
|
|
|
else
|
|
|
|
currSpeed = fCurrentEaseCurve->VelocityGivenTime((float)(stopTime - fCurrentEaseCurve->fBeginWorldTime));
|
|
|
|
|
|
|
|
fEaseOutCurve->RecalcToSpeed(currSpeed > fSpeed ? currSpeed : fSpeed, 0);
|
|
|
|
fEaseOutCurve->SetLengthOnDistance(GetBestStopDist(fEaseOutCurve->GetMinDistance(), fEaseOutCurve->GetMaxDistance(),
|
|
|
|
fEaseOutCurve->GetNormDistance(), stopAnimTime));
|
|
|
|
fEaseOutCurve->fBeginWorldTime = stopTime - fEaseOutCurve->TimeGivenVelocity(currSpeed);
|
|
|
|
|
|
|
|
fCurrentEaseCurve = fEaseOutCurve;
|
|
|
|
|
|
|
|
return IProcessStateChange(stopTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Start(double startTime)
|
|
|
|
{
|
|
|
|
// If start has not been called since the last stop, kEasingIn will not be set
|
|
|
|
if( (fFlags & kEasingIn) && (startTime == fLastStateChange) )
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
SetFlag(kEasingIn, true);
|
|
|
|
|
|
|
|
if (startTime < 0)
|
|
|
|
startTime = hsTimer::GetSysSeconds();
|
|
|
|
|
|
|
|
if (fEaseInCurve != nil)
|
|
|
|
{
|
|
|
|
float currSpeed;
|
|
|
|
if (fCurrentEaseCurve == nil || startTime >= fCurrentEaseCurve->GetEndWorldTime())
|
|
|
|
currSpeed = 0;
|
|
|
|
else
|
|
|
|
currSpeed = fCurrentEaseCurve->VelocityGivenTime((float)(startTime - fCurrentEaseCurve->fBeginWorldTime));
|
|
|
|
|
|
|
|
if (currSpeed <= fSpeed)
|
|
|
|
{
|
|
|
|
fEaseInCurve->RecalcToSpeed(0, fSpeed);
|
|
|
|
fEaseInCurve->fBeginWorldTime = startTime - fEaseInCurve->TimeGivenVelocity(currSpeed);
|
|
|
|
|
|
|
|
fCurrentEaseCurve = fEaseInCurve;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ // We eased out in the middle of a speed change, but were told to start again before slowing past
|
|
|
|
// the target speed, so the "ease in" is really a slow down.
|
|
|
|
SetSpeed(fSpeed, fEaseInCurve->fSpeed / fEaseInCurve->fLength);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for a start callback
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kStart)
|
|
|
|
{
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetFlag(kStopped, false);
|
|
|
|
if (fFlags & kBackwards)
|
|
|
|
{
|
|
|
|
if (fCurrentAnimTime == fBegin)
|
|
|
|
return IProcessStateChange(startTime, fEnd);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fCurrentAnimTime == fEnd)
|
|
|
|
return IProcessStateChange(startTime, fBegin);
|
|
|
|
}
|
|
|
|
return IProcessStateChange(startTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Backwards(bool on)
|
|
|
|
{
|
|
|
|
return on ? Backwards() : Forewards();
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Backwards()
|
|
|
|
{
|
|
|
|
if( IsBackwards() )
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
// check for a reverse callback
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kReverse)
|
|
|
|
{
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetFlag(kBackwards, true);
|
|
|
|
|
|
|
|
// Record state changes
|
|
|
|
IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime);
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Forewards()
|
|
|
|
{
|
|
|
|
if( !IsBackwards() )
|
|
|
|
return *this;
|
|
|
|
|
|
|
|
// check for a reverse callback
|
|
|
|
int i;
|
|
|
|
for( i = fCallbackMsgs.GetCount()-1; i >= 0; --i )
|
|
|
|
{
|
|
|
|
if (fCallbackMsgs[i]->fEvent == kReverse)
|
|
|
|
{
|
|
|
|
ISendCallback(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetFlag(kBackwards, false);
|
|
|
|
|
|
|
|
// Record state changes
|
|
|
|
IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime);
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::Loop(bool on)
|
|
|
|
{
|
|
|
|
SetFlag(kLoop, on);
|
|
|
|
|
|
|
|
// Record state changes
|
|
|
|
IProcessStateChange(hsTimer::GetSysSeconds(), fCurrentAnimTime);
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::PlayToTime(float time)
|
|
|
|
{
|
|
|
|
fFlags |= kNeedsReset;
|
|
|
|
if (fCurrentAnimTime > time)
|
|
|
|
{
|
|
|
|
if (fFlags & kLoop)
|
|
|
|
{
|
|
|
|
fWrapTime = time;
|
|
|
|
fFlags |= kWrap;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fBegin = time;
|
|
|
|
Backwards();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fEnd = time;
|
|
|
|
}
|
|
|
|
Start();
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
plAnimTimeConvert& plAnimTimeConvert::PlayToPercentage(float percent)
|
|
|
|
{
|
|
|
|
return PlayToTime(fBegin + (fEnd - fBegin) * percent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::RemoveCallback(plEventCallbackMsg* pMsg)
|
|
|
|
{
|
|
|
|
int idx = fCallbackMsgs.Find(pMsg);
|
|
|
|
if( idx != fCallbackMsgs.kMissingIndex )
|
|
|
|
{
|
|
|
|
hsRefCnt_SafeUnRef(fCallbackMsgs[idx]);
|
|
|
|
fCallbackMsgs.Remove(idx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool plAnimTimeConvert::HandleCmd(plAnimCmdMsg* modMsg)
|
|
|
|
{
|
|
|
|
if (fFlags & kNeedsReset)
|
|
|
|
ResetWrap();
|
|
|
|
|
|
|
|
// The net msg screener is already checking for callbacks,
|
|
|
|
// I'm just being extra safe.
|
|
|
|
if (!modMsg->HasBCastFlag(plMessage::kNetCreatedRemotely))
|
|
|
|
{
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kAddCallbacks) )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < modMsg->GetNumCallbacks(); i++ )
|
|
|
|
{
|
|
|
|
AddCallback(plEventCallbackMsg::ConvertNoRef(modMsg->GetEventCallback(i)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kRemoveCallbacks) )
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for( i = 0; i < modMsg->GetNumCallbacks(); i++ )
|
|
|
|
{
|
|
|
|
RemoveCallback(modMsg->GetEventCallback(i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kSetBackwards) )
|
|
|
|
{
|
|
|
|
Backwards();
|
|
|
|
}
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kSetForewards) )
|
|
|
|
{
|
|
|
|
Forewards();
|
|
|
|
}
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kStop) )
|
|
|
|
Stop();
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kSetLooping) )
|
|
|
|
Loop();
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kUnSetLooping) )
|
|
|
|
NoLoop();
|
|
|
|
|
|
|
|
if (modMsg->Cmd(plAnimCmdMsg::kSetBegin))
|
|
|
|
{
|
|
|
|
if (modMsg->fBegin >= fInitialBegin)
|
|
|
|
SetBegin(modMsg->fBegin);
|
|
|
|
else
|
|
|
|
SetBegin(fInitialBegin);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modMsg->Cmd(plAnimCmdMsg::kSetEnd))
|
|
|
|
{
|
|
|
|
if (modMsg->fEnd <= fInitialEnd)
|
|
|
|
SetEnd(modMsg->fEnd);
|
|
|
|
else
|
|
|
|
SetEnd(fInitialEnd);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fBegin > fEnd)
|
|
|
|
{
|
|
|
|
fBegin = fInitialBegin;
|
|
|
|
fEnd = fInitialEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kSetLoopEnd) )
|
|
|
|
SetLoopEnd(modMsg->fLoopEnd);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kSetLoopBegin) )
|
|
|
|
SetLoopBegin(modMsg->fLoopBegin);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kSetSpeed) )
|
|
|
|
SetSpeed(modMsg->fSpeed, modMsg->fSpeedChangeRate);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kGoToTime) )
|
|
|
|
{
|
|
|
|
if (modMsg->fTime < fBegin)
|
|
|
|
SetCurrentAnimTime(fBegin, true);
|
|
|
|
else if (modMsg->fTime > fEnd)
|
|
|
|
SetCurrentAnimTime(fEnd, true);
|
|
|
|
else
|
|
|
|
SetCurrentAnimTime(modMsg->fTime, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( modMsg->Cmd(plAnimCmdMsg::kGoToPercent) )
|
|
|
|
SetCurrentAnimTime(fBegin + (fEnd - fBegin) * modMsg->fTime);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kGoToBegin) )
|
|
|
|
SetCurrentAnimTime(fBegin, true);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kGoToEnd) )
|
|
|
|
SetCurrentAnimTime(fEnd, true);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kGoToLoopBegin) )
|
|
|
|
SetCurrentAnimTime(fLoopBegin, true);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kGoToLoopEnd) )
|
|
|
|
SetCurrentAnimTime(fLoopEnd, true);
|
|
|
|
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kToggleState) )
|
|
|
|
{
|
|
|
|
if( IsStopped() )
|
|
|
|
{
|
|
|
|
Start();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kContinue) )
|
|
|
|
{
|
|
|
|
Start();
|
|
|
|
}
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kIncrementForward) )
|
|
|
|
{
|
|
|
|
if (fCurrentAnimTime == fEnd)
|
|
|
|
return true;
|
|
|
|
double currTime = hsTimer::GetSysSeconds();
|
|
|
|
float newTime = fCurrentAnimTime + hsTimer::GetDelSysSeconds();
|
|
|
|
if (newTime > fEnd)
|
|
|
|
{
|
|
|
|
newTime = fEnd;
|
|
|
|
}
|
|
|
|
Forewards();
|
|
|
|
SetCurrentAnimTime(newTime);
|
|
|
|
}
|
|
|
|
if( modMsg->Cmd(plAnimCmdMsg::kIncrementBackward) )
|
|
|
|
{
|
|
|
|
if (fCurrentAnimTime == fBegin)
|
|
|
|
return true;
|
|
|
|
double currTime = hsTimer::GetSysSeconds();
|
|
|
|
float newTime = fCurrentAnimTime - hsTimer::GetDelSysSeconds();
|
|
|
|
if (newTime < fBegin)
|
|
|
|
{
|
|
|
|
newTime = fBegin;
|
|
|
|
}
|
|
|
|
Backwards();
|
|
|
|
SetCurrentAnimTime(newTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (modMsg->Cmd(plAnimCmdMsg::kPlayToTime))
|
|
|
|
PlayToTime(modMsg->fTime);
|
|
|
|
|
|
|
|
if (modMsg->Cmd(plAnimCmdMsg::kPlayToPercentage))
|
|
|
|
PlayToPercentage(modMsg->fTime);
|
|
|
|
|
|
|
|
// Basically, simulate what would happen if we played the animation
|
|
|
|
if (modMsg->Cmd(plAnimCmdMsg::kFastForward))
|
|
|
|
{
|
|
|
|
if (IsForewards())
|
|
|
|
SetCurrentAnimTime(fEnd, true);
|
|
|
|
else
|
|
|
|
SetCurrentAnimTime(fBegin, true);
|
|
|
|
// but if it should continue to play, statr it.
|
|
|
|
if (IsLooped())
|
|
|
|
Start();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::AddCallback(plEventCallbackMsg* pMsg)
|
|
|
|
{
|
|
|
|
hsRefCnt_SafeRef(pMsg);
|
|
|
|
fCallbackMsgs.Append(pMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::ClearCallbacks()
|
|
|
|
{
|
|
|
|
for (int i = 0; i<fCallbackMsgs.Count(); i++)
|
|
|
|
{
|
|
|
|
hsRefCnt_SafeUnRef(fCallbackMsgs[i]);
|
|
|
|
}
|
|
|
|
fCallbackMsgs.Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void plAnimTimeConvert::EnableCallbacks(bool val)
|
|
|
|
{
|
|
|
|
SetFlag(kNoCallbacks, !val);
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plATCState::Read(hsStream *s, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
fStartWorldTime = s->ReadLEDouble();
|
|
|
|
fStartAnimTime = s->ReadLEScalar();
|
|
|
|
|
|
|
|
fFlags = (uint8_t)(s->ReadLE32());
|
|
|
|
fEnd = s->ReadLEScalar();
|
|
|
|
fLoopBegin = s->ReadLEScalar();
|
|
|
|
fLoopEnd = s->ReadLEScalar();
|
|
|
|
fSpeed = s->ReadLEScalar();
|
|
|
|
fWrapTime = s->ReadLEScalar();
|
|
|
|
if (s->ReadBool())
|
|
|
|
fEaseCurve = plATCEaseCurve::ConvertNoRef(mgr->ReadCreatable(s));
|
|
|
|
}
|
|
|
|
|
|
|
|
void plATCState::Write(hsStream *s, hsResMgr *mgr)
|
|
|
|
{
|
|
|
|
s->WriteLEDouble(fStartWorldTime);
|
|
|
|
s->WriteLEScalar(fStartAnimTime);
|
|
|
|
|
|
|
|
s->WriteLE32(fFlags);
|
|
|
|
s->WriteLEScalar(fEnd);
|
|
|
|
s->WriteLEScalar(fLoopBegin);
|
|
|
|
s->WriteLEScalar(fLoopEnd);
|
|
|
|
s->WriteLEScalar(fSpeed);
|
|
|
|
s->WriteLEScalar(fWrapTime);
|
|
|
|
if (fEaseCurve != nil)
|
|
|
|
{
|
|
|
|
s->WriteBool(true);
|
|
|
|
mgr->WriteCreatable(s, fEaseCurve);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
s->WriteBool(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|