forked from mirror/openmw-tes3mp
Animation playback works, no movement accumulation yet
This commit is contained in:
parent
6fada6acf8
commit
09742d5b95
9 changed files with 791 additions and 60 deletions
|
@ -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;
|
||||
|
||||
/*
|
||||
struct AnimSource : public Ogre::AnimationAlloc {
|
||||
//NifOgre::TextKeyMap mTextKeys;
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
|
||||
typedef std::map<std::string, osg::ref_ptr<NifOsg::KeyframeController> > ControllerMap;
|
||||
|
||||
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…
Reference in a new issue