1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 05:19:55 +00:00
openmw-tes3mp/apps/openmw/mwrender/animation.cpp
Chris Robinson 376dfed15b Revert "Use a child scene node for the accumulation root"
This reverts commit d6f923f274.

We don't need it for any of the NIFs we're currently handling. As long as
there's no NIF files that would break it, we should require a stationary root
if an animation wants to accumulate. If we must, a better idea may be to inject
an extra bone into the skeleton instance and make that the accumulation root.
2013-02-01 08:50:32 -08:00

284 lines
8.6 KiB
C++

#include "animation.hpp"
#include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h>
#include <OgreEntity.h>
#include <OgreBone.h>
#include <OgreSubMesh.h>
#include <OgreSceneManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp"
namespace MWRender
{
Animation::Animation(const MWWorld::Ptr &ptr)
: mPtr(ptr)
, mController(NULL)
, mInsert(NULL)
, mAccumRoot(NULL)
, mNonAccumRoot(NULL)
, mAccumulate(Ogre::Vector3::ZERO)
, mStartPosition(0.0f)
, mLastPosition(0.0f)
, mCurrentKeys(NULL)
, mCurrentAnim(NULL)
, mCurrentTime(0.0f)
, mPlaying(false)
, mLooping(false)
, mAnimSpeedMult(1.0f)
{
}
Animation::~Animation()
{
if(mInsert)
{
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mEntityList.mEntities.size();i++)
sceneMgr->destroyEntity(mEntityList.mEntities[i]);
}
mEntityList.mEntities.clear();
mEntityList.mSkelBase = NULL;
}
void Animation::createEntityList(Ogre::SceneNode *node, const std::string &model)
{
mInsert = node;
assert(mInsert);
mEntityList = NifOgre::Loader::createEntities(mInsert, model);
if(mEntityList.mSkelBase)
{
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);
}
Ogre::SkeletonInstance *skelinst = mEntityList.mSkelBase->getSkeleton();
// Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr.
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
Ogre::SkeletonPtr skel = skelMgr.getByName(skelinst->getName());
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;
mAccumRoot = skelinst->getRootBone();
mAccumRoot->setManuallyControlled(true);
mNonAccumRoot = skelinst->getBone(bone->getHandle());
mStartPosition = mNonAccumRoot->getInitialPosition();
mLastPosition = mStartPosition;
asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{
Ogre::AnimationState *state = asiter.getNext();
const Ogre::Any &groupdata = bindings.getUserAny(std::string(NifOgre::sTextKeyExtraDataID)+
"@"+state->getAnimationName());
if(!groupdata.isEmpty())
mTextKeys[state->getAnimationName()] = Ogre::any_cast<NifOgre::TextKeyMap>(groupdata);
}
break;
}
// 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).
boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
}
}
bool Animation::hasAnimation(const std::string &anim)
{
return mEntityList.mSkelBase && mEntityList.mSkelBase->getSkeleton()->hasAnimation(anim);
}
void Animation::setController(MWMechanics::CharacterController *controller)
{
mController = controller;
}
void Animation::setAccumulation(const Ogre::Vector3 &accum)
{
mAccumulate = accum;
}
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();
}
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());
}
}
Ogre::Vector3 Animation::updatePosition(float time)
{
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());
Ogre::Vector3 posdiff = Ogre::Vector3::ZERO;
if(mNonAccumRoot)
{
/* Get the non-accumulation root's difference from the last update. */
posdiff = (mNonAccumRoot->getPosition() - mLastPosition) * mAccumulate;
/* Translate the accumulation root back to compensate for the move. */
mAccumRoot->translate(-posdiff);
mLastPosition += posdiff;
}
return posdiff;
}
void Animation::reset(const std::string &marker)
{
mNextKey = mCurrentKeys->begin();
while(mNextKey != mCurrentKeys->end() && mNextKey->second != marker)
mNextKey++;
if(mNextKey != mCurrentKeys->end())
mCurrentTime = mNextKey->first;
else
{
mNextKey = mCurrentKeys->begin();
mCurrentTime = 0.0f;
}
applyAnimation(mCurrentAnim, mCurrentTime, mEntityList.mSkelBase->getSkeleton());
if(mNonAccumRoot)
{
mLastPosition = mNonAccumRoot->getPosition();
mAccumRoot->setPosition(mStartPosition - mLastPosition);
}
}
void Animation::play(const std::string &groupname, const std::string &start, bool loop)
{
try {
mCurrentAnim = mEntityList.mSkelBase->getSkeleton()->getAnimation(groupname);
mCurrentKeys = &mTextKeys[groupname];
reset(start);
mPlaying = true;
mLooping = loop;
}
catch(std::exception &e) {
std::cerr<< e.what() <<std::endl;
}
}
Ogre::Vector3 Animation::runAnimation(float timepassed)
{
Ogre::Vector3 movement = Ogre::Vector3::ZERO;
timepassed *= mAnimSpeedMult;
while(mCurrentAnim && mPlaying && timepassed > 0.0f)
{
float targetTime = mCurrentTime + timepassed;
if(mNextKey == mCurrentKeys->end() || mNextKey->first > targetTime)
{
movement += updatePosition(targetTime);
mPlaying = (mLooping || mCurrentAnim->getLength() >= targetTime);
break;
}
float time = mNextKey->first;
const std::string &evt = mNextKey->second;
mNextKey++;
movement += updatePosition(time);
timepassed = targetTime - time;
if(evt == "start" || evt == "loop start")
{
/* Do nothing */
continue;
}
if(evt == "loop stop")
{
if(mLooping)
{
reset("loop start");
if(mCurrentTime >= time)
break;
}
continue;
}
if(evt == "stop")
{
if(mLooping)
{
reset("loop start");
if(mCurrentTime >= time)
break;
}
else
{
mPlaying = false;
if(mController)
mController->markerEvent(time, evt);
}
continue;
}
if(mController)
mController->markerEvent(time, evt);
}
return movement;
}
}