/* 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 event_play(&AnimationTrackState::internalPlaySignal, "PlayAnimation", "gameTime", "fadeTime", "weight", "speed" , Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST); static Reflection::RemoteEventDesc event_stop(&AnimationTrackState::internalStopSignal, "StopAnimation", "gameTime", "fadeTime", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST); static Reflection::RemoteEventDesc event_adjustWeight(&AnimationTrackState::internalAdjustWeightSignal, "AdjustAnimationWeight", "gameTime", "weight", "fadeTime", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST); static Reflection::RemoteEventDesc event_adjustSpeed(&AnimationTrackState::internalAdjustSpeedSignal, "AdjustAnimation", "gameTime", "speed", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST); static Reflection::RemoteEventDesc event_Keyframe(&AnimationTrackState::keyframeReachedSignal, "KeyframeReached", "keyframeName", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST); static Reflection::RemoteEventDesc event_Stopped(&AnimationTrackState::stoppedSignal, "Stopped", Security::None, Reflection::RemoteEventCommon::REPLICATE_ONLY, Reflection::RemoteEventCommon::BROADCAST); REFLECTION_END(); AnimationTrackState::AnimationTrackState(shared_ptr keyframeSequence, weak_ptr animator) : DescribedNonCreatable() , 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 animationTrackIn) { animationTrack = animationTrackIn; } bool AnimationTrackState::isStopped(double time) { return (time >= fadeEndTime) && G3D::fuzzyEq(fadeEndWeight,0); } double AnimationTrackState::getGameTime() { if(shared_ptr 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& child, double minKeyframeTime, double maxKeyframeTime) { Keyframe* kf = Instance::fastDynamicCast(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& 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(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; } } } }