diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0e906181c5..ad98efb14e 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -46,6 +46,42 @@ Animation::~Animation() } +Ogre::Bone *Animation::insertSkeletonSource(const std::string &name) +{ + Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); + Ogre::SkeletonPtr skel = skelMgr.getByName(name); + if(skel.isNull()) + { + std::cerr<< "Failed to get skeleton source "<touch(); + mSkeletonSources.push_back(skel); + + Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); + while(boneiter.hasMoreElements()) + { + Ogre::Bone *bone = boneiter.getNext(); + Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); + const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); + if(data.isEmpty() || !Ogre::any_cast(data)) + continue; + + for(int i = 0;i < skel->getNumAnimations();i++) + { + Ogre::Animation *anim = skel->getAnimation(i); + const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ + "@"+anim->getName()); + if(!groupdata.isEmpty()) + mTextKeys[anim->getName()] = Ogre::any_cast(groupdata); + } + + return bone; + } + + return NULL; +} + void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model) { mInsert = node->createChildSceneNode(); @@ -63,51 +99,35 @@ void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model state->setLoop(false); } + // Set the bones as manually controlled since we're applying the + // transformations manually (needed if we want to apply an animation + // from one skeleton onto another). 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(skelinst->getName()); - Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator(); + Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) - { - Ogre::Bone *bone = boneiter.getNext(); - Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings(); - const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID); - if(data.isEmpty() || !Ogre::any_cast(data)) - continue; + boneiter.getNext()->setManuallyControlled(true); + Ogre::Bone *bone = insertSkeletonSource(skelinst->getName()); + if(bone) + { mAccumRoot = mInsert; mNonAccumRoot = skelinst->getBone(bone->getHandle()); mStartPosition = mNonAccumRoot->getInitialPosition(); mLastPosition = mStartPosition; - - asiter = aset->getAnimationStateIterator(); - while(asiter.hasMoreElements()) - { - Ogre::AnimationState *state = asiter.getNext(); - const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+ - "@"+state->getAnimationName()); - if(!groupdata.isEmpty()) - mTextKeys[state->getAnimationName()] = Ogre::any_cast(groupdata); - } - - break; } - - // Set the bones as manually controlled since we're applying the - // transformations manually (needed if we want to apply an animation - // from one skeleton onto another). - boneiter = skelinst->getBoneIterator(); - while(boneiter.hasMoreElements()) - boneiter.getNext()->setManuallyControlled(true); } } bool Animation::hasAnimation(const std::string &anim) { - return mEntityList.mSkelBase && mEntityList.mSkelBase->getSkeleton()->hasAnimation(anim); + for(std::vector::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++) + { + if((*iter)->hasAnimation(anim)) + return true; + } + return false; } @@ -208,8 +228,20 @@ void Animation::reset(const std::string &marker) void Animation::play(const std::string &groupname, const std::string &start, bool loop) { try { - mCurrentAnim = mEntityList.mSkelBase->getSkeleton()->getAnimation(groupname); - mCurrentKeys = &mTextKeys[groupname]; + bool found = false; + /* Look in reverse; last-inserted source has priority. */ + for(std::vector::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++) + { + if((*iter)->hasAnimation(groupname)) + { + mCurrentAnim = (*iter)->getAnimation(groupname); + mCurrentKeys = &mTextKeys[groupname]; + found = true; + break; + } + } + if(!found) + throw std::runtime_error("Failed to find animation "+groupname); reset(start); mPlaying = true; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 46a1ed88d2..62e93f1100 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -28,6 +28,8 @@ protected: Ogre::Vector3 mStartPosition; Ogre::Vector3 mLastPosition; + std::vector mSkeletonSources; + NifOgre::TextKeyMap *mCurrentKeys; NifOgre::TextKeyMap::const_iterator mNextKey; Ogre::Animation *mCurrentAnim; @@ -53,6 +55,9 @@ protected: * anything. If the marker is not found, it resets to the beginning. */ void reset(const std::string &marker); + /* Inserts an additional skeleton into the animation source chain. Returns + * the bone representing the non-accum root from the base skeleton. */ + Ogre::Bone *insertSkeletonSource(const std::string &name); void createEntityList(Ogre::SceneNode *node, const std::string &model);