diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 4385d0b9d..9fa2c74be 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -45,18 +45,19 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim { case CharState_Idle: mCurrentGroup = "idle"; - mAnimation->playGroup(mCurrentGroup, 1, 1); + mAnimation->play(mCurrentGroup, "start"); break; case CharState_Dead: mCurrentGroup = "death1"; - mAnimation->playGroup(mCurrentGroup, 1, 1); + mAnimation->play(mCurrentGroup, "stop"); break; } } CharacterController::CharacterController(const CharacterController &rhs) : mPtr(rhs.mPtr), mAnimation(rhs.mAnimation), mAnimNames(rhs.mAnimNames) - , mCurrentGroup(rhs.mCurrentGroup), mState(rhs.mState), mSkipAnim(rhs.mSkipAnim) + , mAnimQueue(rhs.mAnimQueue), mCurrentGroup(rhs.mCurrentGroup) + , mState(rhs.mState), mSkipAnim(rhs.mSkipAnim) { if(mAnimNames.size() == 0) return; @@ -79,12 +80,33 @@ void CharacterController::markerEvent(const std::string &evt) // to this actor type return; } - if(evt.length() <= mCurrentGroup.length()+2 || evt.compare(0, mCurrentGroup.length(), mCurrentGroup) != 0 || - evt.compare(mCurrentGroup.length(), 2, ": ") != 0) + std::string::size_type ms = mCurrentGroup.length()+2; + if(evt.length() <= ms || evt.compare(0, ms-2, mCurrentGroup) != 0 || evt.compare(ms-2, 2, ": ") != 0) { std::cerr<< "Event \""<= 2 && mAnimQueue[0] == mAnimQueue[1]) + { + if(evt.compare(ms, evt.length()-ms, "loop stop") == 0 || evt.compare(ms, evt.length()-ms, "stop") == 0) + { + mAnimQueue.pop_front(); + mAnimation->play(mCurrentGroup, "loop start"); + } + } + else if(mAnimQueue.size() > 0) + { + if(evt.compare(ms, evt.length()-ms, "stop") == 0) + { + mAnimQueue.pop_front(); + if(mAnimQueue.size() > 0) + { + mCurrentGroup = mAnimQueue.front(); + mAnimation->play(mCurrentGroup, "start"); + } + } + } } @@ -101,7 +123,23 @@ void CharacterController::playGroup(const std::string &groupname, int mode, int { // set mState = CharState_Idle? if(std::find(mAnimNames.begin(), mAnimNames.end(), groupname) != mAnimNames.end()) - mAnimation->playGroup(groupname, mode, count); + { + count = std::max(count, 1); + if(mode != 0 || mAnimQueue.size() == 0) + { + mAnimQueue.clear(); + while(count-- > 0) + mAnimQueue.push_back(groupname); + mCurrentGroup = groupname; + mAnimation->play(mCurrentGroup, ((mode==2) ? "loop start" : "start")); + } + else if(mode == 0) + { + mAnimQueue.resize(1); + while(count-- > 0) + mAnimQueue.push_back(groupname); + } + } } void CharacterController::skipAnim() @@ -116,15 +154,16 @@ void CharacterController::setState(CharacterState state) if(mAnimNames.size() == 0) return; + mAnimQueue.clear(); switch(mState) { case CharState_Idle: mCurrentGroup = "idle"; - mAnimation->playGroup(mCurrentGroup, 1, 1); + mAnimation->play(mCurrentGroup, "start"); break; case CharState_Dead: mCurrentGroup = "death1"; - mAnimation->playGroup(mCurrentGroup, 1, 1); + mAnimation->play(mCurrentGroup, "start"); break; } } diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 202e10578..992cb8f1f 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -23,6 +23,9 @@ class CharacterController std::vector mAnimNames; + typedef std::deque AnimationQueue; + AnimationQueue mAnimQueue; + std::string mCurrentGroup; CharacterState mState; bool mSkipAnim; diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d10accab7..4d9ee108c 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -23,6 +23,8 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mNonAccumRoot(NULL) , mStartPosition(0.0f) , mLastPosition(0.0f) + , mCurrentKeys(NULL) + , mAnimState(NULL) , mTime(0.0f) { } @@ -121,14 +123,14 @@ void Animation::updatePosition(float time) { if(time == mTime) return; - mCurGroup.mAnimState->setTimePosition(time); + mAnimState->setTimePosition(time); mTime = time; if(mNonAccumRoot) { /* Update the animation and get the non-accumulation root's difference from the * last update. */ - mEntityList.mSkelBase->getSkeleton()->setAnimationState(*mCurGroup.mAnimState->getParent()); + mEntityList.mSkelBase->getSkeleton()->setAnimationState(*mAnimState->getParent()); Ogre::Vector3 posdiff = mNonAccumRoot->getPosition() - mLastPosition; /* Translate the accumulation root back to compensate for the move. */ @@ -149,136 +151,81 @@ void Animation::updatePosition(float time) void Animation::resetPosition(float time) { - mCurGroup.mAnimState->setTimePosition(time); + mAnimState->setTimePosition(time); mTime = time; - mCurGroup.mNext = mCurGroup.mStart; - while(mCurGroup.mNext != mCurGroup.mTextKeys->end() && mCurGroup.mNext->first < time) - mCurGroup.mNext++; + mNextKey = mCurrentKeys->begin(); + while(mNextKey != mCurrentKeys->end() && mNextKey->first < time) + mNextKey++; if(mNonAccumRoot) { - mEntityList.mSkelBase->getSkeleton()->setAnimationState(*mCurGroup.mAnimState->getParent()); + mEntityList.mSkelBase->getSkeleton()->setAnimationState(*mAnimState->getParent()); mLastPosition = mNonAccumRoot->getPosition(); mAccumRoot->setPosition(mStartPosition - mLastPosition); } } -bool Animation::findGroupTimes(const std::string &groupname, Animation::GroupTimes *times) +float Animation::findStart(const std::string &groupname, const std::string &start) { - times->mTextKeys = &mTextKeys[groupname]; - const NifOgre::TextKeyMap &textkeys = *times->mTextKeys; - if(textkeys.size() == 0) - return false; + mNextKey = mCurrentKeys->end(); + if(mCurrentKeys->size() == 0) + return 0.0f; if(groupname == "all") { - times->mStart = times->mLoopStart = textkeys.begin(); - times->mLoopStop = times->mStop = textkeys.end(); - times->mLoopStop--; times->mStop--; - return true; + mNextKey = mCurrentKeys->begin(); + return 0.0f; } - const std::string start = groupname+": start"; - const std::string startloop = groupname+": loop start"; - const std::string stop = groupname+": stop"; - const std::string stoploop = groupname+": loop stop"; - - times->mStart = times->mLoopStart = - times->mStop = times->mLoopStop = textkeys.end(); - + std::string startmarker = groupname+": "+start; NifOgre::TextKeyMap::const_iterator iter; - for(iter = textkeys.begin();iter != textkeys.end();iter++) + for(iter = mCurrentKeys->begin();iter != mCurrentKeys->end();iter++) { - if(start == iter->second) - { - times->mStart = iter; - times->mLoopStart = iter; - } - else if(startloop == iter->second) - times->mLoopStart = iter; - else if(stoploop == iter->second) - times->mLoopStop = iter; - else if(stop == iter->second) - { - times->mStop = iter; - if(times->mLoopStop == textkeys.end()) - times->mLoopStop = iter; - return (times->mStart != textkeys.end()); - } + if(iter->second == startmarker) + return iter->first; } - - return false; + return 0.0f; } -void Animation::playGroup(std::string groupname, int mode, int loops) +void Animation::play(const std::string &groupname, const std::string &start) { - GroupTimes times; - + float time = 0.0f; try { if(!mEntityList.mSkelBase) throw std::runtime_error("Attempting to animate an inanimate object"); - std::transform(groupname.begin(), groupname.end(), groupname.begin(), ::tolower); - times.mAnimState = mEntityList.mSkelBase->getAnimationState(groupname); - times.mLoops = loops; + if(mAnimState) + mAnimState->setEnabled(false); + mAnimState = mEntityList.mSkelBase->getAnimationState(groupname); + mCurrentKeys = &mTextKeys[groupname]; + mAnimState->setEnabled(true); - if(!findGroupTimes(groupname, ×)) - throw std::runtime_error("Failed to find animation group "+groupname); + time = findStart(groupname, start); } catch(std::exception &e) { std::cerr<< e.what() <setEnabled(false); - mCurGroup = times; - mNextGroup = GroupTimes(); - mCurGroup.mAnimState->setEnabled(true); - resetPosition(mCurGroup.mNext->first); - } + resetPosition(time); } void Animation::runAnimation(float timepassed) { - while(mCurGroup.mAnimState && timepassed > 0.0f) + while(mAnimState && timepassed > 0.0f) { float targetTime = mTime + timepassed; - if(mCurGroup.mNext != mCurGroup.mTextKeys->end() && - mCurGroup.mNext->first <= targetTime) + if(mNextKey != mCurrentKeys->end() && mNextKey->first <= targetTime) { - updatePosition(mCurGroup.mNext->first); + const std::string &evt = mNextKey->second; + updatePosition(mNextKey->first); + mNextKey++; timepassed = targetTime - mTime; if(mController) - mController->markerEvent(mCurGroup.mNext->second); - if(mCurGroup.mNext == mCurGroup.mLoopStop && mCurGroup.mLoops > 1) - { - mCurGroup.mLoops--; - resetPosition(mCurGroup.mLoopStart->first); - continue; - } - else if(mCurGroup.mNext == mCurGroup.mStop) - { - if(!mNextGroup.mAnimState) - break; - - mCurGroup.mAnimState->setEnabled(false); - mCurGroup = mNextGroup; - mNextGroup = GroupTimes(); - mCurGroup.mAnimState->setEnabled(true); - resetPosition(mCurGroup.mStart->first); - continue; - } - mCurGroup.mNext++; + mController->markerEvent(evt); continue; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 5dbe7b510..722eccc30 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -15,23 +15,6 @@ namespace MWRender class Animation { - struct GroupTimes { - NifOgre::TextKeyMap *mTextKeys; - - NifOgre::TextKeyMap::const_iterator mStart; - NifOgre::TextKeyMap::const_iterator mStop; - NifOgre::TextKeyMap::const_iterator mLoopStart; - NifOgre::TextKeyMap::const_iterator mLoopStop; - - NifOgre::TextKeyMap::const_iterator mNext; - - Ogre::AnimationState *mAnimState; - size_t mLoops; - - GroupTimes() : mTextKeys(NULL), mAnimState(NULL), mLoops(0) - { } - }; - protected: MWWorld::Ptr mPtr; MWMechanics::CharacterController *mController; @@ -44,9 +27,10 @@ protected: Ogre::Vector3 mStartPosition; Ogre::Vector3 mLastPosition; + NifOgre::TextKeyMap *mCurrentKeys; + NifOgre::TextKeyMap::const_iterator mNextKey; + Ogre::AnimationState *mAnimState; float mTime; - GroupTimes mCurGroup; - GroupTimes mNextGroup; /* Updates the animation to the specified time, and moves the mPtr object * based on the change since the last update or reset. */ @@ -55,7 +39,7 @@ protected: * object. */ void resetPosition(float time); - bool findGroupTimes(const std::string &groupname, GroupTimes *times); + float findStart(const std::string &groupname, const std::string &start); void createEntityList(Ogre::SceneNode *node, const std::string &model); @@ -66,7 +50,7 @@ public: void setController(MWMechanics::CharacterController *controller); std::vector getAnimationNames(); - void playGroup(std::string groupname, int mode, int loops); + void play(const std::string &groupname, const std::string &start); virtual void runAnimation(float timepassed); }; diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index eb9954df6..cbee9c865 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -144,7 +144,7 @@ namespace MWRender { mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); - mAnimation->playGroup ("inventoryhandtohand", 0, 1); + mAnimation->play("inventoryhandtohand", "start"); } // --------------------------------------------------------------------------------------------------