Files
2025-09-18 17:55:52 -04:00

376 lines
11 KiB
C++

/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */
#include "stdafx.h"
#include "V8DataModel/AnimationTrackState.h"
#include "V8DataModel/Animation.h"
#include "V8DataModel/Animator.h"
#include "V8DataModel/KeyframeSequence.h"
#include "V8Tree/Service.h"
#include "Util/RunStateOwner.h"
#include "V8DataModel/Animation.h"
#include "V8datamodel/KeyframeSequenceProvider.h"
namespace RBX {
const char* const sAnimationTrackState = "AnimationTrackState";
static const float autoStopFadeTime = 0.3f;
REFLECTION_BEGIN();
static Reflection::RemoteEventDesc<AnimationTrackState, void(float,float,float, float)> event_play(&AnimationTrackState::internalPlaySignal, "PlayAnimation", "gameTime", "fadeTime", "weight", "speed" , Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(float,float)> event_stop(&AnimationTrackState::internalStopSignal, "StopAnimation", "gameTime", "fadeTime", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(float,float,float)> event_adjustWeight(&AnimationTrackState::internalAdjustWeightSignal, "AdjustAnimationWeight", "gameTime", "weight", "fadeTime", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(float,float)> event_adjustSpeed(&AnimationTrackState::internalAdjustSpeedSignal, "AdjustAnimation", "gameTime", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void(std::string)> event_Keyframe(&AnimationTrackState::keyframeReachedSignal, "KeyframeReached", "keyframeName", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
static Reflection::RemoteEventDesc<AnimationTrackState, void()> event_Stopped(&AnimationTrackState::stoppedSignal, "Stopped", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST);
REFLECTION_END();
AnimationTrackState::AnimationTrackState(shared_ptr<const KeyframeSequence> keyframeSequence, weak_ptr<const Animator> animator)
: DescribedNonCreatable<AnimationTrackState, Instance, sAnimationTrackState>()
, startTime(0.0f)
, fadeStartTime(0.0f)
, fadeStartWeight(0.0f)
, fadeEndTime(0.0f)
, fadeEndWeight(0.0f)
, speed(1.0f)
, phase(0.0f)
, keyframeSequence(keyframeSequence)
, animator(animator)
, preKeyframe(-1)
, isPlaying(false)
, priority(KeyframeSequence::CORE)
, priorityOverridden(false)
{
setName(sAnimationTrackState);
internalPlaySignal.connect(boost::bind(&AnimationTrackState::onPlay, this, _1, _2, _3, _4));
internalStopSignal.connect(boost::bind(&AnimationTrackState::onStop, this, _1, _2));
internalAdjustWeightSignal.connect(boost::bind(&AnimationTrackState::onAdjustWeight, this, _1, _2, _3));
internalAdjustSpeedSignal.connect(boost::bind(&AnimationTrackState::onAdjustSpeed, this, _1, _2));
lockParent();
}
void AnimationTrackState::setAnimationTrack(shared_ptr<AnimationTrack> animationTrackIn)
{
animationTrack = animationTrackIn;
}
bool AnimationTrackState::isStopped(double time)
{
return (time >= fadeEndTime) && G3D::fuzzyEq(fadeEndWeight,0);
}
double AnimationTrackState::getGameTime()
{
if(shared_ptr<const Animator> safeAnimator = animator.lock()){
return safeAnimator->getGameTime();
}
//We've lost our animator, shut.it.down
keyframeSequence.reset();
return 0.0f;
}
double AnimationTrackState::getWeightAtTime(double time)
{
if(time >= fadeEndTime)
{
return fadeEndWeight;
}
else if(time <= fadeStartTime)
{
return fadeStartWeight;
}
else
{
// lerp between two.
double interval = (fadeEndTime - fadeStartTime);
return ((time - fadeStartTime)/ interval) * fadeEndWeight +
((fadeEndTime - time) / interval) * fadeStartWeight;
}
}
double AnimationTrackState::getKeyframeAtTime(double time)
{
if (!inReverse())
{
return (time - startTime) * speed + phase;
}
else
{
return (getDuration() - (time - startTime) * -speed) + phase;
}
}
float AnimationTrackState::getDuration()
{
if (const KeyframeSequence *pSequence = getKeyframeSequence())
{
return pSequence->getDuration();
}
return 0.0f;
}
KeyframeSequence::Priority AnimationTrackState::getPriority() const
{
if (priorityOverridden) {
return priority;
}
else
{
if (const KeyframeSequence* sequence = getKeyframeSequence())
{
return sequence->getPriority();
}
else
{
return KeyframeSequence::CORE;
}
}
}
void AnimationTrackState::setPriority(KeyframeSequence::Priority priorityIn)
{
priorityOverridden = true;
priority = priorityIn;
}
void AnimationTrackState::setKeyframeAtTime(double gameTime, double keyframetime)
{
double delta = keyframetime - getKeyframeAtTime(gameTime);
phase += delta;
}
void AnimationTrackState::onPlay(float gameTime, float fadeTime, float weight, float speed)
{
if(gameTime >= startTime){
phase = 0;
startTime = gameTime;
fadeStartTime = startTime;
fadeStartWeight = 0.0;
fadeEndTime = startTime + fadeTime;
fadeEndWeight = weight;
this->speed = speed;
lastKeyframeTime = 0;
preKeyframe = -1;
isPlaying = true;
//automated animation detection does not detect the first keyframe if the animation is running in reverse
if (inReverse())
{
const KeyframeSequence* kfs = getKeyframeSequence();
if (kfs->numChildren() > 0)
{
if (kfs->getChild(kfs->numChildren() - 1))
{
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
}
}
}
}
}
void AnimationTrackState::play(float fadeTime, float weight, float speed)
{
event_play.fireAndReplicateEvent(this, (float)getGameTime(), fadeTime, weight, speed);
}
void AnimationTrackState::onStop(float gameTime, float fadeTime)
{
fadeStartTime = gameTime;
fadeStartWeight = getWeightAtTime(gameTime);
fadeEndTime = gameTime + fadeTime;
fadeEndWeight = 0.0;
event_Stopped.fireAndReplicateEvent(this);
isPlaying = false;
}
void AnimationTrackState::stop(float fadeTime)
{
event_stop.fireAndReplicateEvent(this, (float)getGameTime(), fadeTime);
}
void AnimationTrackState::onAdjustWeight(float gameTime, float weight, float fadeTime)
{
fadeStartTime = gameTime;
fadeStartWeight = getWeightAtTime(gameTime);
fadeEndTime = gameTime + fadeTime;
fadeEndWeight = weight;
}
void AnimationTrackState::onAdjustSpeed(float gameTime, float speed)
{
if(speed != this->speed)
{
//if we reversed directions, we need to reset the preKeyframe
if ( (this->speed >= 0 && speed < 0) || (this->speed < 0 && speed >= 0) )
{
preKeyframe = -1;
}
// must adjust phase to prevent skipping.
double keyframetime = getKeyframeAtTime(gameTime);
this->speed = speed;
setKeyframeAtTime(gameTime, keyframetime);
}
}
void AnimationTrackState::adjustWeight(float weight, float fadeTime)
{
event_adjustWeight.fireAndReplicateEvent(this, (float)getGameTime(), weight, fadeTime);
}
void AnimationTrackState::adjustSpeed(float speed)
{
event_adjustSpeed.fireAndReplicateEvent(this, (float)getGameTime(), speed);
}
void AnimationTrackState::triggerKeyframeReachedSignal(const shared_ptr<Instance>& child, double minKeyframeTime, double maxKeyframeTime)
{
Keyframe* kf = Instance::fastDynamicCast<Keyframe>(child.get());
if(kf)
{
if(kf->getTime() > minKeyframeTime && kf->getTime() <= maxKeyframeTime)
{
event_Keyframe.fireAndReplicateEvent(this, kf->getName());
}
}
}
bool AnimationTrackState::inReverse()
{
return (speed < 0);
}
double AnimationTrackState::getDurationClampedKeyframeTime(double keyframeTime)
{
double duration = getDuration();
if (duration > 0.0)
{
if (keyframeTime < 0)
{
int durationsOff = (int)(fabs(keyframeTime) / duration) + 1;
keyframeTime = keyframeTime + durationsOff * duration;
}
else if (keyframeTime > duration)
{
int durationsOff = (int)(keyframeTime / duration);
keyframeTime = keyframeTime - durationsOff * duration;
}
}
return keyframeTime;
}
void AnimationTrackState::resetKeyframeReachedDetection(double keyframeTime)
{
lastKeyframeTime = keyframeTime;
preKeyframe = -1;
}
void AnimationTrackState::step(std::vector<PoseAccumulator>& jointposes, double time)
{
double keyframetime = getKeyframeAtTime(time);
float trackweight = (float)getWeightAtTime(time);
const KeyframeSequence* kfs = getKeyframeSequence();
double normKeyframeTime = keyframetime;
float duration = getKeyframeSequence()->getDuration();
kfs->apply(jointposes, lastKeyframeTime, keyframetime, trackweight);
if(!kfs->getLoop()) // play one time. give stop command after trigger of last keyframe.
{
//determine if we are finished with the animation
bool doneWithAnimation;
if (!inReverse())
doneWithAnimation = (lastKeyframeTime < duration && keyframetime >= duration);
else
doneWithAnimation = (lastKeyframeTime > 0 && keyframetime <= 0);
if (duration <= 0)
doneWithAnimation = false;
if(doneWithAnimation)
{
// fire signal for the last frame
if (!inReverse())
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren()-1)->getName());
else
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
// should we use the internal event? or the external one?
// we will play it safe and use the internal one. it is easier to notice a problem with a non-stopped animation than with a
// double fire.
onStop((float)time, autoStopFadeTime);
}
}
else
{
//find out where in the looping animation we are -- even if we're moving backwards
normKeyframeTime = getDurationClampedKeyframeTime(keyframetime);
}
detectKeyframeReached(normKeyframeTime, getDurationClampedKeyframeTime(lastKeyframeTime));
lastKeyframeTime = keyframetime;
}
void AnimationTrackState::detectKeyframeReached(double animationTime, double lastAnimationTime)
{
if (!isPlaying)
return;
const KeyframeSequence* kfs = getKeyframeSequence();
int numKf = kfs->numChildren();
if (numKf == 0)
return;
//did we just loop? if so, fire the keyframes relevant. of course, only if the animation does loop
if (kfs->getLoop())
{
if (!inReverse())
{
if (lastAnimationTime > animationTime)
{
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
}
}
else
{
if (animationTime > lastAnimationTime)
{
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(0)->getName());
event_Keyframe.fireAndReplicateEvent(this, kfs->getChild(kfs->numChildren() - 1)->getName());
}
}
}
//now we need to check the midway keyframes
for (int index = 0; index < numKf; index++)
{
const Keyframe* keyframe = Instance::fastDynamicCast<const Keyframe>(kfs->getChild(index));
RBXASSERT(keyframe);
bool passedKeyframe;
if (!inReverse())
passedKeyframe = (lastAnimationTime <= keyframe->getTime() && keyframe->getTime() < animationTime);
else
passedKeyframe = (lastAnimationTime >= keyframe->getTime() && keyframe->getTime() > animationTime);
if (passedKeyframe)
{
if (preKeyframe == index)
continue;
event_Keyframe.fireAndReplicateEvent(this, keyframe->getName());
preKeyframe = index;
break;
}
}
}
}