Animation playback works, no movement accumulation yet

c++11
scrawl 10 years ago
parent 6fada6acf8
commit 09742d5b95

@ -1,11 +1,13 @@
#include "animation.hpp"
#include <iomanip>
#include <limits>
#include <osg/PositionAttitudeTransform>
#include <osg/TexGen>
#include <osg/TexEnvCombine>
#include <osg/ComputeBoundsVisitor>
#include <osg/MatrixTransform>
#include <components/nifosg/nifloader.hpp>
@ -13,6 +15,10 @@
#include <components/resource/scenemanager.hpp>
#include <components/resource/texturemanager.hpp>
#include <components/nifosg/nifloader.hpp> // KeyframeHolder
#include <components/vfs/manager.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/sceneutil/statesetupdater.hpp>
@ -74,6 +80,89 @@ namespace
std::vector<osg::ref_ptr<osg::Texture2D> > 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<std::string, osg::ref_ptr<osg::MatrixTransform> > 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<float, std::string>& 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<float>::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<float, std::string> &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<AnimSource> 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<NifOsg::KeyframeController> 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<float, std::string>::const_iterator &key,
const std::multimap<float, std::string>& 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 "<<groupname<<" for "<<mPtr.getCellRef().getRefId() <<std::endl;
resetActiveGroups();
// This shouldn't be needed, same is already done in resetActiveGroups()
/*
if (!state.mPlaying && mNonAccumCtrl)
{
// If the animation state is not playing, we need to manually apply the accumulation
// (see updatePosition, which would be called if the animation was playing)
mAccumRoot->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<float>::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<<grp)))
continue;
if(active == mStates.end() || active->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<AnimSource> animsrc = active->second.mSource;
for (AnimSource::ControllerMap::iterator it = animsrc->mControllerMap[grp].begin(); it != animsrc->mControllerMap[grp].end(); ++it)
{
osg::ref_ptr<osg::Node> 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<Ogre::Controller<Ogre::Real> >&ctrls = (*animsrc)->mControllers[0];
for(size_t i = 0;i < ctrls.size();i++)
{
NifOsg::NodeTargetValue<Ogre::Real> *dstval;
dstval = static_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(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<Ogre::Controller<Ogre::Real> >&ctrls = (*animiter)->mControllers[0];
for(size_t i = 0;i < ctrls.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = static_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(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;

@ -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<const NifOsg::KeyframeHolder> mKeyframes;
typedef std::map<std::string, osg::ref_ptr<NifOsg::KeyframeController> > ControllerMap;
/*
struct AnimSource : public Ogre::AnimationAlloc {
//NifOgre::TextKeyMap mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
ControllerMap mControllerMap[sNumGroups];
const std::multimap<float, std::string>& getTextKeys();
};
typedef std::vector< Ogre::SharedPtr<AnimSource> > AnimSourceList;
*/
struct AnimState {
//Ogre::SharedPtr<AnimSource> mSource;
boost::shared_ptr<AnimSource> mSource;
float mStartTime;
float mLoopStartTime;
float mLoopStopTime;
@ -111,15 +118,38 @@ protected:
{ }
};
typedef std::map<std::string,AnimState> AnimStateMap;
AnimStateMap mStates;
typedef std::vector<boost::shared_ptr<AnimSource> > AnimSourceList;
AnimSourceList mAnimSources;
osg::ref_ptr<osg::Group> mInsert;
osg::ref_ptr<osg::Node> mObjectRoot;
// The node expected to accumulate movement during movement animations.
osg::ref_ptr<osg::Node> mAccumRoot;
// The controller animating that node.
osg::ref_ptr<NifOsg::KeyframeController> 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<osg::Node>, osg::ref_ptr<NifOsg::KeyframeController> > AnimSourceControllerMap;
AnimSourceControllerMap mAnimSourceControllers;
boost::shared_ptr<AnimationTime> mAnimationTimePtr[sNumGroups];
// Stored in all lowercase for a case-insensitive lookup
typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > 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<Ogre::Real> *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<float, std::string> &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<float, std::string>::const_iterator &key,
const std::multimap<float, std::string>& 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 {

@ -22,16 +22,16 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr,
const std::string& model, Resource::ResourceSystem* resourceSystem)
: Animation(ptr, osg::ref_ptr<osg::Group>(ptr.getRefData().getBaseNode()), resourceSystem)
{
//MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
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<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
MWWorld::LiveCellRef<ESM::Creature> *ref = mPtr.get<ESM::Creature>();
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);

@ -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);

@ -167,6 +167,7 @@ void Objects::insertNPC(const MWWorld::Ptr &ptr)
insertBegin(ptr);
std::auto_ptr<NpcAnimation> anim (new NpcAnimation(ptr, osg::ref_ptr<osg::Group>(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);

@ -413,7 +413,7 @@ namespace NifOsg
continue;
osg::ref_ptr<NifOsg::KeyframeController> callback(new NifOsg::KeyframeController(key->data.getPtr()));
callback->mFunction = boost::shared_ptr<NifOsg::ControllerFunction>(new NifOsg::ControllerFunction(key));
callback->setFunction(boost::shared_ptr<NifOsg::ControllerFunction>(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<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource);
toSetup->setSource(boost::shared_ptr<SceneUtil::ControllerSource>(new SceneUtil::FrameTimeSource));
toSetup->mFunction = boost::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl));
toSetup->setFunction(boost::shared_ptr<ControllerFunction>(new ControllerFunction(ctrl)));
}
static osg::ref_ptr<osg::Node> handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::TextureManager* textureManager,

@ -44,7 +44,8 @@ namespace NifOsg
public:
TextKeyMap mTextKeys;
std::map<std::string, osg::ref_ptr<KeyframeController> > mKeyframeControllers;
typedef std::map<std::string, osg::ref_ptr<const KeyframeController> > KeyframeControllerMap;
KeyframeControllerMap mKeyframeControllers;
};
/// The main class responsible for loading NIF files into an OSG-Scenegraph.

@ -23,6 +23,26 @@ namespace SceneUtil
return mFunction->calculate(mSource->getValue(nv));
}
void Controller::setSource(boost::shared_ptr<ControllerSource> source)
{
mSource = source;
}
void Controller::setFunction(boost::shared_ptr<ControllerFunction> function)
{
mFunction = function;
}
boost::shared_ptr<ControllerSource> Controller::getSource() const
{
return mSource;
}
boost::shared_ptr<ControllerFunction> 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

@ -41,6 +41,13 @@ namespace SceneUtil
float getInputValue(osg::NodeVisitor* nv);
void setSource(boost::shared_ptr<ControllerSource> source);
void setFunction(boost::shared_ptr<ControllerFunction> function);
boost::shared_ptr<ControllerSource> getSource() const;
boost::shared_ptr<ControllerFunction> getFunction() const;
private:
boost::shared_ptr<ControllerSource> mSource;
// The source value gets passed through this function before it's passed on to the DestValue.

Loading…
Cancel
Save