bool AnimationClip::update(float elapsedTime) { if (isClipStateBitSet(CLIP_IS_PAUSED_BIT)) { return false; } if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT)) { // If the marked for removal bit is set, it means stop() was called on the AnimationClip at some point // after the last update call. Reset the flag, and return true so the AnimationClip is removed from the // running clips on the AnimationController. onEnd(); return true; } if (!isClipStateBitSet(CLIP_IS_STARTED_BIT)) { // Clip is just starting onBegin(); } else { // Clip was already running _elapsedTime += elapsedTime * _speed; if (_repeatCount == REPEAT_INDEFINITE && _elapsedTime <= 0) { // Elapsed time is moving backwards, so wrap it back around the end when it falls below zero _elapsedTime = _activeDuration + _elapsedTime; // TODO: account for _loopBlendTime } } // Current time within a loop of the clip float currentTime = 0.0f; // Check to see if clip is complete. if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0.0f))) { // We finished our active duration (including repeats), so clamp to our end value. resetClipStateBit(CLIP_IS_STARTED_BIT); // Ensure we end off at the endpoints of our clip (-speed==0, +speed==_duration) currentTime = _speed < 0.0f ? 0.0f : _duration; } else { // If _duration == 0, we have a "pose". Just set currentTime to 0. if (_duration == 0) { currentTime = 0.0f; } else { // Animation is running normally. currentTime = fmodf(_elapsedTime, _duration + _loopBlendTime); } } // Notify any listeners of Animation events. if (_listeners) { GP_ASSERT(_listenerItr); if (_speed >= 0.0f) { while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime) { GP_ASSERT(_listenerItr); GP_ASSERT(**_listenerItr); GP_ASSERT((**_listenerItr)->_listener); (**_listenerItr)->_listener->animationEvent(this, Listener::TIME); ++*_listenerItr; } } else { while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime) { GP_ASSERT(_listenerItr); GP_ASSERT(**_listenerItr); GP_ASSERT((**_listenerItr)->_listener); (**_listenerItr)->_listener->animationEvent(this, Listener::TIME); --*_listenerItr; } } } // Add back in start time, and divide by the total animation's duration to get the actual percentage complete GP_ASSERT(_animation); // Compute percentage complete for the current loop (prevent a divide by zero if _duration==0). // Note that we don't use (currentTime/(_duration+_loopBlendTime)). That's because we want a // % value that is outside the 0-1 range for loop smoothing/blending purposes. float percentComplete = _duration == 0 ? 1 : currentTime / (float)_duration; if (_loopBlendTime == 0.0f) percentComplete = MATH_CLAMP(percentComplete, 0.0f, 1.0f); // If we're cross fading, compute blend weights if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT)) { GP_ASSERT(_crossFadeToClip); GP_ASSERT(_crossFadeOutDuration > 0); if (isClipStateBitSet(CLIP_IS_FADING_OUT_STARTED_BIT)) // Calculate elapsed time since the fade out begin. { GP_ASSERT(_crossFadeToClip); _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * fabs(_speed); resetClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT); } else { // continue tracking elapsed time. _crossFadeOutElapsed += elapsedTime * fabs(_speed); } if (_crossFadeOutElapsed < _crossFadeOutDuration) { // Calculate this clip's blend weight. float tempBlendWeight = ((float)_crossFadeOutDuration - _crossFadeOutElapsed) / (float)_crossFadeOutDuration; // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight if (isClipStateBitSet(CLIP_IS_FADING_IN_BIT)) { _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight) * _blendWeight; _blendWeight -= _crossFadeToClip->_blendWeight; } else { // Just set the blend weight. _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight); _blendWeight = tempBlendWeight; } } else { // Fade is done. _crossFadeToClip->_blendWeight = 1.0f; _blendWeight = 0.0f; resetClipStateBit(CLIP_IS_STARTED_BIT); resetClipStateBit(CLIP_IS_FADING_OUT_BIT); _crossFadeToClip->resetClipStateBit(CLIP_IS_FADING_IN_BIT); SAFE_RELEASE(_crossFadeToClip); } } // Evaluate this clip. Animation::Channel* channel = NULL; AnimationValue* value = NULL; AnimationTarget* target = NULL; size_t channelCount = _animation->_channels.size(); float percentageStart = (float)_startTime / (float)_animation->_duration; float percentageEnd = (float)_endTime / (float)_animation->_duration; float percentageBlend = (float)_loopBlendTime / (float)_animation->_duration; for (size_t i = 0; i < channelCount; i++) { channel = _animation->_channels[i]; GP_ASSERT(channel); target = channel->_target; GP_ASSERT(target); value = _values[i]; GP_ASSERT(value); // Evaluate the point on Curve GP_ASSERT(channel->getCurve()); channel->getCurve()->evaluate(percentComplete, percentageStart, percentageEnd, percentageBlend, value->_value); // Set the animation value on the target property. target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight); } // When ended. Probably should move to it's own method so we can call it when the clip is ended early. if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT) || !isClipStateBitSet(CLIP_IS_STARTED_BIT)) { onEnd(); return true; } return false; }
bool AnimationClip::update(float elapsedTime) { if (isClipStateBitSet(CLIP_IS_PAUSED_BIT)) { return false; } else if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT)) { // If the marked for removal bit is set, it means stop() was called on the AnimationClip at some point // after the last update call. Reset the flag, and return true so the AnimationClip is removed from the // running clips on the AnimationController. onEnd(); return true; } else if (!isClipStateBitSet(CLIP_IS_STARTED_BIT)) { onBegin(); } else { _elapsedTime += elapsedTime * _speed; if (_repeatCount == REPEAT_INDEFINITE && _elapsedTime <= 0) _elapsedTime = _activeDuration + _elapsedTime; } float currentTime = 0.0f; // Check to see if clip is complete. if (_repeatCount != REPEAT_INDEFINITE && ((_speed >= 0.0f && _elapsedTime >= _activeDuration) || (_speed <= 0.0f && _elapsedTime <= 0.0f))) { resetClipStateBit(CLIP_IS_STARTED_BIT); if (_speed >= 0.0f) { // If _duration == 0, we have a "pose". Just set currentTime to 0. if (_duration == 0) { currentTime = 0.0f; } else { currentTime = (float)(_activeDuration % _duration); // Get's the fractional part of the final repeat. if (currentTime == 0.0f) currentTime = _duration; } } else { currentTime = 0.0f; // If we are negative speed, the end value should be 0. } } else { // If _duration == 0, we have a "pose". Just set currentTime to 0. if (_duration == 0) currentTime = 0.0f; else // Gets portion/fraction of the repeat. currentTime = fmodf(_elapsedTime, _duration); } // Notify any listeners of Animation events. if (_listeners) { GP_ASSERT(_listenerItr); if (_speed >= 0.0f) { while (*_listenerItr != _listeners->end() && _elapsedTime >= (long) (**_listenerItr)->_eventTime) { GP_ASSERT(_listenerItr); GP_ASSERT(**_listenerItr); GP_ASSERT((**_listenerItr)->_listener); (**_listenerItr)->_listener->animationEvent(this, Listener::TIME); ++*_listenerItr; } } else { while (*_listenerItr != _listeners->begin() && _elapsedTime <= (long) (**_listenerItr)->_eventTime) { GP_ASSERT(_listenerItr); GP_ASSERT(**_listenerItr); GP_ASSERT((**_listenerItr)->_listener); (**_listenerItr)->_listener->animationEvent(this, Listener::TIME); --*_listenerItr; } } } // Add back in start time, and divide by the total animation's duration to get the actual percentage complete GP_ASSERT(_animation); // If the animation duration is zero (start time == end time, such as when there is only a single keyframe), // then prevent a divide by zero and set percentComplete = 1. float percentComplete = _animation->_duration == 0 ? 1 : ((float)_startTime + currentTime) / (float)_animation->_duration; percentComplete = MATH_CLAMP(percentComplete, 0.0f, 1.0f); if (isClipStateBitSet(CLIP_IS_FADING_OUT_BIT)) { GP_ASSERT(_crossFadeToClip); GP_ASSERT(_crossFadeOutDuration > 0); if (isClipStateBitSet(CLIP_IS_FADING_OUT_STARTED_BIT)) // Calculate elapsed time since the fade out begin. { GP_ASSERT(_crossFadeToClip); _crossFadeOutElapsed = (Game::getGameTime() - _crossFadeToClip->_timeStarted) * fabs(_speed); resetClipStateBit(CLIP_IS_FADING_OUT_STARTED_BIT); } else { // continue tracking elapsed time. _crossFadeOutElapsed += elapsedTime * fabs(_speed); } if (_crossFadeOutElapsed < _crossFadeOutDuration) { // Calculate this clip's blend weight. float tempBlendWeight = ((float)_crossFadeOutDuration - _crossFadeOutElapsed) / (float)_crossFadeOutDuration; // If this clip is fading in, adjust the crossfade clip's weight to be a percentage of your current blend weight if (isClipStateBitSet(CLIP_IS_FADING_IN_BIT)) { _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight) * _blendWeight; _blendWeight -= _crossFadeToClip->_blendWeight; } else { // Just set the blend weight. _crossFadeToClip->_blendWeight = (1.0f - tempBlendWeight); _blendWeight = tempBlendWeight; } } else { // Fade is done. _crossFadeToClip->_blendWeight = 1.0f; _blendWeight = 0.0f; resetClipStateBit(CLIP_IS_STARTED_BIT); resetClipStateBit(CLIP_IS_FADING_OUT_BIT); _crossFadeToClip->resetClipStateBit(CLIP_IS_FADING_IN_BIT); SAFE_RELEASE(_crossFadeToClip); } } // Evaluate this clip. Animation::Channel* channel = NULL; AnimationValue* value = NULL; AnimationTarget* target = NULL; size_t channelCount = _animation->_channels.size(); for (size_t i = 0; i < channelCount; i++) { channel = _animation->_channels[i]; GP_ASSERT(channel); target = channel->_target; GP_ASSERT(target); value = _values[i]; GP_ASSERT(value); // Evaluate the point on Curve GP_ASSERT(channel->getCurve()); channel->getCurve()->evaluate(percentComplete, value->_value); // Set the animation value on the target property. target->setAnimationPropertyValue(channel->_propertyId, value, _blendWeight); } // When ended. Probably should move to it's own method so we can call it when the clip is ended early. if (isClipStateBitSet(CLIP_IS_MARKED_FOR_REMOVAL_BIT) || !isClipStateBitSet(CLIP_IS_STARTED_BIT)) { onEnd(); return true; } return false; }