2011-12-11 23:42:39 -05:00
|
|
|
#include "animation.hpp"
|
|
|
|
|
2013-01-05 04:01:11 -08:00
|
|
|
#include <OgreSkeletonManager.h>
|
2012-07-03 15:32:38 +02:00
|
|
|
#include <OgreSkeletonInstance.h>
|
|
|
|
#include <OgreEntity.h>
|
|
|
|
#include <OgreBone.h>
|
|
|
|
#include <OgreSubMesh.h>
|
2012-07-18 00:17:39 -07:00
|
|
|
#include <OgreSceneManager.h>
|
2011-12-17 01:29:08 -05:00
|
|
|
|
2013-01-06 21:18:48 -08:00
|
|
|
#include "../mwbase/environment.hpp"
|
|
|
|
#include "../mwbase/world.hpp"
|
|
|
|
|
2013-01-16 13:09:21 -08:00
|
|
|
#include "../mwmechanics/character.hpp"
|
|
|
|
|
2012-07-13 03:51:58 -07:00
|
|
|
namespace MWRender
|
|
|
|
{
|
2012-07-12 20:12:18 -07:00
|
|
|
|
2013-01-06 17:05:48 -08:00
|
|
|
Animation::Animation(const MWWorld::Ptr &ptr)
|
|
|
|
: mPtr(ptr)
|
2013-01-16 13:09:21 -08:00
|
|
|
, mController(NULL)
|
2013-01-06 17:05:48 -08:00
|
|
|
, mInsert(NULL)
|
2013-01-06 21:18:48 -08:00
|
|
|
, mAccumRoot(NULL)
|
2013-01-06 17:31:53 -08:00
|
|
|
, mNonAccumRoot(NULL)
|
2013-01-18 14:25:32 -08:00
|
|
|
, mAccumulate(Ogre::Vector3::ZERO)
|
2013-01-07 04:48:59 -08:00
|
|
|
, mStartPosition(0.0f)
|
2013-01-06 21:18:48 -08:00
|
|
|
, mLastPosition(0.0f)
|
2013-01-17 13:18:40 -08:00
|
|
|
, mCurrentKeys(NULL)
|
2013-01-30 07:34:07 -08:00
|
|
|
, mCurrentAnim(NULL)
|
|
|
|
, mCurrentTime(0.0f)
|
2013-01-29 00:43:42 -08:00
|
|
|
, mPlaying(false)
|
2013-01-30 07:34:07 -08:00
|
|
|
, mLooping(false)
|
2013-01-18 21:40:47 -08:00
|
|
|
, mAnimSpeedMult(1.0f)
|
2012-07-12 20:12:18 -07:00
|
|
|
{
|
2011-12-17 01:29:08 -05:00
|
|
|
}
|
2012-02-20 14:02:24 +01:00
|
|
|
|
2012-07-12 20:12:18 -07:00
|
|
|
Animation::~Animation()
|
|
|
|
{
|
2013-01-04 23:19:48 -08:00
|
|
|
if(mInsert)
|
|
|
|
{
|
|
|
|
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
|
|
|
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
|
|
|
|
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
|
|
|
|
}
|
2012-07-18 00:17:39 -07:00
|
|
|
mEntityList.mEntities.clear();
|
2013-01-04 23:19:48 -08:00
|
|
|
mEntityList.mSkelBase = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-02-05 16:29:51 -08:00
|
|
|
Ogre::Bone *Animation::insertSkeletonSource(const std::string &name)
|
|
|
|
{
|
|
|
|
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
|
|
|
|
Ogre::SkeletonPtr skel = skelMgr.getByName(name);
|
|
|
|
if(skel.isNull())
|
|
|
|
{
|
|
|
|
std::cerr<< "Failed to get skeleton source "<<name <<std::endl;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
skel->touch();
|
|
|
|
mSkeletonSources.push_back(skel);
|
|
|
|
|
|
|
|
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator();
|
|
|
|
while(boneiter.hasMoreElements())
|
|
|
|
{
|
|
|
|
Ogre::Bone *bone = boneiter.getNext();
|
|
|
|
Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings();
|
|
|
|
const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID);
|
|
|
|
if(data.isEmpty() || !Ogre::any_cast<bool>(data))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for(int i = 0;i < skel->getNumAnimations();i++)
|
|
|
|
{
|
|
|
|
Ogre::Animation *anim = skel->getAnimation(i);
|
|
|
|
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+
|
|
|
|
"@"+anim->getName());
|
|
|
|
if(!groupdata.isEmpty())
|
|
|
|
mTextKeys[anim->getName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
return bone;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-04 23:19:48 -08:00
|
|
|
void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model)
|
|
|
|
{
|
2013-02-02 05:43:37 -08:00
|
|
|
mInsert = node->createChildSceneNode();
|
2013-01-04 23:19:48 -08:00
|
|
|
assert(mInsert);
|
|
|
|
|
2013-01-09 09:10:59 -08:00
|
|
|
mEntityList = NifOgre::Loader::createEntities(mInsert, model);
|
2013-01-04 23:19:48 -08:00
|
|
|
if(mEntityList.mSkelBase)
|
|
|
|
{
|
2013-01-09 03:30:55 -08:00
|
|
|
Ogre::AnimationStateSet *aset = mEntityList.mSkelBase->getAllAnimationStates();
|
|
|
|
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
|
|
|
|
while(asiter.hasMoreElements())
|
|
|
|
{
|
|
|
|
Ogre::AnimationState *state = asiter.getNext();
|
|
|
|
state->setEnabled(false);
|
|
|
|
state->setLoop(false);
|
|
|
|
}
|
|
|
|
|
2013-02-05 16:29:51 -08:00
|
|
|
// Set the bones as manually controlled since we're applying the
|
|
|
|
// transformations manually (needed if we want to apply an animation
|
|
|
|
// from one skeleton onto another).
|
2013-01-06 21:18:48 -08:00
|
|
|
Ogre::SkeletonInstance *skelinst = mEntityList.mSkelBase->getSkeleton();
|
2013-02-05 16:29:51 -08:00
|
|
|
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
|
2013-01-09 03:30:55 -08:00
|
|
|
while(boneiter.hasMoreElements())
|
2013-02-05 16:29:51 -08:00
|
|
|
boneiter.getNext()->setManuallyControlled(true);
|
2013-01-07 05:56:03 -08:00
|
|
|
|
2013-02-05 16:29:51 -08:00
|
|
|
Ogre::Bone *bone = insertSkeletonSource(skelinst->getName());
|
2013-02-05 17:15:40 -08:00
|
|
|
if(!bone)
|
|
|
|
{
|
|
|
|
for(std::vector<Ogre::SkeletonPtr>::const_iterator iter(mSkeletonSources.begin());
|
|
|
|
!bone && iter != mSkeletonSources.end();iter++)
|
|
|
|
{
|
|
|
|
Ogre::Skeleton::BoneIterator boneiter = (*iter)->getBoneIterator();
|
|
|
|
while(boneiter.hasMoreElements())
|
|
|
|
{
|
|
|
|
bone = boneiter.getNext();
|
|
|
|
Ogre::UserObjectBindings &bindings = bone->getUserObjectBindings();
|
|
|
|
const Ogre::Any &data = bindings.getUserAny(NifOgre::sTextKeyExtraDataID);
|
|
|
|
if(!data.isEmpty() && Ogre::any_cast<bool>(data))
|
|
|
|
break;
|
|
|
|
|
|
|
|
bone = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-05 16:29:51 -08:00
|
|
|
if(bone)
|
|
|
|
{
|
2013-02-02 05:43:37 -08:00
|
|
|
mAccumRoot = mInsert;
|
2013-02-05 17:15:40 -08:00
|
|
|
mNonAccumRoot = skelinst->getBone(bone->getName());
|
2013-01-20 15:39:43 -08:00
|
|
|
|
2013-01-26 04:48:53 -08:00
|
|
|
mStartPosition = mNonAccumRoot->getInitialPosition();
|
2013-01-20 15:39:43 -08:00
|
|
|
mLastPosition = mStartPosition;
|
2013-01-07 05:23:44 -08:00
|
|
|
}
|
2013-01-04 23:19:48 -08:00
|
|
|
}
|
2012-07-12 20:12:18 -07:00
|
|
|
}
|
2012-01-06 22:52:15 -05:00
|
|
|
|
2012-07-24 13:51:48 -07:00
|
|
|
|
2013-01-19 21:55:04 -08:00
|
|
|
bool Animation::hasAnimation(const std::string &anim)
|
2013-01-16 15:00:06 -08:00
|
|
|
{
|
2013-02-05 16:29:51 -08:00
|
|
|
for(std::vector<Ogre::SkeletonPtr>::const_iterator iter(mSkeletonSources.begin());iter != mSkeletonSources.end();iter++)
|
|
|
|
{
|
|
|
|
if((*iter)->hasAnimation(anim))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2013-01-16 15:00:06 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-16 11:01:08 -08:00
|
|
|
void Animation::setController(MWMechanics::CharacterController *controller)
|
|
|
|
{
|
|
|
|
mController = controller;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-18 14:25:32 -08:00
|
|
|
void Animation::setAccumulation(const Ogre::Vector3 &accum)
|
|
|
|
{
|
|
|
|
mAccumulate = accum;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-30 07:04:18 -08:00
|
|
|
void Animation::applyAnimation(const Ogre::Animation *anim, float time, Ogre::SkeletonInstance *skel)
|
|
|
|
{
|
|
|
|
Ogre::TimeIndex timeindex = anim->_getTimeIndex(time);
|
|
|
|
Ogre::Animation::NodeTrackIterator tracks = anim->getNodeTrackIterator();
|
|
|
|
while(tracks.hasMoreElements())
|
|
|
|
{
|
|
|
|
Ogre::NodeAnimationTrack *track = tracks.getNext();
|
|
|
|
const Ogre::String &targetname = track->getAssociatedNode()->getName();
|
|
|
|
if(!skel->hasBone(targetname))
|
|
|
|
continue;
|
|
|
|
Ogre::Bone *bone = skel->getBone(targetname);
|
|
|
|
bone->setOrientation(Ogre::Quaternion::IDENTITY);
|
|
|
|
bone->setPosition(Ogre::Vector3::ZERO);
|
|
|
|
bone->setScale(Ogre::Vector3::UNIT_SCALE);
|
|
|
|
track->applyToNode(bone, timeindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
// HACK: Dirty the animation state set so that Ogre will apply the
|
|
|
|
// transformations to entities this skeleton instance is shared with.
|
|
|
|
mEntityList.mSkelBase->getAllAnimationStates()->_notifyDirty();
|
|
|
|
}
|
|
|
|
|
2013-01-30 09:29:16 -08:00
|
|
|
void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel)
|
|
|
|
{
|
|
|
|
Ogre::Skeleton::BoneIterator boneiter = skel->getBoneIterator();
|
|
|
|
while(boneiter.hasMoreElements())
|
|
|
|
{
|
|
|
|
Ogre::Bone *bone = boneiter.getNext();
|
|
|
|
if(!skelsrc->hasBone(bone->getName()))
|
|
|
|
continue;
|
|
|
|
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
|
|
|
|
bone->setOrientation(srcbone->getOrientation());
|
|
|
|
bone->setPosition(srcbone->getPosition());
|
|
|
|
bone->setScale(srcbone->getScale());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-30 07:04:18 -08:00
|
|
|
|
2013-01-18 16:21:29 -08:00
|
|
|
Ogre::Vector3 Animation::updatePosition(float time)
|
2013-01-06 21:18:48 -08:00
|
|
|
{
|
2013-01-30 07:34:07 -08:00
|
|
|
if(mLooping)
|
|
|
|
mCurrentTime = std::fmod(std::max(time, 0.0f), mCurrentAnim->getLength());
|
|
|
|
else
|
|
|
|
mCurrentTime = std::min(mCurrentAnim->getLength(), std::max(time, 0.0f));
|
|
|
|
applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton());
|
2013-01-16 20:14:49 -08:00
|
|
|
|
2013-01-18 16:21:29 -08:00
|
|
|
Ogre::Vector3 posdiff = Ogre::Vector3::ZERO;
|
2013-01-06 21:18:48 -08:00
|
|
|
if(mNonAccumRoot)
|
|
|
|
{
|
2013-01-30 07:04:18 -08:00
|
|
|
/* Get the non-accumulation root's difference from the last update. */
|
2013-01-18 16:21:29 -08:00
|
|
|
posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate;
|
2013-01-06 21:18:48 -08:00
|
|
|
|
|
|
|
/* Translate the accumulation root back to compensate for the move. */
|
2013-02-05 16:59:20 -08:00
|
|
|
mAccumRoot->translate(-posdiff * mAccumRoot->_getDerivedScale());
|
2013-01-06 21:18:48 -08:00
|
|
|
mLastPosition += posdiff;
|
|
|
|
}
|
2013-01-18 16:21:29 -08:00
|
|
|
return posdiff;
|
2013-01-06 21:18:48 -08:00
|
|
|
}
|
|
|
|
|
2013-01-22 00:31:45 -08:00
|
|
|
void Animation::reset(const std::string &marker)
|
2013-01-06 21:18:48 -08:00
|
|
|
{
|
2013-01-17 13:18:40 -08:00
|
|
|
mNextKey = mCurrentKeys->begin();
|
2013-01-22 00:31:45 -08:00
|
|
|
while(mNextKey != mCurrentKeys->end() && mNextKey->second != marker)
|
2013-01-17 13:18:40 -08:00
|
|
|
mNextKey++;
|
2013-01-16 11:57:08 -08:00
|
|
|
|
2013-01-22 00:31:45 -08:00
|
|
|
if(mNextKey != mCurrentKeys->end())
|
2013-01-30 07:34:07 -08:00
|
|
|
mCurrentTime = mNextKey->first;
|
2013-01-22 00:31:45 -08:00
|
|
|
else
|
2013-01-09 03:30:55 -08:00
|
|
|
{
|
2013-01-17 13:18:40 -08:00
|
|
|
mNextKey = mCurrentKeys->begin();
|
2013-01-30 07:34:07 -08:00
|
|
|
mCurrentTime = 0.0f;
|
2013-01-09 03:30:55 -08:00
|
|
|
}
|
2013-01-30 07:34:07 -08:00
|
|
|
applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton());
|
2013-01-09 03:30:55 -08:00
|
|
|
|
2013-01-22 00:31:45 -08:00
|
|
|
if(mNonAccumRoot)
|
2012-07-24 13:51:48 -07:00
|
|
|
{
|
2013-01-22 00:31:45 -08:00
|
|
|
mLastPosition = mNonAccumRoot->getPosition();
|
2013-02-05 16:59:20 -08:00
|
|
|
mAccumRoot->setPosition((mStartPosition - mLastPosition) * mAccumRoot->_getDerivedScale());
|
2012-07-24 13:51:48 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-21 22:51:13 -08:00
|
|
|
void Animation::play(const std::string &groupname, const std::string &start, bool loop)
|
2012-07-12 20:12:18 -07:00
|
|
|
{
|
2013-01-09 01:40:38 -08:00
|
|
|
try {
|
2013-02-05 16:29:51 -08:00
|
|
|
bool found = false;
|
|
|
|
/* Look in reverse; last-inserted source has priority. */
|
|
|
|
for(std::vector<Ogre::SkeletonPtr>::const_reverse_iterator iter(mSkeletonSources.rbegin());iter != mSkeletonSources.rend();iter++)
|
|
|
|
{
|
|
|
|
if((*iter)->hasAnimation(groupname))
|
|
|
|
{
|
|
|
|
mCurrentAnim = (*iter)->getAnimation(groupname);
|
|
|
|
mCurrentKeys = &mTextKeys[groupname];
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!found)
|
|
|
|
throw std::runtime_error("Failed to find animation "+groupname);
|
2013-01-30 07:34:07 -08:00
|
|
|
|
2013-01-22 00:31:45 -08:00
|
|
|
reset(start);
|
2013-01-29 00:43:42 -08:00
|
|
|
mPlaying = true;
|
2013-01-30 07:34:07 -08:00
|
|
|
mLooping = loop;
|
2012-07-24 13:51:48 -07:00
|
|
|
}
|
2013-01-09 01:40:38 -08:00
|
|
|
catch(std::exception &e) {
|
|
|
|
std::cerr<< e.what() <<std::endl;
|
2013-01-07 05:56:03 -08:00
|
|
|
}
|
2012-07-24 13:51:48 -07:00
|
|
|
}
|
2011-12-15 00:33:10 -05:00
|
|
|
|
2013-01-18 16:21:29 -08:00
|
|
|
Ogre::Vector3 Animation::runAnimation(float timepassed)
|
2012-07-21 14:41:26 -07:00
|
|
|
{
|
2013-01-18 16:21:29 -08:00
|
|
|
Ogre::Vector3 movement = Ogre::Vector3::ZERO;
|
2013-01-30 07:04:18 -08:00
|
|
|
|
2013-01-18 21:40:47 -08:00
|
|
|
timepassed *= mAnimSpeedMult;
|
2013-02-03 01:38:42 -08:00
|
|
|
while(mCurrentAnim && mPlaying)
|
2012-07-21 14:41:26 -07:00
|
|
|
{
|
2013-01-30 07:34:07 -08:00
|
|
|
float targetTime = mCurrentTime + timepassed;
|
2013-01-17 14:49:42 -08:00
|
|
|
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
|
2012-07-24 13:51:48 -07:00
|
|
|
{
|
2013-01-18 16:21:29 -08:00
|
|
|
movement += updatePosition(targetTime);
|
2013-01-30 07:34:07 -08:00
|
|
|
mPlaying = (mLooping || mCurrentAnim->getLength() >= targetTime);
|
2013-01-17 14:49:42 -08:00
|
|
|
break;
|
2013-01-16 11:57:08 -08:00
|
|
|
}
|
2012-07-21 14:41:26 -07:00
|
|
|
|
2013-01-18 14:50:55 -08:00
|
|
|
float time = mNextKey->first;
|
2013-01-17 14:49:42 -08:00
|
|
|
const std::string &evt = mNextKey->second;
|
|
|
|
mNextKey++;
|
|
|
|
|
2013-01-18 16:21:29 -08:00
|
|
|
movement += updatePosition(time);
|
2013-01-18 14:50:55 -08:00
|
|
|
timepassed = targetTime - time;
|
|
|
|
|
2013-01-21 22:51:13 -08:00
|
|
|
if(evt == "start" || evt == "loop start")
|
|
|
|
{
|
|
|
|
/* Do nothing */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(evt == "loop stop")
|
|
|
|
{
|
2013-01-30 07:34:07 -08:00
|
|
|
if(mLooping)
|
2013-01-21 22:51:13 -08:00
|
|
|
{
|
2013-01-22 00:31:45 -08:00
|
|
|
reset("loop start");
|
2013-01-30 07:34:07 -08:00
|
|
|
if(mCurrentTime >= time)
|
2013-01-21 22:51:13 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(evt == "stop")
|
|
|
|
{
|
2013-01-30 07:34:07 -08:00
|
|
|
if(mLooping)
|
2013-01-21 22:51:13 -08:00
|
|
|
{
|
2013-01-22 00:31:45 -08:00
|
|
|
reset("loop start");
|
2013-01-30 07:34:07 -08:00
|
|
|
if(mCurrentTime >= time)
|
2013-01-21 22:51:13 -08:00
|
|
|
break;
|
|
|
|
}
|
2013-01-29 00:43:42 -08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
mPlaying = false;
|
|
|
|
if(mController)
|
|
|
|
mController->markerEvent(time, evt);
|
|
|
|
}
|
2013-01-21 22:51:13 -08:00
|
|
|
continue;
|
|
|
|
}
|
2013-01-17 14:49:42 -08:00
|
|
|
if(mController)
|
2013-01-18 14:50:55 -08:00
|
|
|
mController->markerEvent(time, evt);
|
2012-07-21 14:41:26 -07:00
|
|
|
}
|
2013-01-30 07:04:18 -08:00
|
|
|
|
2013-01-18 16:21:29 -08:00
|
|
|
return movement;
|
2012-07-21 14:41:26 -07:00
|
|
|
}
|
|
|
|
|
2012-02-20 14:02:24 +01:00
|
|
|
}
|