diff --git a/apps/openmw/mwrender/activatoranimation.cpp b/apps/openmw/mwrender/activatoranimation.cpp index fc5f88b8af..f3d0ec3ff4 100644 --- a/apps/openmw/mwrender/activatoranimation.cpp +++ b/apps/openmw/mwrender/activatoranimation.cpp @@ -23,6 +23,8 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr) setObjectRoot(mPtr.getRefData().getBaseNode(), name, false); setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha); + + addAnimSource(name); } } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f81c1fe0f7..36fe541db2 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -19,8 +19,7 @@ namespace MWRender { Animation::AnimLayer::AnimLayer() - : mControllers(NULL) - , mTextKeys(NULL) + : mSource(NULL) , mTime(0.0f) , mPlaying(false) , mLoopCount(0) @@ -76,6 +75,8 @@ Animation::~Animation() { if(mInsert) { + mAnimSources.clear(); + Ogre::SceneManager *sceneMgr = mInsert->getCreator(); destroyObjectList(sceneMgr, mObjectRoot); } @@ -87,8 +88,22 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b OgreAssert(!mInsert, "Object already has a root!"); mInsert = node->createChildSceneNode(); - mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) : - NifOgre::Loader::createObjectBase(mInsert, model)); + std::string mdlname = Misc::StringUtils::lowerCase(model); + std::string::size_type p = mdlname.rfind('\\'); + if(p == std::string::npos) + p = mdlname.rfind('/'); + if(p != std::string::npos) + mdlname.insert(mdlname.begin()+p+1, 'x'); + else + mdlname.insert(mdlname.begin(), 'x'); + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(mdlname)) + { + mdlname = model; + Misc::StringUtils::toLower(mdlname); + } + + mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) : + NifOgre::Loader::createObjectBase(mInsert, mdlname)); if(mObjectRoot.mSkelBase) { mSkelBase = mObjectRoot.mSkelBase; @@ -109,12 +124,6 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator(); while(boneiter.hasMoreElements()) boneiter.getNext()->setManuallyControlled(true); - - if(mObjectRoot.mTextKeys.size() > 0) - { - mAccumRoot = mInsert; - mNonAccumRoot = skelinst->getBone(mObjectRoot.mTextKeys.begin()->first); - } } for(size_t i = 0;i < mObjectRoot.mControllers.size();i++) { @@ -148,6 +157,75 @@ void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::ui } +void Animation::addAnimSource(const std::string &model) +{ + OgreAssert(mInsert, "Object is missing a root!"); + if(!mSkelBase) + return; + + std::string kfname = Misc::StringUtils::lowerCase(model); + std::string::size_type p = kfname.rfind('\\'); + if(p == std::string::npos) + p = kfname.rfind('/'); + if(p != std::string::npos) + kfname.insert(kfname.begin()+p+1, 'x'); + else + kfname.insert(kfname.begin(), 'x'); + + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + kfname.replace(kfname.size()-4, 4, ".kf"); + + if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname)) + return; + + mAnimSources.push_back(AnimSource()); + NifOgre::Loader::createKfControllers(mSkelBase, kfname, + mAnimSources.back().mTextKeys, + mAnimSources.back().mControllers); + if(mAnimSources.back().mTextKeys.size() == 0 || mAnimSources.back().mControllers.size() == 0) + { + mAnimSources.pop_back(); + return; + } + + std::vector > &ctrls = mAnimSources.back().mControllers; + NifOgre::NodeTargetValue *dstval; + + for(size_t i = 0;i < ctrls.size();i++) + { + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + + if(i == 0 && !mAccumRoot) + { + mAccumRoot = mInsert; + mNonAccumRoot = dstval->getNode(); + } + + ctrls[i].setSource(mAnimationValuePtr[0]); + } +} + +void Animation::clearAnimSources() +{ + for(size_t layer = 0;layer < sMaxLayers;layer++) + { + mLayer[layer].mGroupName.clear(); + mLayer[layer].mSource = NULL; + mLayer[layer].mTime = 0.0f; + mLayer[layer].mLoopCount = 0; + mLayer[layer].mPlaying = false; + } + mNonAccumCtrl = NULL; + mAnimVelocity = 0.0f; + + mLastPosition = Ogre::Vector3(0.0f); + mAccumRoot = NULL; + mNonAccumRoot = NULL; + + mAnimSources.clear(); +} + + Ogre::Node *Animation::getNode(const std::string &name) { if(mSkelBase) @@ -175,12 +253,10 @@ NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::Tex bool Animation::hasAnimation(const std::string &anim) { - if(!mSkelBase) - return false; - - if(mObjectRoot.mTextKeys.size() > 0) + AnimSourceList::const_iterator iter(mAnimSources.begin()); + for(;iter != mAnimSources.end();iter++) { - const NifOgre::TextKeyMap &keys = mObjectRoot.mTextKeys.begin()->second; + const NifOgre::TextKeyMap &keys = iter->mTextKeys; if(findGroupStart(keys, anim) != keys.end()) return true; } @@ -415,8 +491,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con return false; mLayer[layeridx].mGroupName.clear(); - mLayer[layeridx].mTextKeys = NULL; - mLayer[layeridx].mControllers = NULL; + mLayer[layeridx].mSource = NULL; mLayer[layeridx].mTime = 0.0f; mLayer[layeridx].mLoopCount = 0; mLayer[layeridx].mPlaying = false; @@ -428,19 +503,17 @@ bool Animation::play(const std::string &groupname, const std::string &start, con bool foundanim = false; /* Look in reverse; last-inserted source has priority. */ - do { - NifOgre::ObjectList &objlist = mObjectRoot; - if(objlist.mTextKeys.size() == 0) - continue; - - const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second; + AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); + for(;iter != mAnimSources.rend();iter++) + { + const NifOgre::TextKeyMap &keys = iter->mTextKeys; NifOgre::NodeTargetValue *nonaccumctrl = NULL; if(layeridx == 0 && mNonAccumRoot) { - for(size_t i = 0;i < objlist.mControllers.size();i++) + for(size_t i = 0;i < iter->mControllers.size();i++) { NifOgre::NodeTargetValue *dstval; - dstval = dynamic_cast*>(objlist.mControllers[i].getDestination().getPointer()); + dstval = dynamic_cast*>(iter->mControllers[i].getDestination().getPointer()); if(dstval && dstval->getNode() == mNonAccumRoot) { nonaccumctrl = dstval; @@ -455,8 +528,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con continue; mLayer[layeridx].mGroupName = groupname; - mLayer[layeridx].mTextKeys = &keys; - mLayer[layeridx].mControllers = &objlist.mControllers; + mLayer[layeridx].mSource = &*iter; mLayer[layeridx].mLoopCount = loops; mLayer[layeridx].mPlaying = true; @@ -481,7 +553,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con movinganim = (nonaccumctrl==mNonAccumCtrl); break; } - } while(0); + } if(!foundanim) std::cerr<< "Failed to find animation "<mControllers.size();i++) + mLayer[layeridx].mSource->mControllers[i].update(); + } if(mSkelBase) { diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ccab2cb434..76ffeecd05 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -30,10 +30,15 @@ protected: virtual void setValue(Ogre::Real value); }; + struct AnimSource { + NifOgre::TextKeyMap mTextKeys; + std::vector > mControllers; + }; + typedef std::vector AnimSourceList; + struct AnimLayer { std::string mGroupName; - std::vector > *mControllers; - const NifOgre::TextKeyMap *mTextKeys; + AnimSource *mSource; NifOgre::TextKeyMap::const_iterator mStartKey; NifOgre::TextKeyMap::const_iterator mLoopStartKey; NifOgre::TextKeyMap::const_iterator mStopKey; @@ -52,8 +57,9 @@ protected: Ogre::SceneNode *mInsert; Ogre::Entity *mSkelBase; NifOgre::ObjectList mObjectRoot; + AnimSourceList mAnimSources; Ogre::Node *mAccumRoot; - Ogre::Bone *mNonAccumRoot; + Ogre::Node *mNonAccumRoot; NifOgre::NodeTargetValue *mNonAccumCtrl; Ogre::Vector3 mAccumulate; Ogre::Vector3 mLastPosition; @@ -95,11 +101,14 @@ protected: bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key); void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly); + void addAnimSource(const std::string &model); static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects); static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue); + void clearAnimSources(); + public: Animation(const MWWorld::Ptr &ptr); virtual ~Animation(); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 45c6eb9973..7817c23c9e 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -23,6 +23,10 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) setObjectRoot(mPtr.getRefData().getBaseNode(), model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); + + if((ref->mBase->mFlags&ESM::Creature::Biped)) + addAnimSource("meshes\\base_anim.nif"); + addAnimSource(model); } } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 0cc6846b5e..f96c53a1aa 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -100,26 +100,26 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); setObjectRoot(node, smodel, true); -#if 0 - addAnimSource(node, smodel); + + addAnimSource(smodel); if(mBodyPrefix.find("argonian") != std::string::npos) - addAnimSource(node, "meshes\\argonian_swimkna.nif"); + addAnimSource("meshes\\argonian_swimkna.nif"); else if(!mNpc->isMale() && !isBeast) - addAnimSource(node, "meshes\\base_anim_female.nif"); + addAnimSource("meshes\\base_anim_female.nif"); if(mNpc->mModel.length() > 0) - addAnimSource(node, "meshes\\"+mNpc->mModel); + addAnimSource("meshes\\"+mNpc->mModel); if(mViewMode == VM_FirstPerson) { /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. */ - addAnimSource(node, "meshes\\base_anim.1st.nif"); + addAnimSource("meshes\\base_anim.1st.nif"); if(isBeast) - addAnimSource(node, "meshes\\base_animkna.1st.nif"); + addAnimSource("meshes\\base_animkna.1st.nif"); if(!mNpc->isMale() && !isBeast) - addAnimSource(node, "meshes\\base_anim_female.1st.nif"); + addAnimSource("meshes\\base_anim_female.1st.nif"); } -#endif + forceUpdate(); } @@ -134,28 +134,28 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode) const ESM::Race *race = store.get().find(mNpc->mRace); bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0; std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif"); -#if 0 + clearAnimSources(); - addAnimSource(node, smodel); + addAnimSource(smodel); if(mBodyPrefix.find("argonian") != std::string::npos) - addAnimSource(node, "meshes\\argonian_swimkna.nif"); + addAnimSource("meshes\\argonian_swimkna.nif"); else if(!mNpc->isMale() && !isBeast) - addAnimSource(node, "meshes\\base_anim_female.nif"); + addAnimSource("meshes\\base_anim_female.nif"); if(mNpc->mModel.length() > 0) - addAnimSource(node, "meshes\\"+mNpc->mModel); + addAnimSource("meshes\\"+mNpc->mModel); if(mViewMode == VM_FirstPerson) { /* A bit counter-intuitive, but unlike third-person anims, it seems * beast races get both base_anim.1st.nif and base_animkna.1st.nif. */ - addAnimSource(node, "meshes\\base_anim.1st.nif"); + addAnimSource("meshes\\base_anim.1st.nif"); if(isBeast) - addAnimSource(node, "meshes\\base_animkna.1st.nif"); + addAnimSource("meshes\\base_animkna.1st.nif"); if(!mNpc->isMale() && !isBeast) - addAnimSource(node, "meshes\\base_anim_female.1st.nif"); + addAnimSource("meshes\\base_anim_female.1st.nif"); } MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr); -#endif + for(size_t i = 0;i < sPartListSize;i++) removeIndividualPart(i); forceUpdate(); diff --git a/components/nifogre/ogrenifloader.cpp b/components/nifogre/ogrenifloader.cpp index acd99c26b4..ed2dc28056 100644 --- a/components/nifogre/ogrenifloader.cpp +++ b/components/nifogre/ogrenifloader.cpp @@ -818,6 +818,64 @@ public: } createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0); } + + static void loadKf(Ogre::Skeleton *skel, const std::string &name, + TextKeyMap &textKeys, std::vector > &ctrls) + { + Nif::NIFFile::ptr nif = Nif::NIFFile::create(name); + if(nif->numRoots() < 1) + { + nif->warn("Found no root nodes in "+name+"."); + return; + } + + const Nif::Record *r = nif->getRoot(0); + assert(r != NULL); + + const Nif::NiSequenceStreamHelper *seq = dynamic_cast(r); + if(seq == NULL) + { + nif->warn("First root was not a NiSequenceStreamHelper, but a "+ + r->recName+"."); + return; + } + + Nif::ExtraPtr extra = seq->extra; + if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData) + { + nif->warn("First extra data was not a NiTextKeyExtraData, but a "+ + (extra.empty() ? std::string("nil") : extra->recName)+"."); + return; + } + + extractTextKeys(static_cast(extra.getPtr()), textKeys); + + extra = extra->extra; + Nif::ControllerPtr ctrl = seq->controller; + for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next)) + { + if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController) + { + nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName); + continue; + } + + const Nif::NiStringExtraData *strdata = static_cast(extra.getPtr()); + const Nif::NiKeyframeController *key = static_cast(ctrl.getPtr()); + + if(key->data.empty()) + continue; + if(!skel->hasBone(strdata->string)) + continue; + + Ogre::Bone *trgtbone = skel->getBone(strdata->string); + Ogre::ControllerValueRealPtr srcval; + Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr())); + Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false)); + + ctrls.push_back(Ogre::Controller(srcval, dstval, func)); + } + } }; @@ -912,4 +970,14 @@ ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string nam return objectlist; } + +void Loader::createKfControllers(Ogre::Entity *skelBase, + const std::string &name, + TextKeyMap &textKeys, + std::vector > &ctrls) +{ + NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls); +} + + } // namespace NifOgre diff --git a/components/nifogre/ogrenifloader.hpp b/components/nifogre/ogrenifloader.hpp index 7db12281eb..45f3cbcd8a 100644 --- a/components/nifogre/ogrenifloader.hpp +++ b/components/nifogre/ogrenifloader.hpp @@ -68,6 +68,11 @@ public: static ObjectList createObjectBase(Ogre::SceneNode *parentNode, std::string name, const std::string &group="General"); + + static void createKfControllers(Ogre::Entity *skelBase, + const std::string &name, + TextKeyMap &textKeys, + std::vector > &ctrls); }; // FIXME: Should be with other general Ogre extensions.