diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 711feb4a61..5310dfdbbc 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1533,6 +1533,9 @@ void CharacterController::update(float duration) updateMagicEffects(); + if(mAnimQueue.size() > 1 && (mAnimation->getLoopingEnabled(mAnimQueue.front().mGroup) == true)) + mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, false); + if(!cls.isActor()) { if(mAnimQueue.size() > 1) @@ -2009,8 +2012,26 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int } else { - if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname && isAnimPlaying(mAnimQueue.front().mGroup)) - return true; + // If the given animation is a looped animation, is already playing + // and has not yet reached its Loop Stop key, make it the only animation + // in the queue, and retain the loop count from the animation that was + // already playing. This emulates observed behavior from the original + // engine and allows banners to animate correctly. + if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname && + mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop start") >= 0) + { + float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop stop"); + + if (endOfLoop < 0) // if no Loop Stop key was found, use the Stop key + endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": stop"); + + if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop)) + { + mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, true); + mAnimQueue.resize(1); + return true; + } + } count = std::max(count, 1); @@ -2035,8 +2056,6 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int } else if(mode == 0) { - if (!mAnimQueue.empty()) - mAnimation->stopLooping(mAnimQueue.front().mGroup); mAnimQueue.resize(1); mAnimQueue.push_back(entry); } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index e5614f3f8f..03370e7f83 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1023,50 +1023,54 @@ namespace MWRender { float targetTime; - if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) - goto handle_loop; - - targetTime = state.getTime() + timepassed; - if(textkey == textkeys.end() || textkey->first > targetTime) + if (state.getTime() < state.mLoopStopTime || state.mLoopCount == 0) { - if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) - updatePosition(state.getTime(), targetTime, movement); - state.setTime(std::min(targetTime, state.mStopTime)); - } - else - { - if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) - updatePosition(state.getTime(), textkey->first, movement); - state.setTime(textkey->first); - } + targetTime = state.getTime() + timepassed; + if(textkey == textkeys.end() || textkey->first > targetTime) + { + if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) + updatePosition(state.getTime(), targetTime, movement); + state.setTime(std::min(targetTime, state.mStopTime)); + } + else + { + if(mAccumCtrl && state.mTime == mAnimationTimePtr[0]->getTimePtr()) + updatePosition(state.getTime(), textkey->first, movement); + state.setTime(textkey->first); + } - state.mPlaying = (state.getTime() < state.mStopTime); - timepassed = targetTime - state.getTime(); + state.mPlaying = (state.getTime() < state.mStopTime); + timepassed = targetTime - state.getTime(); - while(textkey != textkeys.end() && textkey->first <= state.getTime()) - { - handleTextKey(state, stateiter->first, textkey, textkeys); - ++textkey; - } - - if(state.getTime() >= state.mLoopStopTime && state.mLoopCount > 0) - { - handle_loop: - state.mLoopCount--; - state.setTime(state.mLoopStartTime); - state.mPlaying = true; - - textkey = textkeys.lower_bound(state.getTime()); while(textkey != textkeys.end() && textkey->first <= state.getTime()) { handleTextKey(state, stateiter->first, textkey, textkeys); ++textkey; } - - if(state.getTime() >= state.mLoopStopTime) - break; } + if(state.getTime() >= state.mLoopStopTime) + { + if (!state.mLoopingEnabled) + state.mLoopCount = 0; + else if (state.mLoopCount > 0) + { + state.mLoopCount--; + state.setTime(state.mLoopStartTime); + state.mPlaying = true; + + textkey = textkeys.lower_bound(state.getTime()); + while(textkey != textkeys.end() && textkey->first <= state.getTime()) + { + handleTextKey(state, stateiter->first, textkey, textkeys); + ++textkey; + } + + if(state.getTime() >= state.mLoopStopTime) + break; + } + } + if(timepassed <= 0.0f) break; } @@ -1095,6 +1099,21 @@ namespace MWRender return movement; } + void Animation::setLoopingEnabled(const std::string &groupname, bool enabled) + { + AnimStateMap::iterator state(mStates.find(groupname)); + if(state != mStates.end()) + state->second.mLoopingEnabled = enabled; + } + + bool Animation::getLoopingEnabled(const std::string &groupname) const + { + AnimStateMap::const_iterator state(mStates.find(groupname)); + if(state != mStates.end()) + return state->second.mLoopingEnabled; + return false; + } + void Animation::setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature) { osg::ref_ptr previousStateset; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index a837a26aeb..2eb857cbd0 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -183,6 +183,7 @@ protected: float mSpeedMult; bool mPlaying; + bool mLoopingEnabled; size_t mLoopCount; AnimPriority mPriority; @@ -190,8 +191,8 @@ protected: bool mAutoDisable; AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), - mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), - mPriority(0), mBlendMask(0), mAutoDisable(true) + mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopingEnabled(true), + mLoopCount(0), mPriority(0), mBlendMask(0), mAutoDisable(true) { } ~AnimState(); @@ -432,6 +433,10 @@ public: virtual osg::Vec3f runAnimation(float duration); + void setLoopingEnabled(const std::string &groupname, bool enabled); + + bool getLoopingEnabled(const std::string &groupname) const; + /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(float duration);