diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 75d409aec..d291e0fd0 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1,11 +1,13 @@ #include "animation.hpp" #include +#include #include #include #include #include +#include #include @@ -13,6 +15,10 @@ #include #include +#include // KeyframeHolder + +#include + #include #include @@ -74,6 +80,89 @@ namespace std::vector > mTextures; }; + class NodeMapVisitor : public osg::NodeVisitor + { + public: + NodeMapVisitor() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + + void apply(osg::MatrixTransform& trans) + { + mMap[Misc::StringUtils::lowerCase(trans.getName())] = &trans; + traverse(trans); + } + + typedef std::map > NodeMap; + + const NodeMap& getNodeMap() const + { + return mMap; + } + + private: + NodeMap mMap; + }; + + NifOsg::TextKeyMap::const_iterator findGroupStart(const NifOsg::TextKeyMap &keys, const std::string &groupname) + { + NifOsg::TextKeyMap::const_iterator iter(keys.begin()); + for(;iter != keys.end();++iter) + { + if(iter->second.compare(0, groupname.size(), groupname) == 0 && + iter->second.compare(groupname.size(), 2, ": ") == 0) + break; + } + return iter; + } + + float calcAnimVelocity(const std::multimap& keys, + NifOsg::KeyframeController *nonaccumctrl, const osg::Vec3f& accum, const std::string &groupname) + { + const std::string start = groupname+": start"; + const std::string loopstart = groupname+": loop start"; + const std::string loopstop = groupname+": loop stop"; + const std::string stop = groupname+": stop"; + float starttime = std::numeric_limits::max(); + float stoptime = 0.0f; + + // Pick the last Loop Stop key and the last Loop Start key. + // This is required because of broken text keys in AshVampire.nif. + // It has *two* WalkForward: Loop Stop keys at different times, the first one is used for stopping playback + // but the animation velocity calculation uses the second one. + // As result the animation velocity calculation is not correct, and this incorrect velocity must be replicated, + // because otherwise the Creature's Speed (dagoth uthol) would not be sufficient to move fast enough. + NifOsg::TextKeyMap::const_reverse_iterator keyiter(keys.rbegin()); + while(keyiter != keys.rend()) + { + if(keyiter->second == start || keyiter->second == loopstart) + { + starttime = keyiter->first; + break; + } + ++keyiter; + } + keyiter = keys.rbegin(); + while(keyiter != keys.rend()) + { + if (keyiter->second == stop) + stoptime = keyiter->first; + else if (keyiter->second == loopstop) + { + stoptime = keyiter->first; + break; + } + ++keyiter; + } + + if(stoptime > starttime) + { + osg::Vec3f startpos = osg::componentMultiply(nonaccumctrl->getTranslation(starttime), accum); + osg::Vec3f endpos = osg::componentMultiply(nonaccumctrl->getTranslation(stoptime), accum); + + return (startpos-endpos).length() / (stoptime - starttime); + } + + return 0.0f; + } } namespace MWRender @@ -84,7 +173,8 @@ namespace MWRender , mInsert(parentNode) , mResourceSystem(resourceSystem) { - + for(size_t i = 0;i < sNumGroups;i++) + mAnimationTimePtr[i].reset(new AnimationTime(this)); } Animation::~Animation() @@ -98,16 +188,589 @@ namespace MWRender mPtr = ptr; } + void Animation::setAccumulation(const osg::Vec3f& accum) + { + mAccumulate = accum; + } + + size_t Animation::detectAnimGroup(osg::Node* node) + { + static const char sGroupRoots[sNumGroups][32] = { + "", /* Lower body / character root */ + "Bip01 Spine1", /* Torso */ + "Bip01 L Clavicle", /* Left arm */ + "Bip01 R Clavicle", /* Right arm */ + }; + + while(node != mObjectRoot) + { + const std::string &name = node->getName(); + for(size_t i = 1;i < sNumGroups;i++) + { + if(name == sGroupRoots[i]) + return i; + } + + assert(node->getNumParents() > 0); + + node = node->getParent(0); + } + + return 0; + } + + const std::multimap &Animation::AnimSource::getTextKeys() + { + return mKeyframes->mTextKeys; + } + void Animation::addAnimSource(const std::string &model) { + std::string kfname = model; + Misc::StringUtils::toLower(kfname); + + if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0) + kfname.replace(kfname.size()-4, 4, ".kf"); + + if(!mResourceSystem->getVFS()->exists(kfname)) + return; + + boost::shared_ptr animsrc; + animsrc.reset(new AnimSource); + animsrc->mKeyframes = mResourceSystem->getSceneManager()->getKeyframes(kfname); + + if (animsrc->mKeyframes->mTextKeys.empty() || animsrc->mKeyframes->mKeyframeControllers.empty()) + return; + + for (NifOsg::KeyframeHolder::KeyframeControllerMap::const_iterator it = animsrc->mKeyframes->mKeyframeControllers.begin(); + it != animsrc->mKeyframes->mKeyframeControllers.end(); ++it) + { + std::string bonename = Misc::StringUtils::lowerCase(it->first); + NodeMap::const_iterator found = mNodeMap.find(bonename); + if (found == mNodeMap.end()) + throw std::runtime_error("addAnimSource: can't find bone " + bonename); + + osg::Node* node = found->second; + + size_t group = detectAnimGroup(node); + + // clone the controller, because each Animation needs its own ControllerSource + osg::ref_ptr cloned = osg::clone(it->second.get(), osg::CopyOp::DEEP_COPY_ALL); + cloned->setSource(mAnimationTimePtr[group]); + + animsrc->mControllerMap[group].insert(std::make_pair(bonename, cloned)); + } + + mAnimSources.push_back(animsrc); + SceneUtil::AssignControllerSourcesVisitor assignVisitor(mAnimationTimePtr[0]); + mObjectRoot->accept(assignVisitor); + + if (!mAccumRoot) + { + NodeMap::const_iterator found = mNodeMap.find("root bone"); + if (found == mNodeMap.end()) + found = mNodeMap.find("bip01"); + + if (found != mNodeMap.end()) + mAccumRoot = found->second; + } + } + + bool Animation::hasAnimation(const std::string &anim) + { + AnimSourceList::const_iterator iter(mAnimSources.begin()); + for(;iter != mAnimSources.end();++iter) + { + const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); + if(findGroupStart(keys, anim) != keys.end()) + return true; + } + + return false; + } + + float Animation::getStartTime(const std::string &groupname) const + { + for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) + { + const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); + + NifOsg::TextKeyMap::const_iterator found = findGroupStart(keys, groupname); + if(found != keys.end()) + return found->first; + } + return -1.f; + } + + float Animation::getTextKeyTime(const std::string &textKey) const + { + for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) + { + const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); + + for(NifOsg::TextKeyMap::const_iterator iterKey(keys.begin()); iterKey != keys.end(); ++iterKey) + { + if(iterKey->second.compare(0, textKey.size(), textKey) == 0) + return iterKey->first; + } + } + + return -1.f; + } + + void Animation::handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, + const std::multimap& map) + { + // TODO: forward to listener? + } + + void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, + const std::string &start, const std::string &stop, float startpoint, size_t loops, bool loopfallback) + { + if(!mObjectRoot || mAnimSources.empty()) + return; + + if(groupname.empty()) + { + resetActiveGroups(); + return; + } + + priority = std::max(0, priority); + + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + if(stateiter->second.mPriority == priority) + mStates.erase(stateiter++); + else + ++stateiter; + } + + stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) + { + stateiter->second.mPriority = priority; + resetActiveGroups(); + return; + } + + /* Look in reverse; last-inserted source has priority. */ + AnimState state; + AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); + for(;iter != mAnimSources.rend();++iter) + { + const NifOsg::TextKeyMap &textkeys = (*iter)->getTextKeys(); + if(reset(state, textkeys, groupname, start, stop, startpoint, loopfallback)) + { + state.mSource = *iter; + state.mSpeedMult = speedmult; + state.mLoopCount = loops; + state.mPlaying = (state.mTime < state.mStopTime); + state.mPriority = priority; + state.mGroups = groups; + state.mAutoDisable = autodisable; + mStates[groupname] = state; + + NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); + if (state.mPlaying) + { + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey, textkeys); + ++textkey; + } + } + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + if(state.mTime >= state.mLoopStopTime) + break; + + NifOsg::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime)); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, groupname, textkey, textkeys); + ++textkey; + } + } + + break; + } + } + if(iter == mAnimSources.rend()) + std::cerr<< "Failed to find animation "<setPosition(-mNonAccumCtrl->getTranslation(state.mTime)*mAccumulate); + } + */ + } + + bool Animation::reset(AnimState &state, const NifOsg::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint, bool loopfallback) + { + // Look for text keys in reverse. This normally wouldn't matter, but for some reason undeadwolf_2.nif has two + // separate walkforward keys, and the last one is supposed to be used. + NifOsg::TextKeyMap::const_reverse_iterator groupend(keys.rbegin()); + for(;groupend != keys.rend();++groupend) + { + if(groupend->second.compare(0, groupname.size(), groupname) == 0 && + groupend->second.compare(groupname.size(), 2, ": ") == 0) + break; + } + + std::string starttag = groupname+": "+start; + NifOsg::TextKeyMap::const_reverse_iterator startkey(groupend); + while(startkey != keys.rend() && startkey->second != starttag) + ++startkey; + if(startkey == keys.rend() && start == "loop start") + { + starttag = groupname+": start"; + startkey = groupend; + while(startkey != keys.rend() && startkey->second != starttag) + ++startkey; + } + if(startkey == keys.rend()) + return false; + + const std::string stoptag = groupname+": "+stop; + NifOsg::TextKeyMap::const_reverse_iterator stopkey(groupend); + while(stopkey != keys.rend() + // We have to ignore extra garbage at the end. + // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". + // Why, just why? :( + && (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) + ++stopkey; + if(stopkey == keys.rend()) + return false; + + if(startkey->first > stopkey->first) + return false; + + state.mStartTime = startkey->first; + if (loopfallback) + { + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = stopkey->first; + } + else + { + state.mLoopStartTime = startkey->first; + state.mLoopStopTime = std::numeric_limits::max(); + } + state.mStopTime = stopkey->first; + + state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint); + + // mLoopStartTime and mLoopStopTime normally get assigned when encountering these keys while playing the animation + // (see handleTextKey). But if startpoint is already past these keys, we need to assign them now. + if(state.mTime > state.mStartTime) + { + const std::string loopstarttag = groupname+": loop start"; + const std::string loopstoptag = groupname+": loop stop"; + + NifOsg::TextKeyMap::const_reverse_iterator key(groupend); + for (; key != startkey && key != keys.rend(); ++key) + { + if (key->first > state.mTime) + continue; + + if (key->second == loopstarttag) + state.mLoopStartTime = key->first; + else if (key->second == loopstoptag) + state.mLoopStopTime = key->first; + } + } + + return true; + } + + void Animation::resetActiveGroups() + { + // remove all previous external controllers from the scene graph + for (AnimSourceControllerMap::iterator it = mAnimSourceControllers.begin(); it != mAnimSourceControllers.end(); ++it) + { + osg::Node* node = it->first; + node->removeUpdateCallback(it->second); + } + mAnimSourceControllers.clear(); + + mAccumCtrl = NULL; + + for(size_t grp = 0;grp < sNumGroups;grp++) + { + AnimStateMap::const_iterator active = mStates.end(); + + AnimStateMap::const_iterator state = mStates.begin(); + for(;state != mStates.end();++state) + { + if(!(state->second.mGroups&(1<second.mPriority < state->second.mPriority) + active = state; + } + + mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ? + std::string() : active->first); + + // add external controllers for the AnimSource active in this group + if (active != mStates.end()) + { + boost::shared_ptr animsrc = active->second.mSource; + + for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[grp].begin(); it != animsrc->mControllerMap[grp].end(); ++it) + { + osg::ref_ptr node = mNodeMap.at(it->first); // this should not throw, we already checked for the node existing in addAnimSource + + node->addUpdateCallback(it->second); + mAnimSourceControllers[node] = it->second; + + if (grp == 0 && node == mAccumRoot) + mAccumCtrl = it->second; + } + } + } + + //if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) + // return; + + /* + AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName()); + if(state == mStates.end()) + { + //if (mAccumRoot && mNonAccumRoot) + // mAccumRoot->setPosition(-mNonAccumRoot->getPosition()*mAccumulate); + return; + } + */ + + //if (mAccumRoot && mNonAccumCtrl) + // mAccumRoot->setPosition(-mNonAccumCtrl->getTranslation(state->second.mTime)*mAccumulate); + } + + void Animation::changeGroups(const std::string &groupname, int groups) + { + AnimStateMap::iterator stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) + { + if(stateiter->second.mGroups != groups) + { + stateiter->second.mGroups = groups; + resetActiveGroups(); + } + return; + } + } + + void Animation::stopLooping(const std::string& groupname) + { + AnimStateMap::iterator stateiter = mStates.find(groupname); + if(stateiter != mStates.end()) + { + stateiter->second.mLoopCount = 0; + return; + } + } + + void Animation::adjustSpeedMult(const std::string &groupname, float speedmult) + { + AnimStateMap::iterator state(mStates.find(groupname)); + if(state != mStates.end()) + state->second.mSpeedMult = speedmult; + } + + bool Animation::isPlaying(const std::string &groupname) const + { + AnimStateMap::const_iterator state(mStates.find(groupname)); + if(state != mStates.end()) + return state->second.mPlaying; + return false; + } + + 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; + return false; + } + + if(complete) + { + 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; + return true; + } + + float Animation::getCurrentTime(const std::string &groupname) const + { + AnimStateMap::const_iterator iter = mStates.find(groupname); + if(iter == mStates.end()) + return -1.f; + + return iter->second.mTime; + } + + void Animation::disable(const std::string &groupname) + { + AnimStateMap::iterator iter = mStates.find(groupname); + if(iter != mStates.end()) + mStates.erase(iter); + resetActiveGroups(); + } + + float Animation::getVelocity(const std::string &groupname) const + { + return 0.f; + /* + // Look in reverse; last-inserted source has priority. + AnimSourceList::const_reverse_iterator animsrc(mAnimSources.rbegin()); + for(;animsrc != mAnimSources.rend();++animsrc) + { + const NifOsg::TextKeyMap &keys = (*animsrc)->mTextKeys; + if(findGroupStart(keys, groupname) != keys.end()) + break; + } + if(animsrc == mAnimSources.rend()) + return 0.0f; + + float velocity = 0.0f; + const NifOsg::TextKeyMap &keys = (*animsrc)->mTextKeys; + const std::vector >&ctrls = (*animsrc)->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOsg::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); + break; + } + } + + // If there's no velocity, keep looking + if(!(velocity > 1.0f)) + { + AnimSourceList::const_reverse_iterator animiter = mAnimSources.rbegin(); + while(*animiter != *animsrc) + ++animiter; + + while(!(velocity > 1.0f) && ++animiter != mAnimSources.rend()) + { + const NifOgre::TextKeyMap &keys = (*animiter)->mTextKeys; + const std::vector >&ctrls = (*animiter)->mControllers[0]; + for(size_t i = 0;i < ctrls.size();i++) + { + NifOgre::NodeTargetValue *dstval; + dstval = static_cast*>(ctrls[i].getDestination().getPointer()); + if(dstval->getNode() == mNonAccumRoot) + { + velocity = calcAnimVelocity(keys, dstval, mAccumulate, groupname); + break; + } + } + } + } + + return velocity; + */ } osg::Vec3f Animation::runAnimation(float duration) { + osg::Vec3f movement(0.f, 0.f, 0.f); + AnimStateMap::iterator stateiter = mStates.begin(); + while(stateiter != mStates.end()) + { + AnimState &state = stateiter->second; + const NifOsg::TextKeyMap &textkeys = state.mSource->getTextKeys(); + NifOsg::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime)); + + float timepassed = duration * state.mSpeedMult; + while(state.mPlaying) + { + float targetTime; + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + goto handle_loop; + + targetTime = state.mTime + timepassed; + if(textkey == textkeys.end() || textkey->first > targetTime) + { + //if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) + // updatePosition(state.mTime, targetTime, movement); + state.mTime = std::min(targetTime, state.mStopTime); + } + else + { + //if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) + // updatePosition(state.mTime, textkey->first, movement); + state.mTime = textkey->first; + } + + state.mPlaying = (state.mTime < state.mStopTime); + timepassed = targetTime - state.mTime; + + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey, textkeys); + ++textkey; + } + + if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0) + { + handle_loop: + state.mLoopCount--; + state.mTime = state.mLoopStartTime; + state.mPlaying = true; + + textkey = textkeys.lower_bound(state.mTime); + while(textkey != textkeys.end() && textkey->first <= state.mTime) + { + handleTextKey(state, stateiter->first, textkey, textkeys); + ++textkey; + } + + if(state.mTime >= state.mLoopStopTime) + break; + } + + if(timepassed <= 0.0f) + break; + } + + if(!state.mPlaying && state.mAutoDisable) + { + mStates.erase(stateiter++); + + resetActiveGroups(); + } + else + ++stateiter; + } + updateEffects(duration); - return osg::Vec3f(); + return movement; } void Animation::setObjectRoot(const std::string &model) @@ -117,7 +780,14 @@ namespace MWRender mObjectRoot->getParent(0)->removeChild(mObjectRoot); } + mNodeMap.clear(); + mAnimSourceControllers.clear(); + mObjectRoot = mResourceSystem->getSceneManager()->createInstance(model, mInsert); + + NodeMapVisitor visitor; + mObjectRoot->accept(visitor); + mNodeMap = visitor.getNodeMap(); } osg::Group* Animation::getObjectRoot() @@ -318,6 +988,15 @@ namespace MWRender } } + float Animation::AnimationTime::getValue(osg::NodeVisitor*) + { + // FIXME: hold a pointer instead of searching every frame + AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); + if(iter != mAnimation->mStates.end()) + return iter->second.mTime; + return 0.0f; + } + float EffectAnimationTime::getValue(osg::NodeVisitor*) { return mTime; diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index ea47a5b79..a04544ac8 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -15,9 +15,13 @@ namespace Resource class ResourceSystem; } +namespace NifOsg +{ + class KeyframeHolder; +} + namespace MWRender { -class Camera; class EffectAnimationTime : public SceneUtil::ControllerSource { @@ -68,7 +72,7 @@ protected: const std::string &getAnimName() const { return mAnimationName; } - virtual float getValue(); + virtual float getValue(osg::NodeVisitor* nv); }; class NullAnimationTime : public SceneUtil::ControllerSource @@ -80,16 +84,19 @@ protected: } }; + struct AnimSource + { + osg::ref_ptr mKeyframes; + + typedef std::map > ControllerMap; - /* - struct AnimSource : public Ogre::AnimationAlloc { - //NifOgre::TextKeyMap mTextKeys; - std::vector > mControllers[sNumGroups]; + ControllerMap mControllerMap[sNumGroups]; + + const std::multimap& getTextKeys(); }; - typedef std::vector< Ogre::SharedPtr > AnimSourceList; - */ + struct AnimState { - //Ogre::SharedPtr mSource; + boost::shared_ptr mSource; float mStartTime; float mLoopStartTime; float mLoopStopTime; @@ -111,15 +118,38 @@ protected: { } }; typedef std::map AnimStateMap; + AnimStateMap mStates; + + typedef std::vector > AnimSourceList; + AnimSourceList mAnimSources; osg::ref_ptr mInsert; osg::ref_ptr mObjectRoot; + // The node expected to accumulate movement during movement animations. + osg::ref_ptr mAccumRoot; + + // The controller animating that node. + osg::ref_ptr mAccumCtrl; + + // Keep track of keyframe controllers from external files that we added to our scene graph. + // We may need to rebuild these controllers when the active animation groups / sources change. + typedef std::map, osg::ref_ptr > AnimSourceControllerMap; + AnimSourceControllerMap mAnimSourceControllers; + + boost::shared_ptr mAnimationTimePtr[sNumGroups]; + + // Stored in all lowercase for a case-insensitive lookup + typedef std::map > NodeMap; + NodeMap mNodeMap; + MWWorld::Ptr mPtr; Resource::ResourceSystem* mResourceSystem; + osg::Vec3f mAccumulate; + /// @brief Detaches the node from its parent when the object goes out of scope. class PartHolder { @@ -160,34 +190,25 @@ protected: /* Sets the appropriate animations on the bone groups based on priority. */ - //void resetActiveGroups(); + void resetActiveGroups(); - //static size_t detectAnimGroup(const Ogre::Node *node); - - /* - static float calcAnimVelocity(const NifOgre::TextKeyMap &keys, - NifOgre::NodeTargetValue *nonaccumctrl, - const Ogre::Vector3 &accum, - const std::string &groupname); - */ + size_t detectAnimGroup(osg::Node* node); /* Updates the position of the accum root node for the given time, and * returns the wanted movement vector from the previous time. */ //void updatePosition(float oldtime, float newtime, Ogre::Vector3 &position); - //static NifOgre::TextKeyMap::const_iterator findGroupStart(const NifOgre::TextKeyMap &keys, const std::string &groupname); - /* Resets the animation to the time of the specified start marker, without * moving anything, and set the end time to the specified stop marker. If * the marker is not found, or if the markers are the same, it returns * false. */ - //bool reset(AnimState &state, const NifOgre::TextKeyMap &keys, - // const std::string &groupname, const std::string &start, const std::string &stop, - // float startpoint, bool loopfallback); + bool reset(AnimState &state, const std::multimap &keys, + const std::string &groupname, const std::string &start, const std::string &stop, + float startpoint, bool loopfallback); - //void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key, - // const NifOgre::TextKeyMap& map); + void handleTextKey(AnimState &state, const std::string &groupname, const std::multimap::const_iterator &key, + const std::multimap& map); /* Sets the root model of the object. * @@ -235,12 +256,12 @@ public: void updatePtr(const MWWorld::Ptr &ptr); - //bool hasAnimation(const std::string &anim); + bool hasAnimation(const std::string &anim); // Specifies the axis' to accumulate on. Non-accumulated axis will just // move visually, but not affect the actual movement. Each x/y/z value // should be on the scale of 0 to 1. - //void setAccumulation(const Ogre::Vector3 &accum); + void setAccumulation(const osg::Vec3f& accum); /** Plays an animation. * \param groupname Name of the animation group to play. @@ -262,20 +283,20 @@ public: * \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use * the "start" and "stop" keys for looping? */ - //void play(const std::string &groupname, int priority, int groups, bool autodisable, - // float speedmult, const std::string &start, const std::string &stop, - // float startpoint, size_t loops, bool loopfallback=false); + void play(const std::string &groupname, int priority, int groups, bool autodisable, + float speedmult, const std::string &start, const std::string &stop, + float startpoint, size_t loops, bool loopfallback=false); /** If the given animation group is currently playing, set its remaining loop count to '0'. */ - //void stopLooping(const std::string& groupName); + void stopLooping(const std::string& groupName); /** Adjust the speed multiplier of an already playing animation. */ - //void adjustSpeedMult (const std::string& groupname, float speedmult); + void adjustSpeedMult (const std::string& groupname, float speedmult); /** Returns true if the named animation group is playing. */ - //bool isPlaying(const std::string &groupname) const; + bool isPlaying(const std::string &groupname) const; /// Returns true if no important animations are currently playing on the upper body. //bool upperBodyReady() const; @@ -286,30 +307,34 @@ public: * \param speedmult Stores the animation speed multiplier * \return True if the animation is active, false otherwise. */ - //bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; + bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const; /// Get the absolute position in the animation track of the first text key with the given group. - //float getStartTime(const std::string &groupname) const; + float getStartTime(const std::string &groupname) const; /// Get the absolute position in the animation track of the text key - //float getTextKeyTime(const std::string &textKey) const; + float getTextKeyTime(const std::string &textKey) const; /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. - //float getCurrentTime(const std::string& groupname) const; + float getCurrentTime(const std::string& groupname) const; /** Disables the specified animation group; * \param groupname Animation group to disable. */ - //void disable(const std::string &groupname); - //void changeGroups(const std::string &groupname, int group); + void disable(const std::string &groupname); + void changeGroups(const std::string &groupname, int group); /** Retrieves the velocity (in units per second) that the animation will move. */ - //float getVelocity(const std::string &groupname) const; + float getVelocity(const std::string &groupname) const; virtual osg::Vec3f runAnimation(float duration); /// This is typically called as part of runAnimation, but may be called manually if needed. void updateEffects(float duration); + +private: + Animation(const Animation&); + void operator=(Animation&); }; class ObjectAnimation : public Animation { diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 241ea56a3..721215749 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -22,16 +22,16 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr, const std::string& model, Resource::ResourceSystem* resourceSystem) : Animation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), resourceSystem) { - //MWWorld::LiveCellRef *ref = mPtr.get(); + MWWorld::LiveCellRef *ref = mPtr.get(); if(!model.empty()) { setObjectRoot(model /* , baseonly = false */); //setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - //if((ref->mBase->mFlags&ESM::Creature::Bipedal)) - // addAnimSource("meshes\\xbase_anim.nif"); - //addAnimSource(model); + if((ref->mBase->mFlags&ESM::Creature::Bipedal)) + addAnimSource("meshes\\xbase_anim.nif"); + addAnimSource(model); } } @@ -41,16 +41,16 @@ CreatureWeaponAnimation::CreatureWeaponAnimation(const MWWorld::Ptr &ptr, const , mShowWeapons(false) , mShowCarriedLeft(false) { - //MWWorld::LiveCellRef *ref = mPtr.get(); + MWWorld::LiveCellRef *ref = mPtr.get(); if(!model.empty()) { setObjectRoot(model /* , baseonly = false*/); //setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - //if((ref->mBase->mFlags&ESM::Creature::Bipedal)) - // addAnimSource("meshes\\xbase_anim.nif"); - //addAnimSource(model); + if((ref->mBase->mFlags&ESM::Creature::Bipedal)) + addAnimSource("meshes\\xbase_anim.nif"); + addAnimSource(model); mPtr.getClass().getInventoryStore(mPtr).setListener(this, mPtr); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 03af50c71..ab32864bd 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -281,7 +281,6 @@ void NpcAnimation::updateNpcBase() smodel = Misc::ResourceHelpers::correctActorModelPath(smodel, mResourceSystem->getVFS()); setObjectRoot(smodel /*, baseonly = true*/); - /* if(mViewMode != VM_FirstPerson) { addAnimSource(smodel); @@ -310,7 +309,6 @@ void NpcAnimation::updateNpcBase() addAnimSource("meshes\\xbase_anim_female.1st.nif"); } } - */ for(size_t i = 0;i < ESM::PRT_Count;i++) removeIndividualPart((ESM::PartReferenceType)i); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 4b1fa6d5b..e278662aa 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -167,6 +167,7 @@ void Objects::insertNPC(const MWWorld::Ptr &ptr) insertBegin(ptr); std::auto_ptr anim (new NpcAnimation(ptr, osg::ref_ptr(ptr.getRefData().getBaseNode()), mResourceSystem, 0)); + anim->play("idle2", 0, Animation::Group_All, false, 1.f, "loop start", "loop stop", 0.f, 50); //testing if (anim->getObjectRoot()) anim->getObjectRoot()->addCullCallback(new SceneUtil::LightListCallback); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 3b3ae4b2c..38ead0925 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -413,7 +413,7 @@ namespace NifOsg continue; osg::ref_ptr callback(new NifOsg::KeyframeController(key->data.getPtr())); - callback->mFunction = boost::shared_ptr(new NifOsg::ControllerFunction(key)); + callback->setFunction(boost::shared_ptr(new NifOsg::ControllerFunction(key))); target.mKeyframeControllers[strdata->string] = callback; } @@ -459,9 +459,9 @@ namespace NifOsg { bool autoPlay = animflags & Nif::NiNode::AnimFlag_AutoPlay; if (autoPlay) - toSetup->mSource = boost::shared_ptr(new SceneUtil::FrameTimeSource); + toSetup->setSource(boost::shared_ptr(new SceneUtil::FrameTimeSource)); - toSetup->mFunction = boost::shared_ptr(new ControllerFunction(ctrl)); + toSetup->setFunction(boost::shared_ptr(new ControllerFunction(ctrl))); } static osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::TextureManager* textureManager, diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index b6e9b1c8f..f2f3c2534 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -44,7 +44,8 @@ namespace NifOsg public: TextKeyMap mTextKeys; - std::map > mKeyframeControllers; + typedef std::map > KeyframeControllerMap; + KeyframeControllerMap mKeyframeControllers; }; /// The main class responsible for loading NIF files into an OSG-Scenegraph. diff --git a/components/sceneutil/controller.cpp b/components/sceneutil/controller.cpp index 79a75063a..6beb1bc80 100644 --- a/components/sceneutil/controller.cpp +++ b/components/sceneutil/controller.cpp @@ -23,6 +23,26 @@ namespace SceneUtil return mFunction->calculate(mSource->getValue(nv)); } + void Controller::setSource(boost::shared_ptr source) + { + mSource = source; + } + + void Controller::setFunction(boost::shared_ptr function) + { + mFunction = function; + } + + boost::shared_ptr Controller::getSource() const + { + return mSource; + } + + boost::shared_ptr Controller::getFunction() const + { + return mFunction; + } + FrameTimeSource::FrameTimeSource() { } @@ -85,8 +105,8 @@ namespace SceneUtil void AssignControllerSourcesVisitor::visit(osg::Node&, Controller &ctrl) { - if (!ctrl.mSource.get()) - ctrl.mSource = mToAssign; + if (!ctrl.getSource()) + ctrl.setSource(mToAssign); } FindMaxControllerLengthVisitor::FindMaxControllerLengthVisitor() @@ -97,8 +117,8 @@ namespace SceneUtil void FindMaxControllerLengthVisitor::visit(osg::Node &, Controller &ctrl) { - if (ctrl.mFunction) - mMaxLength = std::max(mMaxLength, ctrl.mFunction->getMaximum()); + if (ctrl.getFunction()) + mMaxLength = std::max(mMaxLength, ctrl.getFunction()->getMaximum()); } float FindMaxControllerLengthVisitor::getMaxLength() const diff --git a/components/sceneutil/controller.hpp b/components/sceneutil/controller.hpp index 6086663bd..84fe6e896 100644 --- a/components/sceneutil/controller.hpp +++ b/components/sceneutil/controller.hpp @@ -41,6 +41,13 @@ namespace SceneUtil float getInputValue(osg::NodeVisitor* nv); + void setSource(boost::shared_ptr source); + void setFunction(boost::shared_ptr function); + + boost::shared_ptr getSource() const; + boost::shared_ptr getFunction() const; + + private: boost::shared_ptr mSource; // The source value gets passed through this function before it's passed on to the DestValue.