1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 07:19:54 +00:00
openmw-tes3mp/apps/openmw/mwrender/animation.cpp

491 lines
15 KiB
C++
Raw Normal View History

2011-12-12 04:42:39 +00:00
#include "animation.hpp"
#include <OgreSkeletonManager.h>
#include <OgreSkeletonInstance.h>
#include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreParticleSystem.h>
#include <OgreBone.h>
#include <OgreSubMesh.h>
#include <OgreSceneManager.h>
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp"
namespace MWRender
{
void Animation::destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects)
{
for(size_t i = 0;i < objects.mParticles.size();i++)
sceneMgr->destroyParticleSystem(objects.mParticles[i]);
for(size_t i = 0;i < objects.mEntities.size();i++)
sceneMgr->destroyEntity(objects.mEntities[i]);
objects.mControllers.clear();
objects.mCameras.clear();
objects.mParticles.clear();
objects.mEntities.clear();
objects.mSkelBase = NULL;
}
Animation::Animation(const MWWorld::Ptr &ptr)
: mAnimationBaseValuePtr(OGRE_NEW AnimationValue(this))
, mPtr(ptr)
, mController(NULL)
, mInsert(NULL)
, mSkelBase(NULL)
, mAccumRoot(NULL)
, mNonAccumRoot(NULL)
, mAccumulate(0.0f)
, mLastPosition(0.0f)
2013-04-07 21:56:23 +00:00
, mCurrentControllers(NULL)
, mCurrentKeys(NULL)
, mCurrentTime(0.0f)
, mPlaying(false)
, mLooping(false)
, mNonAccumCtrl(NULL)
, mAnimVelocity(0.0f)
, mAnimSpeedMult(1.0f)
{
}
2012-02-20 13:02:24 +00:00
Animation::~Animation()
{
if(mInsert)
{
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
for(size_t i = 0;i < mObjectLists.size();i++)
destroyObjectList(sceneMgr, mObjectLists[i]);
mObjectLists.clear();
}
}
void Animation::addObjectList(Ogre::SceneNode *node, const std::string &model, bool baseonly)
{
if(!mInsert)
{
mInsert = node->createChildSceneNode();
assert(mInsert);
}
mObjectLists.push_back(!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
NifOgre::Loader::createObjectBase(mInsert, model));
NifOgre::ObjectList &objlist = mObjectLists.back();
if(objlist.mSkelBase)
{
if(!mSkelBase)
mSkelBase = objlist.mSkelBase;
Ogre::AnimationStateSet *aset = objlist.mSkelBase->getAllAnimationStates();
Ogre::AnimationStateIterator asiter = aset->getAnimationStateIterator();
while(asiter.hasMoreElements())
{
Ogre::AnimationState *state = asiter.getNext();
state->setEnabled(false);
state->setLoop(false);
}
// 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).
Ogre::SkeletonInstance *skelinst = objlist.mSkelBase->getSkeleton();
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
2013-04-07 21:56:23 +00:00
Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
2013-04-22 07:22:31 +00:00
if(mSkelBase == objlist.mSkelBase)
{
if(objlist.mTextKeys.size() > 0)
{
mAccumRoot = mInsert;
mNonAccumRoot = baseinst->getBone(objlist.mTextKeys.begin()->first);
}
}
else
{
for(size_t i = 0;i < objlist.mControllers.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
if(!dstval) continue;
const Ogre::String &trgtname = dstval->getNode()->getName();
if(!baseinst->hasBone(trgtname)) continue;
Ogre::Bone *bone = baseinst->getBone(trgtname);
dstval->setNode(bone);
}
}
}
for(size_t i = 0;i < objlist.mControllers.size();i++)
{
if(objlist.mControllers[i].getSource().isNull())
objlist.mControllers[i].setSource(mAnimationBaseValuePtr);
}
if(!mCurrentControllers || (*mCurrentControllers).size() == 0)
mCurrentControllers = &objlist.mControllers;
}
2012-01-07 03:52:15 +00:00
void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue)
{
for(size_t i = 0;i < objlist.mEntities.size();i++)
{
Ogre::Entity *ent = objlist.mEntities[i];
if(visflags != 0)
ent->setVisibilityFlags(visflags);
for(unsigned int j = 0;j < ent->getNumSubEntities();++j)
{
Ogre::SubEntity* subEnt = ent->getSubEntity(j);
subEnt->setRenderQueueGroup(subEnt->getMaterial()->isTransparent() ? transqueue : solidqueue);
}
}
for(size_t i = 0;i < objlist.mParticles.size();i++)
{
Ogre::ParticleSystem *part = objlist.mParticles[i];
if(visflags != 0)
part->setVisibilityFlags(visflags);
// TODO: Check particle material for actual transparency
part->setRenderQueueGroup(transqueue);
}
}
Ogre::Node *Animation::getNode(const std::string &name)
{
if(mSkelBase)
{
Ogre::SkeletonInstance *skel = mSkelBase->getSkeleton();
if(skel->hasBone(name))
return skel->getBone(name);
}
return NULL;
}
bool Animation::hasAnimation(const std::string &anim)
{
for(std::vector<NifOgre::ObjectList>::const_iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++)
{
if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(anim))
return true;
}
return false;
}
void Animation::setController(MWMechanics::CharacterController *controller)
{
mController = controller;
}
void Animation::setAccumulation(const Ogre::Vector3 &accum)
{
mAccumulate = accum;
}
void Animation::setSpeed(float speed)
{
mAnimSpeedMult = 1.0f;
if(mAnimVelocity > 1.0f && speed > 0.0f)
mAnimSpeedMult = speed / mAnimVelocity;
}
void Animation::setLooping(bool loop)
{
mLooping = loop;
}
void Animation::updatePtr(const MWWorld::Ptr &ptr)
{
mPtr = ptr;
}
float Animation::calcAnimVelocity(NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl, const std::string &groupname, const NifOgre::TextKeyMap *keys)
{
const std::string loopstart = groupname+": loop start";
const std::string loopstop = groupname+": loop stop";
float loopstarttime = 0.0f;
float loopstoptime = std::numeric_limits<float>::max();
NifOgre::TextKeyMap::const_iterator keyiter = keys->begin();
while(keyiter != keys->end())
{
if(keyiter->second == loopstart)
loopstarttime = keyiter->first;
else if(keyiter->second == loopstop)
{
loopstoptime = keyiter->first;
break;
}
keyiter++;
}
if(loopstoptime > loopstarttime)
{
Ogre::Vector3 startpos = nonaccumctrl->getTranslation(loopstarttime);
Ogre::Vector3 endpos = nonaccumctrl->getTranslation(loopstarttime);
return startpos.distance(endpos) / (loopstoptime-loopstarttime);
}
return 0.0f;
}
static void updateBoneTree(const Ogre::SkeletonInstance *skelsrc, Ogre::Bone *bone)
{
if(skelsrc->hasBone(bone->getName()))
{
Ogre::Bone *srcbone = skelsrc->getBone(bone->getName());
if(!srcbone->getParent() || !bone->getParent())
{
bone->setOrientation(srcbone->getOrientation());
bone->setPosition(srcbone->getPosition());
bone->setScale(srcbone->getScale());
}
else
{
bone->_setDerivedOrientation(srcbone->_getDerivedOrientation());
bone->_setDerivedPosition(srcbone->_getDerivedPosition());
bone->setScale(Ogre::Vector3::UNIT_SCALE);
}
}
else
{
// No matching bone in the source. Make sure it stays properly offset
// from its parent.
bone->resetToInitialState();
}
Ogre::Node::ChildNodeIterator boneiter = bone->getChildIterator();
while(boneiter.hasMoreElements())
updateBoneTree(skelsrc, static_cast<Ogre::Bone*>(boneiter.getNext()));
}
void Animation::updateSkeletonInstance(const Ogre::SkeletonInstance *skelsrc, Ogre::SkeletonInstance *skel)
{
Ogre::Skeleton::BoneIterator boneiter = skel->getRootBoneIterator();
while(boneiter.hasMoreElements())
updateBoneTree(skelsrc, boneiter.getNext());
}
Ogre::Vector3 Animation::updatePosition()
{
Ogre::Vector3 posdiff;
/* Get the non-accumulation root's difference from the last update. */
posdiff = (mNonAccumCtrl->getTranslation(mCurrentTime) - mLastPosition) * mAccumulate;
/* Translate the accumulation root back to compensate for the move. */
mLastPosition += posdiff;
mAccumRoot->setPosition(-mLastPosition);
return posdiff;
}
void Animation::reset(const std::string &start, const std::string &stop)
{
2013-04-22 12:08:52 +00:00
std::string tag = mCurrentGroup+": "+start;
mStartKey = mCurrentKeys->begin();
2013-04-22 12:08:52 +00:00
while(mStartKey != mCurrentKeys->end() && mStartKey->second != tag)
mStartKey++;
2013-04-22 12:08:52 +00:00
if(mStartKey == mCurrentKeys->end() && tag == "loop start")
2013-01-09 11:30:55 +00:00
{
2013-04-22 12:08:52 +00:00
tag = mCurrentGroup+": start";
mStartKey = mCurrentKeys->begin();
2013-04-22 12:08:52 +00:00
while(mStartKey != mCurrentKeys->end() && mStartKey->second != tag)
mStartKey++;
2013-01-09 11:30:55 +00:00
}
2013-04-22 12:08:52 +00:00
if(mStartKey == mCurrentKeys->end())
mStartKey = mCurrentKeys->begin();
tag = mCurrentGroup+": "+stop;
mStopKey = mStartKey;
while(mStopKey != mCurrentKeys->end() && mStopKey->second != tag)
mStopKey++;
if(mStopKey == mCurrentKeys->end())
mStopKey--;
mCurrentTime = mStartKey->first;
mLoopStartKey = mStartKey;
mNextKey = mStartKey;
2013-04-22 12:08:52 +00:00
++mNextKey;
2013-01-09 11:30:55 +00:00
2013-04-22 12:08:52 +00:00
if(mNonAccumCtrl)
mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * mAccumulate;
}
2013-04-22 12:08:52 +00:00
void Animation::doLoop()
{
mCurrentTime = mLoopStartKey->first;
mNextKey = mLoopStartKey;
++mNextKey;
if(mNonAccumCtrl)
2013-04-22 12:08:52 +00:00
mLastPosition = mNonAccumCtrl->getTranslation(mCurrentTime) * mAccumulate;
}
2013-04-22 12:08:52 +00:00
bool Animation::handleTextKey(const NifOgre::TextKeyMap::const_iterator &key)
{
2013-04-22 12:08:52 +00:00
float time = key->first;
const std::string &evt = key->second;
if(evt.compare(0, 7, "sound: ") == 0)
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
return true;
}
if(evt.compare(0, 10, "soundgen: ") == 0)
{
// FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds
// to this actor type
return true;
}
if(evt.compare(0, mCurrentGroup.size(), mCurrentGroup) != 0 ||
evt.compare(mCurrentGroup.size(), 2, ": ") != 0)
{
// Not ours
return true;
}
size_t off = mCurrentGroup.size()+2;
size_t len = evt.size() - off;
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
{
2013-04-22 12:08:52 +00:00
mLoopStartKey = key;
return true;
}
2013-04-22 12:08:52 +00:00
if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
{
if(mLooping)
{
2013-04-22 12:08:52 +00:00
doLoop();
if(mCurrentTime >= time)
return false;
return true;
}
// fall-through
}
if(mController)
mController->markerEvent(time, evt.substr(off));
return true;
}
void Animation::play(const std::string &groupname, const std::string &start, const std::string &stop, bool loop)
{
try {
bool foundanim = false;
/* Look in reverse; last-inserted source has priority. */
for(std::vector<NifOgre::ObjectList>::reverse_iterator iter(mObjectLists.rbegin());iter != mObjectLists.rend();iter++)
{
if(iter->mSkelBase && iter->mSkelBase->hasAnimationState(groupname))
{
const NifOgre::TextKeyMap *keys = &iter->mTextKeys.begin()->second;
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
for(size_t i = 0;i < iter->mControllers.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(iter->mControllers[i].getDestination().getPointer());
if(dstval && dstval->getNode() == mNonAccumRoot)
{
nonaccumctrl = dstval;
break;
}
}
if(!foundanim)
{
mCurrentKeys = keys;
mCurrentGroup = groupname;
mCurrentControllers = &iter->mControllers;
mNonAccumCtrl = nonaccumctrl;
mAnimVelocity = 0.0f;
foundanim = true;
}
if(!mNonAccumRoot)
break;
mAnimVelocity = calcAnimVelocity(nonaccumctrl, groupname, keys);
if(mAnimVelocity > 0.0f) break;
}
}
if(!foundanim)
throw std::runtime_error("Failed to find animation "+groupname);
2013-04-22 12:08:52 +00:00
reset(start, stop);
setLooping(loop);
mPlaying = true;
}
catch(std::exception &e) {
std::cerr<< e.what() <<std::endl;
}
}
2011-12-15 05:33:10 +00:00
Ogre::Vector3 Animation::runAnimation(float timepassed)
{
Ogre::Vector3 movement(0.0f);
timepassed *= mAnimSpeedMult;
while(!mCurrentGroup.empty() && mPlaying)
{
float targetTime = mCurrentTime + timepassed;
if(mNextKey->first > targetTime)
{
mCurrentTime = targetTime;
if(mNonAccumRoot)
movement += updatePosition();
break;
}
2013-04-22 12:08:52 +00:00
NifOgre::TextKeyMap::const_iterator key(mNextKey++);
mCurrentTime = key->first;
if(mNonAccumRoot)
movement += updatePosition();
mPlaying = (mLooping || mStopKey->first > mCurrentTime);
timepassed = targetTime - mCurrentTime;
2013-01-18 22:50:55 +00:00
2013-04-22 12:08:52 +00:00
if(!handleTextKey(key))
break;
}
for(size_t i = 0;i < (*mCurrentControllers).size();i++)
2013-04-07 21:56:23 +00:00
(*mCurrentControllers)[i].update();
if(mSkelBase)
{
const Ogre::SkeletonInstance *baseinst = mSkelBase->getSkeleton();
for(std::vector<NifOgre::ObjectList>::iterator iter(mObjectLists.begin());iter != mObjectLists.end();iter++)
{
Ogre::Entity *ent = iter->mSkelBase;
if(!ent) continue;
Ogre::SkeletonInstance *inst = ent->getSkeleton();
if(baseinst != inst)
updateSkeletonInstance(baseinst, inst);
// HACK: Dirty the animation state set so that Ogre will apply the
// transformations to entities this skeleton instance is shared with.
ent->getAllAnimationStates()->_notifyDirty();
}
}
return movement;
}
2012-02-20 13:02:24 +00:00
}