mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 22:23:51 +00:00
Use time values for the aniamtion's start, stop, and loop points
This seems to be closer to the expected behavior. This more cleanly handles 0- length animations, especially where the start marker comes after the stop marker while still being on the same time key.
This commit is contained in:
parent
bf1d907d07
commit
59f1bc7542
2 changed files with 93 additions and 81 deletions
|
@ -444,64 +444,58 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi
|
||||||
|
|
||||||
bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
|
bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
|
||||||
{
|
{
|
||||||
std::string tag = groupname+": "+start;
|
const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname);
|
||||||
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
|
|
||||||
while(startkey != keys.end() && startkey->second != tag)
|
std::string starttag = groupname+": "+start;
|
||||||
|
NifOgre::TextKeyMap::const_iterator startkey(groupstart);
|
||||||
|
while(startkey != keys.end() && startkey->second != starttag)
|
||||||
startkey++;
|
startkey++;
|
||||||
if(startkey == keys.end() && start == "loop start")
|
if(startkey == keys.end() && start == "loop start")
|
||||||
{
|
{
|
||||||
tag = groupname+": start";
|
starttag = groupname+": start";
|
||||||
startkey = keys.begin();
|
startkey = groupstart;
|
||||||
while(startkey != keys.end() && startkey->second != tag)
|
while(startkey != keys.end() && startkey->second != starttag)
|
||||||
startkey++;
|
startkey++;
|
||||||
}
|
}
|
||||||
if(startkey == keys.end())
|
if(startkey == keys.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
tag = groupname+": "+stop;
|
const std::string stoptag = groupname+": "+stop;
|
||||||
NifOgre::TextKeyMap::const_iterator stopkey(startkey);
|
NifOgre::TextKeyMap::const_iterator stopkey(groupstart);
|
||||||
while(stopkey != keys.end() && stopkey->second != tag)
|
while(stopkey != keys.end() && stopkey->second != stoptag)
|
||||||
stopkey++;
|
stopkey++;
|
||||||
if(stopkey == keys.end())
|
if(stopkey == keys.end())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(startkey == stopkey)
|
if(startkey->first > stopkey->first)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
state.mStartKey = startkey;
|
state.mStartTime = startkey->first;
|
||||||
state.mLoopStartKey = startkey;
|
state.mLoopStartTime = startkey->first;
|
||||||
state.mStopKey = stopkey;
|
state.mLoopStopTime = stopkey->first;
|
||||||
state.mNextKey = startkey;
|
state.mStopTime = stopkey->first;
|
||||||
|
|
||||||
state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint);
|
state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint);
|
||||||
|
if(state.mTime > state.mStartTime)
|
||||||
tag = groupname+": loop start";
|
|
||||||
while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey)
|
|
||||||
{
|
{
|
||||||
if(state.mNextKey->second == tag)
|
const std::string loopstarttag = groupname+": loop start";
|
||||||
state.mLoopStartKey = state.mNextKey;
|
const std::string loopstoptag = groupname+": loop stop";
|
||||||
state.mNextKey++;
|
NifOgre::TextKeyMap::const_iterator key(groupstart);
|
||||||
|
while(key->first <= state.mTime && key != stopkey)
|
||||||
|
{
|
||||||
|
if(key->second == loopstarttag)
|
||||||
|
state.mLoopStartTime = key->first;
|
||||||
|
else if(key->second == loopstoptag)
|
||||||
|
state.mLoopStopTime = key->first;
|
||||||
|
key++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Animation::doLoop(AnimState &state)
|
|
||||||
{
|
|
||||||
if(state.mLoopCount == 0)
|
|
||||||
return false;
|
|
||||||
state.mLoopCount--;
|
|
||||||
|
|
||||||
state.mTime = state.mLoopStartKey->first;
|
void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key)
|
||||||
state.mNextKey = state.mLoopStartKey;
|
|
||||||
state.mNextKey++;
|
|
||||||
state.mPlaying = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key)
|
|
||||||
{
|
{
|
||||||
float time = key->first;
|
float time = key->first;
|
||||||
const std::string &evt = key->second;
|
const std::string &evt = key->second;
|
||||||
|
@ -510,7 +504,7 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co
|
||||||
{
|
{
|
||||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||||
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
|
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
if(evt.compare(0, 10, "soundgen: ") == 0)
|
if(evt.compare(0, 10, "soundgen: ") == 0)
|
||||||
{
|
{
|
||||||
|
@ -523,28 +517,22 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co
|
||||||
type = MWBase::SoundManager::Play_TypeFoot;
|
type = MWBase::SoundManager::Play_TypeFoot;
|
||||||
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type);
|
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type);
|
||||||
}
|
}
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(evt.compare(0, groupname.size(), groupname) != 0 ||
|
if(evt.compare(0, groupname.size(), groupname) != 0 ||
|
||||||
evt.compare(groupname.size(), 2, ": ") != 0)
|
evt.compare(groupname.size(), 2, ": ") != 0)
|
||||||
{
|
{
|
||||||
// Not ours, skip it
|
// Not ours, skip it
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
size_t off = groupname.size()+2;
|
size_t off = groupname.size()+2;
|
||||||
size_t len = evt.size() - off;
|
size_t len = evt.size() - off;
|
||||||
|
|
||||||
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
|
if(evt.compare(off, len, "loop start") == 0)
|
||||||
state.mLoopStartKey = key;
|
state.mLoopStartTime = key->first;
|
||||||
else if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
|
else if(evt.compare(off, len, "loop stop") == 0)
|
||||||
{
|
state.mLoopStopTime = key->first;
|
||||||
if(doLoop(state))
|
|
||||||
{
|
|
||||||
if(state.mTime >= time)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(evt.compare(off, len, "equip attach") == 0)
|
else if(evt.compare(off, len, "equip attach") == 0)
|
||||||
showWeapons(true);
|
showWeapons(true);
|
||||||
else if(evt.compare(off, len, "unequip detach") == 0)
|
else if(evt.compare(off, len, "unequip detach") == 0)
|
||||||
|
@ -557,8 +545,6 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co
|
||||||
MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Thrust);
|
MWWorld::Class::get(mPtr).attack(mPtr, MWMechanics::CreatureStats::AT_Thrust);
|
||||||
else if(evt.compare(off, len, "hit") == 0)
|
else if(evt.compare(off, len, "hit") == 0)
|
||||||
MWWorld::Class::get(mPtr).attack(mPtr, -1);
|
MWWorld::Class::get(mPtr).attack(mPtr, -1);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -596,8 +582,9 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
|
||||||
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
|
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
|
||||||
for(;iter != mAnimSources.rend();iter++)
|
for(;iter != mAnimSources.rend();iter++)
|
||||||
{
|
{
|
||||||
|
const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys;
|
||||||
AnimState state;
|
AnimState state;
|
||||||
if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint))
|
if(reset(state, textkeys, groupname, start, stop, startpoint))
|
||||||
{
|
{
|
||||||
state.mSource = *iter;
|
state.mSource = *iter;
|
||||||
state.mSpeedMult = speedmult;
|
state.mSpeedMult = speedmult;
|
||||||
|
@ -608,6 +595,13 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
|
||||||
state.mAutoDisable = autodisable;
|
state.mAutoDisable = autodisable;
|
||||||
mStates[groupname] = state;
|
mStates[groupname] = state;
|
||||||
|
|
||||||
|
NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime));
|
||||||
|
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||||
|
{
|
||||||
|
handleTextKey(state, groupname, textkey);
|
||||||
|
textkey++;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,29 +662,25 @@ void Animation::resetActiveGroups()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult, std::string *start, std::string *stop) const
|
bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const
|
||||||
{
|
{
|
||||||
AnimStateMap::const_iterator iter = mStates.find(groupname);
|
AnimStateMap::const_iterator iter = mStates.find(groupname);
|
||||||
if(iter == mStates.end())
|
if(iter == mStates.end())
|
||||||
{
|
{
|
||||||
if(complete) *complete = 0.0f;
|
if(complete) *complete = 0.0f;
|
||||||
if(speedmult) *speedmult = 0.0f;
|
if(speedmult) *speedmult = 0.0f;
|
||||||
if(start) *start = "";
|
|
||||||
if(stop) *stop = "";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(complete)
|
if(complete)
|
||||||
{
|
{
|
||||||
if(iter->second.mStopKey->first > iter->second.mStartKey->first)
|
if(iter->second.mStopTime > iter->second.mStartTime)
|
||||||
*complete = (iter->second.mTime - iter->second.mStartKey->first) /
|
*complete = (iter->second.mTime - iter->second.mStartTime) /
|
||||||
(iter->second.mStopKey->first - iter->second.mStartKey->first);
|
(iter->second.mStopTime - iter->second.mStartTime);
|
||||||
else
|
else
|
||||||
*complete = (iter->second.mPlaying ? 0.0f : 1.0f);
|
*complete = (iter->second.mPlaying ? 0.0f : 1.0f);
|
||||||
}
|
}
|
||||||
if(speedmult) *speedmult = iter->second.mSpeedMult;
|
if(speedmult) *speedmult = iter->second.mSpeedMult;
|
||||||
if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2);
|
|
||||||
if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,27 +702,52 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
||||||
while(stateiter != mStates.end())
|
while(stateiter != mStates.end())
|
||||||
{
|
{
|
||||||
AnimState &state = stateiter->second;
|
AnimState &state = stateiter->second;
|
||||||
|
const NifOgre::TextKeyMap &textkeys = state.mSource->mTextKeys;
|
||||||
|
NifOgre::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime));
|
||||||
|
|
||||||
float timepassed = duration * state.mSpeedMult;
|
float timepassed = duration * state.mSpeedMult;
|
||||||
while(state.mPlaying)
|
while(state.mPlaying)
|
||||||
{
|
{
|
||||||
float targetTime = state.mTime + timepassed;
|
float targetTime = state.mTime + timepassed;
|
||||||
if(state.mNextKey->first > targetTime)
|
if(textkey == textkeys.end() || textkey->first > targetTime)
|
||||||
{
|
{
|
||||||
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
||||||
updatePosition(state.mTime, targetTime, movement);
|
updatePosition(state.mTime, targetTime, movement);
|
||||||
state.mTime = targetTime;
|
state.mTime = std::min(targetTime, state.mStopTime);
|
||||||
break;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
||||||
|
updatePosition(state.mTime, textkey->first, movement);
|
||||||
|
state.mTime = textkey->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
NifOgre::TextKeyMap::const_iterator key(state.mNextKey++);
|
state.mPlaying = (state.mTime < state.mStopTime);
|
||||||
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
|
||||||
updatePosition(state.mTime, key->first, movement);
|
|
||||||
state.mTime = key->first;
|
|
||||||
|
|
||||||
state.mPlaying = (key != state.mStopKey);
|
|
||||||
timepassed = targetTime - state.mTime;
|
timepassed = targetTime - state.mTime;
|
||||||
|
|
||||||
if(!handleTextKey(state, stateiter->first, key))
|
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||||
|
{
|
||||||
|
handleTextKey(state, stateiter->first, textkey);
|
||||||
|
textkey++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0)
|
||||||
|
{
|
||||||
|
state.mLoopCount--;
|
||||||
|
state.mTime = state.mLoopStartTime;
|
||||||
|
state.mPlaying = true;
|
||||||
|
if(state.mTime >= state.mLoopStopTime)
|
||||||
|
break;
|
||||||
|
|
||||||
|
textkey = textkeys.lower_bound(state.mTime);
|
||||||
|
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||||
|
{
|
||||||
|
handleTextKey(state, stateiter->first, textkey);
|
||||||
|
textkey++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(timepassed <= 0.0f)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,10 +69,10 @@ protected:
|
||||||
|
|
||||||
struct AnimState {
|
struct AnimState {
|
||||||
Ogre::SharedPtr<AnimSource> mSource;
|
Ogre::SharedPtr<AnimSource> mSource;
|
||||||
NifOgre::TextKeyMap::const_iterator mStartKey;
|
float mStartTime;
|
||||||
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
|
float mLoopStartTime;
|
||||||
NifOgre::TextKeyMap::const_iterator mStopKey;
|
float mLoopStopTime;
|
||||||
NifOgre::TextKeyMap::const_iterator mNextKey;
|
float mStopTime;
|
||||||
|
|
||||||
float mTime;
|
float mTime;
|
||||||
float mSpeedMult;
|
float mSpeedMult;
|
||||||
|
@ -84,7 +84,8 @@ protected:
|
||||||
int mGroups;
|
int mGroups;
|
||||||
bool mAutoDisable;
|
bool mAutoDisable;
|
||||||
|
|
||||||
AnimState() : mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
|
||||||
|
mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
||||||
mPriority(0), mGroups(0), mAutoDisable(true)
|
mPriority(0), mGroups(0), mAutoDisable(true)
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
@ -141,9 +142,7 @@ protected:
|
||||||
const std::string &groupname, const std::string &start, const std::string &stop,
|
const std::string &groupname, const std::string &start, const std::string &stop,
|
||||||
float startpoint);
|
float startpoint);
|
||||||
|
|
||||||
bool doLoop(AnimState &state);
|
void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
|
||||||
|
|
||||||
bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
|
|
||||||
|
|
||||||
/* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle
|
/* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle
|
||||||
* systems in the model are ignored (useful for NPCs, where only the skeleton is needed for
|
* systems in the model are ignored (useful for NPCs, where only the skeleton is needed for
|
||||||
|
@ -209,11 +208,9 @@ public:
|
||||||
* \param groupname Animation group to check.
|
* \param groupname Animation group to check.
|
||||||
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
|
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
|
||||||
* \param speedmult Stores the animation speed multiplier
|
* \param speedmult Stores the animation speed multiplier
|
||||||
* \param start Stores the start key
|
|
||||||
* \param stop Stores the stop key
|
|
||||||
* \return True if the animation is active, false otherwise.
|
* \return True if the animation is active, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL, std::string *start=NULL, std::string *stop=NULL) const;
|
bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const;
|
||||||
|
|
||||||
/** Disables the specified animation group;
|
/** Disables the specified animation group;
|
||||||
* \param groupname Animation group to disable.
|
* \param groupname Animation group to disable.
|
||||||
|
|
Loading…
Reference in a new issue