From 59f1bc7542b45e883122aae0b3c4eea08bea72ed Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Wed, 24 Jul 2013 08:38:36 -0700 Subject: [PATCH] Use time values for the aniamtion's start, stop, and loop points This seems to be closer to the expected behavior. This more cleanly handles 0- length animations, especially where the start marker comes after the stop marker while still being on the same time key. --- apps/openmw/mwrender/animation.cpp | 155 ++++++++++++++++------------- apps/openmw/mwrender/animation.hpp | 19 ++-- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 07c1c1fe70..54e199059a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -444,64 +444,58 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint) { - std::string tag = groupname+": "+start; - NifOgre::TextKeyMap::const_iterator startkey(keys.begin()); - while(startkey != keys.end() && startkey->second != tag) + const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname); + + std::string starttag = groupname+": "+start; + NifOgre::TextKeyMap::const_iterator startkey(groupstart); + while(startkey != keys.end() && startkey->second != starttag) startkey++; if(startkey == keys.end() && start == "loop start") { - tag = groupname+": start"; - startkey = keys.begin(); - while(startkey != keys.end() && startkey->second != tag) + starttag = groupname+": start"; + startkey = groupstart; + while(startkey != keys.end() && startkey->second != starttag) startkey++; } if(startkey == keys.end()) return false; - tag = groupname+": "+stop; - NifOgre::TextKeyMap::const_iterator stopkey(startkey); - while(stopkey != keys.end() && stopkey->second != tag) + const std::string stoptag = groupname+": "+stop; + NifOgre::TextKeyMap::const_iterator stopkey(groupstart); + while(stopkey != keys.end() && stopkey->second != stoptag) stopkey++; if(stopkey == keys.end()) return false; - if(startkey == stopkey) + if(startkey->first > stopkey->first) return false; - state.mStartKey = startkey; - state.mLoopStartKey = startkey; - state.mStopKey = stopkey; - state.mNextKey = startkey; + state.mStartTime = startkey->first; + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = stopkey->first; + state.mStopTime = stopkey->first; - state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint); - - tag = groupname+": loop start"; - while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey) + state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); + if(state.mTime > state.mStartTime) { - if(state.mNextKey->second == tag) - state.mLoopStartKey = state.mNextKey; - state.mNextKey++; + const std::string loopstarttag = groupname+": loop start"; + const std::string loopstoptag = groupname+": loop stop"; + NifOgre::TextKeyMap::const_iterator key(groupstart); + while(key->first <= state.mTime && key != stopkey) + { + if(key->second == loopstarttag) + state.mLoopStartTime = key->first; + else if(key->second == loopstoptag) + state.mLoopStopTime = key->first; + key++; + } } return true; } -bool Animation::doLoop(AnimState &state) -{ - if(state.mLoopCount == 0) - return false; - state.mLoopCount--; - state.mTime = state.mLoopStartKey->first; - state.mNextKey = state.mLoopStartKey; - state.mNextKey++; - state.mPlaying = true; - - return true; -} - - -bool Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) +void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key) { float time = key->first; const std::string &evt = key->second; @@ -510,7 +504,7 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co { MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); - return true; + return; } if(evt.compare(0, 10, "soundgen: ") == 0) { @@ -523,28 +517,22 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co type = MWBase::SoundManager::Play_TypeFoot; sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type); } - return true; + return; } if(evt.compare(0, groupname.size(), groupname) != 0 || evt.compare(groupname.size(), 2, ": ") != 0) { // Not ours, skip it - return true; + return; } size_t off = groupname.size()+2; size_t len = evt.size() - off; - if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) - state.mLoopStartKey = key; - else if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0) - { - if(doLoop(state)) - { - if(state.mTime >= time) - return false; - } - } + if(evt.compare(off, len, "loop start") == 0) + state.mLoopStartTime = key->first; + else if(evt.compare(off, len, "loop stop") == 0) + state.mLoopStopTime = key->first; else if(evt.compare(off, len, "equip attach") == 0) showWeapons(true); else if(evt.compare(off, len, "unequip detach") == 0) @@ -557,8 +545,6 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) MWWorld::Class::get(mPtr).attack(mPtr, -1); - - return true; } @@ -596,8 +582,9 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); for(;iter != mAnimSources.rend();iter++) { + const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; AnimState state; - if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint)) + if(reset(state, textkeys, groupname, start, stop, startpoint)) { state.mSource = *iter; state.mSpeedMult = speedmult; @@ -608,6 +595,13 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo state.mAutoDisable = autodisable; mStates[groupname] = state; + NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey); + textkey++; + } + break; } } @@ -668,29 +662,25 @@ void Animation::resetActiveGroups() } -bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult, std::string *start, std::string *stop) const +bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const { AnimStateMap::const_iterator iter = mStates.find(groupname); if(iter == mStates.end()) { if(complete) *complete = 0.0f; if(speedmult) *speedmult = 0.0f; - if(start) *start = ""; - if(stop) *stop = ""; return false; } if(complete) { - if(iter->second.mStopKey->first > iter->second.mStartKey->first) - *complete = (iter->second.mTime - iter->second.mStartKey->first) / - (iter->second.mStopKey->first - iter->second.mStartKey->first); + if(iter->second.mStopTime > iter->second.mStartTime) + *complete = (iter->second.mTime - iter->second.mStartTime) / + (iter->second.mStopTime - iter->second.mStartTime); else *complete = (iter->second.mPlaying ? 0.0f : 1.0f); } if(speedmult) *speedmult = iter->second.mSpeedMult; - if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2); - if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2); return true; } @@ -712,27 +702,52 @@ Ogre::Vector3 Animation::runAnimation(float duration) while(stateiter != mStates.end()) { AnimState &state = stateiter->second; + const NifOgre::TextKeyMap &textkeys = state.mSource->mTextKeys; + NifOgre::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime)); + float timepassed = duration * state.mSpeedMult; while(state.mPlaying) { float targetTime = state.mTime + timepassed; - if(state.mNextKey->first > targetTime) + if(textkey == textkeys.end() || textkey->first > targetTime) { if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) updatePosition(state.mTime, targetTime, movement); - state.mTime = targetTime; - break; + state.mTime = std::min(targetTime, state.mStopTime); + } + else + { + if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + updatePosition(state.mTime, textkey->first, movement); + state.mTime = textkey->first; } - NifOgre::TextKeyMap::const_iterator key(state.mNextKey++); - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) - updatePosition(state.mTime, key->first, movement); - state.mTime = key->first; - - state.mPlaying = (key != state.mStopKey); + state.mPlaying = (state.mTime < state.mStopTime); timepassed = targetTime - state.mTime; - if(!handleTextKey(state, stateiter->first, key)) + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey); + textkey++; + } + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + if(state.mTime >= state.mLoopStopTime) + break; + + textkey = textkeys.lower_bound(state.mTime); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey); + textkey++; + } + } + + if(timepassed <= 0.0f) break; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index cbedfe5054..0b8f3a02fb 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -69,10 +69,10 @@ protected: struct AnimState { Ogre::SharedPtr mSource; - NifOgre::TextKeyMap::const_iterator mStartKey; - NifOgre::TextKeyMap::const_iterator mLoopStartKey; - NifOgre::TextKeyMap::const_iterator mStopKey; - NifOgre::TextKeyMap::const_iterator mNextKey; + float mStartTime; + float mLoopStartTime; + float mLoopStopTime; + float mStopTime; float mTime; float mSpeedMult; @@ -84,7 +84,8 @@ protected: int mGroups; bool mAutoDisable; - AnimState() : mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), + AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), + mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0), mPriority(0), mGroups(0), mAutoDisable(true) { } }; @@ -141,9 +142,7 @@ protected: const std::string &groupname, const std::string &start, const std::string &stop, float startpoint); - bool doLoop(AnimState &state); - - bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); + void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key); /* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle * systems in the model are ignored (useful for NPCs, where only the skeleton is needed for @@ -209,11 +208,9 @@ public: * \param groupname Animation group to check. * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. * \param speedmult Stores the animation speed multiplier - * \param start Stores the start key - * \param stop Stores the stop key * \return True if the animation is active, false otherwise. */ - bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL, std::string *start=NULL, std::string *stop=NULL) const; + bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /** Disables the specified animation group; * \param groupname Animation group to disable.