diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index b8be879e5..b73d35264 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -7,6 +7,9 @@ #include #include +#include "../mwbase/environment.hpp" +#include "../mwbase/world.hpp" + namespace MWRender { @@ -16,8 +19,14 @@ Animation::Animation(const MWWorld::Ptr &ptr) , mTime(0.0f) , mAnimState(NULL) , mSkipFrame(false) + , mAccumRoot(NULL) , mNonAccumRoot(NULL) + , mLastPosition(0.0f) { + mCurGroup.mStart = mCurGroup.mLoopStart = 0.0f; + mCurGroup.mLoopStop = mCurGroup.mStop = 0.0f; + mNextGroup.mStart = mNextGroup.mLoopStart = 0.0f; + mNextGroup.mLoopStop = mNextGroup.mStop = 0.0f; } Animation::~Animation() @@ -52,9 +61,10 @@ void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model mAnimState = state; } + Ogre::SkeletonInstance *skelinst = mEntityList.mSkelBase->getSkeleton(); // Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr. Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr skel = skelMgr.getByName(mEntityList.mSkelBase->getSkeleton()->getName()); + Ogre::SkeletonPtr skel = skelMgr.getByName(skelinst->getName()); Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator(); while(iter.hasMoreElements()) { @@ -63,7 +73,10 @@ void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model if(!data.isEmpty()) { mTextKeys = Ogre::any_cast(data); - mNonAccumRoot = mEntityList.mSkelBase->getSkeleton()->getBone(bone->getHandle()); + mAccumRoot = skelinst->getRootBone(); + mAccumRoot->setManuallyControlled(true); + mNonAccumRoot = skelinst->getBone(bone->getHandle()); + mLastPosition = mNonAccumRoot->getPosition(); break; } } @@ -71,6 +84,43 @@ void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model } +void Animation::updatePosition(float time) +{ + mAnimState->setTimePosition(time); + if(mNonAccumRoot) + { + /* Update the animation and get the non-accumulation root's difference from the + * last update. */ + mEntityList.mSkelBase->getSkeleton()->setAnimationState(*mAnimState->getParent()); + Ogre::Vector3 posdiff = mNonAccumRoot->getPosition() - mLastPosition; + + /* Translate the accumulation root back to compensate for the move. */ + mAccumRoot->translate(-posdiff); + mLastPosition += posdiff; + + /* Finally, move the object based on how much the non-accumulation root moved. */ + Ogre::Vector3 newpos(mPtr.getRefData().getPosition().pos); + newpos += mInsert->getOrientation() * posdiff; + + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->moveObject(mPtr, newpos.x, newpos.y, newpos.z); + } +} + +void Animation::resetPosition(float time) +{ + mAnimState->setTimePosition(time); + if(mNonAccumRoot) + { + mEntityList.mSkelBase->getSkeleton()->setAnimationState(*mAnimState->getParent()); + mLastPosition = mNonAccumRoot->getPosition(); + /* FIXME: This should be set to -mLastPosition, but without proper collision the + * model gets placed halfway into the ground. */ + mAccumRoot->setPosition(0.0f, 0.0f, 0.0f); + } +} + + struct checklow { bool operator()(const char &a, const char &b) const { @@ -145,6 +195,7 @@ void Animation::playGroup(std::string groupname, int mode, int loops) mCurGroup = times; mNextGroup = GroupTimes(); mTime = ((mode==2) ? mCurGroup.mLoopStart : mCurGroup.mStart); + resetPosition(mTime); } } @@ -155,29 +206,36 @@ void Animation::skipAnim() void Animation::runAnimation(float timepassed) { - if(mCurGroup.mLoops > 0 && !mSkipFrame) + if(mAnimState && !mSkipFrame) { mTime += timepassed; + recheck: if(mTime >= mCurGroup.mLoopStop) { if(mCurGroup.mLoops > 1) { mCurGroup.mLoops--; + updatePosition(mCurGroup.mLoopStop); mTime = mTime - mCurGroup.mLoopStop + mCurGroup.mLoopStart; + resetPosition(mCurGroup.mLoopStart); + goto recheck; } else if(mTime >= mCurGroup.mStop) { if(mNextGroup.mLoops > 0) + { + updatePosition(mCurGroup.mStop); mTime = mTime - mCurGroup.mStop + mNextGroup.mStart; - else - mTime = mCurGroup.mStop; - mCurGroup = mNextGroup; - mNextGroup = GroupTimes(); + resetPosition(mNextGroup.mStart); + mCurGroup = mNextGroup; + mNextGroup = GroupTimes(); + goto recheck; + } + mTime = mCurGroup.mStop; } } - if(mAnimState) - mAnimState->setTimePosition(mTime); + updatePosition(mTime); } mSkipFrame = false; } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 3bc173d70..d1ae5af4c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -37,7 +37,16 @@ protected: NifOgre::EntityList mEntityList; NifOgre::TextKeyMap mTextKeys; - Ogre::Node *mNonAccumRoot; + Ogre::Bone *mAccumRoot; + Ogre::Bone *mNonAccumRoot; + Ogre::Vector3 mLastPosition; + + /* Updates the animation to the specified time, and moves the mPtr object + * based on the change since the last update or reset. */ + void updatePosition(float time); + /* Updates the animation to the specified time, without moving the mPtr + * object. */ + void resetPosition(float time); bool findGroupTimes(const std::string &groupname, GroupTimes *times); diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b144466c9..7431c2a61 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -138,7 +138,6 @@ namespace MWRender mSelectionBuffer = new OEngine::Render::SelectionBuffer(mCamera, 512, 1024, RV_PlayerPreview); mAnimation->playGroup ("inventoryhandtohand", 0, 1); - mAnimation->runAnimation (0); } // --------------------------------------------------------------------------------------------------