mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-10 07:13:53 +00:00
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.
This commit is contained in:
parent
e85bc8b2cd
commit
8e38dc410f
@ -23,6 +23,8 @@ ActivatorAnimation::ActivatorAnimation(const MWWorld::Ptr &ptr)
|
|||||||
|
|
||||||
setObjectRoot(mPtr.getRefData().getBaseNode(), name, false);
|
setObjectRoot(mPtr.getRefData().getBaseNode(), name, false);
|
||||||
setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha);
|
setRenderProperties(mObjectRoot, RV_Misc, RQG_Main, RQG_Alpha);
|
||||||
|
|
||||||
|
addAnimSource(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,7 @@ namespace MWRender
|
|||||||
{
|
{
|
||||||
|
|
||||||
Animation::AnimLayer::AnimLayer()
|
Animation::AnimLayer::AnimLayer()
|
||||||
: mControllers(NULL)
|
: mSource(NULL)
|
||||||
, mTextKeys(NULL)
|
|
||||||
, mTime(0.0f)
|
, mTime(0.0f)
|
||||||
, mPlaying(false)
|
, mPlaying(false)
|
||||||
, mLoopCount(0)
|
, mLoopCount(0)
|
||||||
@ -76,6 +75,8 @@ Animation::~Animation()
|
|||||||
{
|
{
|
||||||
if(mInsert)
|
if(mInsert)
|
||||||
{
|
{
|
||||||
|
mAnimSources.clear();
|
||||||
|
|
||||||
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
Ogre::SceneManager *sceneMgr = mInsert->getCreator();
|
||||||
destroyObjectList(sceneMgr, mObjectRoot);
|
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!");
|
OgreAssert(!mInsert, "Object already has a root!");
|
||||||
mInsert = node->createChildSceneNode();
|
mInsert = node->createChildSceneNode();
|
||||||
|
|
||||||
mObjectRoot = (!baseonly ? NifOgre::Loader::createObjects(mInsert, model) :
|
std::string mdlname = Misc::StringUtils::lowerCase(model);
|
||||||
NifOgre::Loader::createObjectBase(mInsert, 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)
|
if(mObjectRoot.mSkelBase)
|
||||||
{
|
{
|
||||||
mSkelBase = 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();
|
Ogre::Skeleton::BoneIterator boneiter = skelinst->getBoneIterator();
|
||||||
while(boneiter.hasMoreElements())
|
while(boneiter.hasMoreElements())
|
||||||
boneiter.getNext()->setManuallyControlled(true);
|
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++)
|
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)
|
Ogre::Node *Animation::getNode(const std::string &name)
|
||||||
{
|
{
|
||||||
if(mSkelBase)
|
if(mSkelBase)
|
||||||
@ -175,12 +253,10 @@ NifOgre::TextKeyMap::const_iterator Animation::findGroupStart(const NifOgre::Tex
|
|||||||
|
|
||||||
bool Animation::hasAnimation(const std::string &anim)
|
bool Animation::hasAnimation(const std::string &anim)
|
||||||
{
|
{
|
||||||
if(!mSkelBase)
|
AnimSourceList::const_iterator iter(mAnimSources.begin());
|
||||||
return false;
|
for(;iter != mAnimSources.end();iter++)
|
||||||
|
|
||||||
if(mObjectRoot.mTextKeys.size() > 0)
|
|
||||||
{
|
{
|
||||||
const NifOgre::TextKeyMap &keys = mObjectRoot.mTextKeys.begin()->second;
|
const NifOgre::TextKeyMap &keys = iter->mTextKeys;
|
||||||
if(findGroupStart(keys, anim) != keys.end())
|
if(findGroupStart(keys, anim) != keys.end())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -415,8 +491,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
mLayer[layeridx].mGroupName.clear();
|
mLayer[layeridx].mGroupName.clear();
|
||||||
mLayer[layeridx].mTextKeys = NULL;
|
mLayer[layeridx].mSource = NULL;
|
||||||
mLayer[layeridx].mControllers = NULL;
|
|
||||||
mLayer[layeridx].mTime = 0.0f;
|
mLayer[layeridx].mTime = 0.0f;
|
||||||
mLayer[layeridx].mLoopCount = 0;
|
mLayer[layeridx].mLoopCount = 0;
|
||||||
mLayer[layeridx].mPlaying = false;
|
mLayer[layeridx].mPlaying = false;
|
||||||
@ -428,19 +503,17 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
|
|||||||
bool foundanim = false;
|
bool foundanim = false;
|
||||||
|
|
||||||
/* Look in reverse; last-inserted source has priority. */
|
/* Look in reverse; last-inserted source has priority. */
|
||||||
do {
|
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
|
||||||
NifOgre::ObjectList &objlist = mObjectRoot;
|
for(;iter != mAnimSources.rend();iter++)
|
||||||
if(objlist.mTextKeys.size() == 0)
|
{
|
||||||
continue;
|
const NifOgre::TextKeyMap &keys = iter->mTextKeys;
|
||||||
|
|
||||||
const NifOgre::TextKeyMap &keys = objlist.mTextKeys.begin()->second;
|
|
||||||
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
|
NifOgre::NodeTargetValue<Ogre::Real> *nonaccumctrl = NULL;
|
||||||
if(layeridx == 0 && mNonAccumRoot)
|
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;
|
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)
|
if(dstval && dstval->getNode() == mNonAccumRoot)
|
||||||
{
|
{
|
||||||
nonaccumctrl = dstval;
|
nonaccumctrl = dstval;
|
||||||
@ -455,8 +528,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
mLayer[layeridx].mGroupName = groupname;
|
mLayer[layeridx].mGroupName = groupname;
|
||||||
mLayer[layeridx].mTextKeys = &keys;
|
mLayer[layeridx].mSource = &*iter;
|
||||||
mLayer[layeridx].mControllers = &objlist.mControllers;
|
|
||||||
mLayer[layeridx].mLoopCount = loops;
|
mLayer[layeridx].mLoopCount = loops;
|
||||||
mLayer[layeridx].mPlaying = true;
|
mLayer[layeridx].mPlaying = true;
|
||||||
|
|
||||||
@ -481,7 +553,7 @@ bool Animation::play(const std::string &groupname, const std::string &start, con
|
|||||||
movinganim = (nonaccumctrl==mNonAccumCtrl);
|
movinganim = (nonaccumctrl==mNonAccumCtrl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while(0);
|
}
|
||||||
if(!foundanim)
|
if(!foundanim)
|
||||||
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
|
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
|
||||||
|
|
||||||
@ -494,8 +566,7 @@ void Animation::disable(size_t layeridx)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
mLayer[layeridx].mGroupName.clear();
|
mLayer[layeridx].mGroupName.clear();
|
||||||
mLayer[layeridx].mTextKeys = NULL;
|
mLayer[layeridx].mSource = NULL;
|
||||||
mLayer[layeridx].mControllers = NULL;
|
|
||||||
mLayer[layeridx].mTime = 0.0f;
|
mLayer[layeridx].mTime = 0.0f;
|
||||||
mLayer[layeridx].mLoopCount = 0;
|
mLayer[layeridx].mLoopCount = 0;
|
||||||
mLayer[layeridx].mPlaying = false;
|
mLayer[layeridx].mPlaying = false;
|
||||||
@ -558,6 +629,14 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
|||||||
|
|
||||||
for(size_t i = 0;i < mObjectRoot.mControllers.size();i++)
|
for(size_t i = 0;i < mObjectRoot.mControllers.size();i++)
|
||||||
mObjectRoot.mControllers[i].update();
|
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)
|
if(mSkelBase)
|
||||||
{
|
{
|
||||||
|
@ -30,10 +30,15 @@ protected:
|
|||||||
virtual void setValue(Ogre::Real value);
|
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 {
|
struct AnimLayer {
|
||||||
std::string mGroupName;
|
std::string mGroupName;
|
||||||
std::vector<Ogre::Controller<Ogre::Real> > *mControllers;
|
AnimSource *mSource;
|
||||||
const NifOgre::TextKeyMap *mTextKeys;
|
|
||||||
NifOgre::TextKeyMap::const_iterator mStartKey;
|
NifOgre::TextKeyMap::const_iterator mStartKey;
|
||||||
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
|
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
|
||||||
NifOgre::TextKeyMap::const_iterator mStopKey;
|
NifOgre::TextKeyMap::const_iterator mStopKey;
|
||||||
@ -52,8 +57,9 @@ protected:
|
|||||||
Ogre::SceneNode *mInsert;
|
Ogre::SceneNode *mInsert;
|
||||||
Ogre::Entity *mSkelBase;
|
Ogre::Entity *mSkelBase;
|
||||||
NifOgre::ObjectList mObjectRoot;
|
NifOgre::ObjectList mObjectRoot;
|
||||||
|
AnimSourceList mAnimSources;
|
||||||
Ogre::Node *mAccumRoot;
|
Ogre::Node *mAccumRoot;
|
||||||
Ogre::Bone *mNonAccumRoot;
|
Ogre::Node *mNonAccumRoot;
|
||||||
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
|
NifOgre::NodeTargetValue<Ogre::Real> *mNonAccumCtrl;
|
||||||
Ogre::Vector3 mAccumulate;
|
Ogre::Vector3 mAccumulate;
|
||||||
Ogre::Vector3 mLastPosition;
|
Ogre::Vector3 mLastPosition;
|
||||||
@ -95,11 +101,14 @@ protected:
|
|||||||
bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
|
bool handleTextKey(size_t layeridx, const NifOgre::TextKeyMap::const_iterator &key);
|
||||||
|
|
||||||
void setObjectRoot(Ogre::SceneNode *node, const std::string &model, bool baseonly);
|
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 destroyObjectList(Ogre::SceneManager *sceneMgr, NifOgre::ObjectList &objects);
|
||||||
|
|
||||||
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
|
static void setRenderProperties(const NifOgre::ObjectList &objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, Ogre::uint8 transqueue);
|
||||||
|
|
||||||
|
void clearAnimSources();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Animation(const MWWorld::Ptr &ptr);
|
Animation(const MWWorld::Ptr &ptr);
|
||||||
virtual ~Animation();
|
virtual ~Animation();
|
||||||
|
@ -23,6 +23,10 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr)
|
|||||||
|
|
||||||
setObjectRoot(mPtr.getRefData().getBaseNode(), model, false);
|
setObjectRoot(mPtr.getRefData().getBaseNode(), model, false);
|
||||||
setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha);
|
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;
|
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||||
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
||||||
setObjectRoot(node, smodel, true);
|
setObjectRoot(node, smodel, true);
|
||||||
#if 0
|
|
||||||
addAnimSource(node, smodel);
|
addAnimSource(smodel);
|
||||||
if(mBodyPrefix.find("argonian") != std::string::npos)
|
if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||||
addAnimSource(node, "meshes\\argonian_swimkna.nif");
|
addAnimSource("meshes\\argonian_swimkna.nif");
|
||||||
else if(!mNpc->isMale() && !isBeast)
|
else if(!mNpc->isMale() && !isBeast)
|
||||||
addAnimSource(node, "meshes\\base_anim_female.nif");
|
addAnimSource("meshes\\base_anim_female.nif");
|
||||||
if(mNpc->mModel.length() > 0)
|
if(mNpc->mModel.length() > 0)
|
||||||
addAnimSource(node, "meshes\\"+mNpc->mModel);
|
addAnimSource("meshes\\"+mNpc->mModel);
|
||||||
if(mViewMode == VM_FirstPerson)
|
if(mViewMode == VM_FirstPerson)
|
||||||
{
|
{
|
||||||
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
||||||
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
|
* 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)
|
if(isBeast)
|
||||||
addAnimSource(node, "meshes\\base_animkna.1st.nif");
|
addAnimSource("meshes\\base_animkna.1st.nif");
|
||||||
if(!mNpc->isMale() && !isBeast)
|
if(!mNpc->isMale() && !isBeast)
|
||||||
addAnimSource(node, "meshes\\base_anim_female.1st.nif");
|
addAnimSource("meshes\\base_anim_female.1st.nif");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,28 +134,28 @@ void NpcAnimation::setViewMode(NpcAnimation::ViewMode viewMode)
|
|||||||
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
|
const ESM::Race *race = store.get<ESM::Race>().find(mNpc->mRace);
|
||||||
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
bool isBeast = (race->mData.mFlags & ESM::Race::Beast) != 0;
|
||||||
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
std::string smodel = (!isBeast ? "meshes\\base_anim.nif" : "meshes\\base_animkna.nif");
|
||||||
#if 0
|
|
||||||
clearAnimSources();
|
clearAnimSources();
|
||||||
addAnimSource(node, smodel);
|
addAnimSource(smodel);
|
||||||
if(mBodyPrefix.find("argonian") != std::string::npos)
|
if(mBodyPrefix.find("argonian") != std::string::npos)
|
||||||
addAnimSource(node, "meshes\\argonian_swimkna.nif");
|
addAnimSource("meshes\\argonian_swimkna.nif");
|
||||||
else if(!mNpc->isMale() && !isBeast)
|
else if(!mNpc->isMale() && !isBeast)
|
||||||
addAnimSource(node, "meshes\\base_anim_female.nif");
|
addAnimSource("meshes\\base_anim_female.nif");
|
||||||
if(mNpc->mModel.length() > 0)
|
if(mNpc->mModel.length() > 0)
|
||||||
addAnimSource(node, "meshes\\"+mNpc->mModel);
|
addAnimSource("meshes\\"+mNpc->mModel);
|
||||||
if(mViewMode == VM_FirstPerson)
|
if(mViewMode == VM_FirstPerson)
|
||||||
{
|
{
|
||||||
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
/* A bit counter-intuitive, but unlike third-person anims, it seems
|
||||||
* beast races get both base_anim.1st.nif and base_animkna.1st.nif.
|
* 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)
|
if(isBeast)
|
||||||
addAnimSource(node, "meshes\\base_animkna.1st.nif");
|
addAnimSource("meshes\\base_animkna.1st.nif");
|
||||||
if(!mNpc->isMale() && !isBeast)
|
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);
|
MWBase::Environment::get().getMechanicsManager()->forceStateUpdate(mPtr);
|
||||||
#endif
|
|
||||||
for(size_t i = 0;i < sPartListSize;i++)
|
for(size_t i = 0;i < sPartListSize;i++)
|
||||||
removeIndividualPart(i);
|
removeIndividualPart(i);
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
|
@ -818,6 +818,64 @@ public:
|
|||||||
}
|
}
|
||||||
createObjects(name, group, sceneMgr, node, objectlist, flags, 0, 0);
|
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;
|
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
|
} // namespace NifOgre
|
||||||
|
@ -68,6 +68,11 @@ public:
|
|||||||
static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
|
static ObjectList createObjectBase(Ogre::SceneNode *parentNode,
|
||||||
std::string name,
|
std::string name,
|
||||||
const std::string &group="General");
|
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.
|
// FIXME: Should be with other general Ogre extensions.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user