Allow loading multiple animation sources

Animation sources are treated differently from base objects. When given
"path\file.nif", base objects will look for "path\xfile.nif" and use that
if it exists (falling back to the original name if not found). Animation
sources will instead use "path\xfile.kf", ignoring it if the file doesn't
exist.
actorid
Chris Robinson 12 years ago
parent e85bc8b2cd
commit 8e38dc410f

@ -23,6 +23,8 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(mPtr.getRefData().getBaseNode(), name, false);
setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha);
addAnimSource(name);
}
}

@ -19,8 +19,7 @@ namespace MWRender
{
Animation::AnimLayer::AnimLayer()
: mControllers(NULL)
, mTextKeys(NULL)
: mSource(NULL)
, mTime(0.0f)
, mPlaying(false)
, mLoopCount(0)
@ -76,6 +75,8 @@ Animation::~Animation()
{
if(mInsert)
{
mAnimSources.clear();
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
destroyObjectList(sceneMgr, mObjectRoot);
}
@ -87,8 +88,22 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b
OgreAssert(!mInsert, "Object already has a root!");
mInsert = node->createChildSceneNode();
mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
NifOgre::Loader::createObjectBase(mInsert, model));
std::string mdlname = Misc::StringUtils::lowerCase(model);
std::string::size_type p = mdlname.rfind('\\');
if(p == std::string::npos)
p = mdlname.rfind('/');
if(p != std::string::npos)
mdlname.insert(mdlname.begin()+p+1, 'x');
else
mdlname.insert(mdlname.begin(), 'x');
if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(mdlname))
{
mdlname = model;
Misc::StringUtils::toLower(mdlname);
}
mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, mdlname) :
NifOgre::Loader::createObjectBase(mInsert, mdlname));
if(mObjectRoot.mSkelBase)
{
mSkelBase = mObjectRoot.mSkelBase;
@ -109,12 +124,6 @@ void Animation::setObjectRoot(Ogre::SceneNode *node, const std::string &model, b
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
while(boneiter.hasMoreElements())
boneiter.getNext()->setManuallyControlled(true);
if(mObjectRoot.mTextKeys.size() > 0)
{
mAccumRoot = mInsert;
mNonAccumRoot = skelinst->getBone(mObjectRoot.mTextKeys.begin()->first);
}
}
for(size_t i = 0;i < mObjectRoot.mControllers.size();i++)
{
@ -148,6 +157,75 @@ void Animation::setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::ui
}
void Animation::addAnimSource(const std::string &model)
{
OgreAssert(mInsert, "Object is missing a root!");
if(!mSkelBase)
return;
std::string kfname = Misc::StringUtils::lowerCase(model);
std::string::size_type p = kfname.rfind('\\');
if(p == std::string::npos)
p = kfname.rfind('/');
if(p != std::string::npos)
kfname.insert(kfname.begin()+p+1, 'x');
else
kfname.insert(kfname.begin(), 'x');
if(kfname.size() > 4 && kfname.compare(kfname.size()-4, 4, ".nif") == 0)
kfname.replace(kfname.size()-4, 4, ".kf");
if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(kfname))
return;
mAnimSources.push_back(AnimSource());
NifOgre::Loader::createKfControllers(mSkelBase, kfname,
mAnimSources.back().mTextKeys,
mAnimSources.back().mControllers);
if(mAnimSources.back().mTextKeys.size() == 0 || mAnimSources.back().mControllers.size() == 0)
{
mAnimSources.pop_back();
return;
}
std::vector<Ogre::Controller<Ogre::Real> > &ctrls = mAnimSources.back().mControllers;
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
for(size_t i = 0;i < ctrls.size();i++)
{
dstval = static_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(ctrls[i].getDestination().getPointer());
if(i == 0 && !mAccumRoot)
{
mAccumRoot = mInsert;
mNonAccumRoot = dstval->getNode();
}
ctrls[i].setSource(mAnimationValuePtr[0]);
}
}
void Animation::clearAnimSources()
{
for(size_t layer = 0;layer < sMaxLayers;layer++)
{
mLayer[layer].mGroupName.clear();
mLayer[layer].mSource = NULL;
mLayer[layer].mTime = 0.0f;
mLayer[layer].mLoopCount = 0;
mLayer[layer].mPlaying = false;
}
mNonAccumCtrl = NULL;
mAnimVelocity = 0.0f;
mLastPosition = Ogre::Vector3(0.0f);
mAccumRoot = NULL;
mNonAccumRoot = NULL;
mAnimSources.clear();
}
Ogre::Node *Animation::getNode(const std::string &name)
{
if(mSkelBase)
@ -175,12 +253,10 @@ NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::Tex
bool Animation::hasAnimation(const std::string &anim)
{
if(!mSkelBase)
return false;
if(mObjectRoot.mTextKeys.size() > 0)
AnimSourceList::const_iterator iter(mAnimSources.begin());
for(;iter != mAnimSources.end();iter++)
{
const NifOgre::TextKeyMap &keys = mObjectRoot.mTextKeys.begin()->second;
const NifOgre::TextKeyMap &keys = iter->mTextKeys;
if(findGroupStart(keys, anim) != keys.end())
return true;
}
@ -415,8 +491,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
return false;
mLayer[layeridx].mGroupName.clear();
mLayer[layeridx].mTextKeys = NULL;
mLayer[layeridx].mControllers = NULL;
mLayer[layeridx].mSource = NULL;
mLayer[layeridx].mTime = 0.0f;
mLayer[layeridx].mLoopCount = 0;
mLayer[layeridx].mPlaying = false;
@ -428,19 +503,17 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
bool foundanim = false;
/* Look in reverse; last-inserted source has priority. */
do {
NifOgre::ObjectList &objlist = mObjectRoot;
if(objlist.mTextKeys.size() == 0)
continue;
const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second;
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
for(;iter != mAnimSources.rend();iter++)
{
const NifOgre::TextKeyMap &keys = iter->mTextKeys;
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
if(layeridx == 0 && mNonAccumRoot)
{
for(size_t i = 0;i < objlist.mControllers.size();i++)
for(size_t i = 0;i < iter->mControllers.size();i++)
{
NifOgre::NodeTargetValue<Ogre::Real> *dstval;
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(objlist.mControllers[i].getDestination().getPointer());
dstval = dynamic_cast<NifOgre::NodeTargetValue<Ogre::Real>*>(iter->mControllers[i].getDestination().getPointer());
if(dstval && dstval->getNode() == mNonAccumRoot)
{
nonaccumctrl = dstval;
@ -455,8 +528,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
continue;
mLayer[layeridx].mGroupName = groupname;
mLayer[layeridx].mTextKeys = &keys;
mLayer[layeridx].mControllers = &objlist.mControllers;
mLayer[layeridx].mSource = &*iter;
mLayer[layeridx].mLoopCount = loops;
mLayer[layeridx].mPlaying = true;
@ -481,7 +553,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
movinganim = (nonaccumctrl==mNonAccumCtrl);
break;
}
} while(0);
}
if(!foundanim)
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
@ -494,8 +566,7 @@ void Animation::disable(size_t layeridx)
return;
mLayer[layeridx].mGroupName.clear();
mLayer[layeridx].mTextKeys = NULL;
mLayer[layeridx].mControllers = NULL;
mLayer[layeridx].mSource = NULL;
mLayer[layeridx].mTime = 0.0f;
mLayer[layeridx].mLoopCount = 0;
mLayer[layeridx].mPlaying = false;
@ -558,6 +629,14 @@ Ogre::Vector3 Animation::runAnimation(float duration)
for(size_t i = 0;i < mObjectRoot.mControllers.size();i++)
mObjectRoot.mControllers[i].update();
for(size_t layeridx = 0;layeridx < sMaxLayers;layeridx++)
{
if(mLayer[layeridx].mGroupName.empty())
continue;
for(size_t i = 0;i < mLayer[layeridx].mSource->mControllers.size();i++)
mLayer[layeridx].mSource->mControllers[i].update();
}
if(mSkelBase)
{

@ -30,10 +30,15 @@ protected:
virtual void setValue(Ogre::Real value);
};
struct AnimSource {
NifOgre::TextKeyMap mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers;
};
typedef std::vector<AnimSource> AnimSourceList;
struct AnimLayer {
std::string mGroupName;
std::vector<Ogre::Controller<Ogre::Real> > *mControllers;
const NifOgre::TextKeyMap *mTextKeys;
AnimSource *mSource;
NifOgre::TextKeyMap::const_iterator mStartKey;
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
NifOgre::TextKeyMap::const_iterator mStopKey;
@ -52,8 +57,9 @@ protected:
Ogre::SceneNode *mInsert;
Ogre::Entity *mSkelBase;
NifOgre::ObjectList mObjectRoot;
AnimSourceList mAnimSources;
Ogre::Node *mAccumRoot;
Ogre::Bone *mNonAccumRoot;
Ogre::Node *mNonAccumRoot;
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
Ogre::Vector3 mAccumulate;
Ogre::Vector3 mLastPosition;
@ -95,11 +101,14 @@ protected:
bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly);
void addAnimSource(const std::string &model);
static void destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
void clearAnimSources();
public:
Animation(const MWWorld::Ptr &ptr);
virtual ~Animation();

@ -23,6 +23,10 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
setObjectRoot(mPtr.getRefData().getBaseNode(), model, false);
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
if((ref->mBase->mFlags&ESM::Creature::Biped))
addAnimSource("meshes\\base_anim.nif");
addAnimSource(model);
}
}

@ -100,26 +100,26 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
setObjectRoot(node, smodel, true);
#if 0
addAnimSource(node, smodel);
addAnimSource(smodel);
if(mBodyPrefix.find("argonian") != std::string::npos)
addAnimSource(node, "meshes\\argonian_swimkna.nif");
addAnimSource("meshes\\argonian_swimkna.nif");
else if(!mNpc->isMale() && !isBeast)
addAnimSource(node, "meshes\\base_anim_female.nif");
addAnimSource("meshes\\base_anim_female.nif");
if(mNpc->mModel.length() > 0)
addAnimSource(node, "meshes\\"+mNpc->mModel);
addAnimSource("meshes\\"+mNpc->mModel);
if(mViewMode == VM_FirstPerson)
{
/* A bit counter-intuitive, but unlike third-person anims, it seems
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
*/
addAnimSource(node, "meshes\\base_anim.1st.nif");
addAnimSource("meshes\\base_anim.1st.nif");
if(isBeast)
addAnimSource(node, "meshes\\base_animkna.1st.nif");
addAnimSource("meshes\\base_animkna.1st.nif");
if(!mNpc->isMale() && !isBeast)
addAnimSource(node, "meshes\\base_anim_female.1st.nif");
addAnimSource("meshes\\base_anim_female.1st.nif");
}
#endif
forceUpdate();
}
@ -134,28 +134,28 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
#if 0
clearAnimSources();
addAnimSource(node, smodel);
addAnimSource(smodel);
if(mBodyPrefix.find("argonian") != std::string::npos)
addAnimSource(node, "meshes\\argonian_swimkna.nif");
addAnimSource("meshes\\argonian_swimkna.nif");
else if(!mNpc->isMale() && !isBeast)
addAnimSource(node, "meshes\\base_anim_female.nif");
addAnimSource("meshes\\base_anim_female.nif");
if(mNpc->mModel.length() > 0)
addAnimSource(node, "meshes\\"+mNpc->mModel);
addAnimSource("meshes\\"+mNpc->mModel);
if(mViewMode == VM_FirstPerson)
{
/* A bit counter-intuitive, but unlike third-person anims, it seems
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
*/
addAnimSource(node, "meshes\\base_anim.1st.nif");
addAnimSource("meshes\\base_anim.1st.nif");
if(isBeast)
addAnimSource(node, "meshes\\base_animkna.1st.nif");
addAnimSource("meshes\\base_animkna.1st.nif");
if(!mNpc->isMale() && !isBeast)
addAnimSource(node, "meshes\\base_anim_female.1st.nif");
addAnimSource("meshes\\base_anim_female.1st.nif");
}
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
#endif
for(size_t i = 0;i < sPartListSize;i++)
removeIndividualPart(i);
forceUpdate();

@ -818,6 +818,64 @@ public:
}
createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
}
static void loadKf(Ogre::Skeleton *skel, const std::string &name,
TextKeyMap &textKeys, std::vector<Ogre::Controller<Ogre::Real> > &ctrls)
{
Nif::NIFFile::ptr nif = Nif::NIFFile::create(name);
if(nif->numRoots() < 1)
{
nif->warn("Found no root nodes in "+name+".");
return;
}
const Nif::Record *r = nif->getRoot(0);
assert(r != NULL);
const Nif::NiSequenceStreamHelper *seq = dynamic_cast<const Nif::NiSequenceStreamHelper*>(r);
if(seq == NULL)
{
nif->warn("First root was not a NiSequenceStreamHelper, but a "+
r->recName+".");
return;
}
Nif::ExtraPtr extra = seq->extra;
if(extra.empty() || extra->recType != Nif::RC_NiTextKeyExtraData)
{
nif->warn("First extra data was not a NiTextKeyExtraData, but a "+
(extra.empty() ? std::string("nil") : extra->recName)+".");
return;
}
extractTextKeys(static_cast<const Nif::NiTextKeyExtraData*>(extra.getPtr()), textKeys);
extra = extra->extra;
Nif::ControllerPtr ctrl = seq->controller;
for(;!extra.empty() && !ctrl.empty();(extra=extra->extra),(ctrl=ctrl->next))
{
if(extra->recType != Nif::RC_NiStringExtraData || ctrl->recType != Nif::RC_NiKeyframeController)
{
nif->warn("Unexpected extra data "+extra->recName+" with controller "+ctrl->recName);
continue;
}
const Nif::NiStringExtraData *strdata = static_cast<const Nif::NiStringExtraData*>(extra.getPtr());
const Nif::NiKeyframeController *key = static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr());
if(key->data.empty())
continue;
if(!skel->hasBone(strdata->string))
continue;
Ogre::Bone *trgtbone = skel->getBone(strdata->string);
Ogre::ControllerValueRealPtr srcval;
Ogre::ControllerValueRealPtr dstval(OGRE_NEW KeyframeController::Value(trgtbone, key->data.getPtr()));
Ogre::ControllerFunctionRealPtr func(OGRE_NEW KeyframeController::Function(key, false));
ctrls.push_back(Ogre::Controller<Ogre::Real>(srcval, dstval, func));
}
}
};
@ -912,4 +970,14 @@ ObjectList Loader::createObjectBase(Ogre::SceneNode *parentNode, std::string nam
return objectlist;
}
void Loader::createKfControllers(Ogre::Entity *skelBase,
const std::string &name,
TextKeyMap &textKeys,
std::vector<Ogre::Controller<Ogre::Real> > &ctrls)
{
NIFObjectLoader::loadKf(skelBase->getSkeleton(), name, textKeys, ctrls);
}
} // namespace NifOgre

@ -68,6 +68,11 @@ public:
static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
std::string name,
const std::string &group="General");
static void createKfControllers(Ogre::Entity *skelBase,
const std::string &name,
TextKeyMap &textKeys,
std::vector<Ogre::Controller<Ogre::Real> > &ctrls);
};
// FIXME: Should be with other general Ogre extensions.

Loading…
Cancel
Save