Merge branch 'master' into graphics

Conflicts:
	apps/openmw/CMakeLists.txt
	apps/openmw/mwrender/npcanimation.cpp
	apps/openmw/mwrender/sky.cpp
This commit is contained in:
scrawl 2013-02-25 15:12:03 +01:00
commit cacdb33b42
75 changed files with 2856 additions and 4142 deletions

View file

@ -3,17 +3,17 @@
#include <string>
#include <vector>
#include <list>
#include "esmcommon.hpp"
#include "defs.hpp"
#include "apps/openmw/mwbase/world.hpp"
/*
namespace MWWorld {
class ESMStore;
class CellStore;
namespace MWWorld
{
class ESMStore;
}
*/
namespace ESM
{

View file

@ -21,8 +21,6 @@
*/
//loadResource->handleNode->handleNiTriShape->createSubMesh
#include "ogre_nif_loader.hpp"
#include <algorithm>
@ -35,6 +33,7 @@
#include <OgreSubMesh.h>
#include <OgreRoot.h>
#include <OgreEntity.h>
#include <OgreSubEntity.h>
#include <OgreTagPoint.h>
#include <boost/lexical_cast.hpp>
@ -53,9 +52,6 @@ typedef unsigned char ubyte;
namespace std
{
// These operators allow extra data types to be stored in an Ogre::Any
// object, which can then be stored in user object bindings on the nodes
// TODO: Do something useful
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&)
{ return o; }
@ -149,8 +145,12 @@ public:
};
class NIFSkeletonLoader : public Ogre::ManualResourceLoader {
/** Manual resource loader for NIF skeletons. This is the main class
responsible for translating the internal NIF skeleton structure into
something Ogre can use (includes animations and node TextKeyData).
*/
class NIFSkeletonLoader : public Ogre::ManualResourceLoader
{
static void warn(const std::string &msg)
{
std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl;
@ -163,168 +163,49 @@ static void fail(const std::string &msg)
}
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
static void buildAnimation(Ogre::Skeleton *skel, const std::string &name, const std::vector<const Nif::NiKeyframeController*> &ctrls, const std::vector<std::string> &targets, float startTime, float stopTime)
{
TextKeyMap textkeys;
for(size_t i = 0;i < tk->list.size();i++)
{
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
while(pos < str.length() && ::isspace(str[pos]))
pos++;
if(pos >= str.length())
break;
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos)));
pos = nextpos;
}
}
return textkeys;
}
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL)
{
Ogre::Bone *bone;
if(!skel->hasBone(node->name))
bone = skel->createBone(node->name);
else
bone = skel->createBone();
if(parent) parent->addChild(bone);
bone->setOrientation(node->trafo.rotation);
bone->setPosition(node->trafo.pos);
bone->setScale(Ogre::Vector3(node->trafo.scale));
bone->setBindingPose();
bone->setInitialState();
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiKeyframeController)
ctrls.push_back(static_cast<Nif::NiKeyframeController*>(ctrl.getPtr()));
ctrl = ctrl->next;
}
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk)));
}
e = e->extra;
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode)
{
const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
buildBones(skel, children[i].getPtr(), ctrls, bone);
}
}
}
/* Comparitor to help sort Key<> vectors */
template<class T>
struct KeyTimeSort
{
bool operator()(const Nif::KeyT<T> &lhs, const Nif::KeyT<T> &rhs) const
{ return lhs.mTime < rhs.mTime; }
};
typedef std::map<std::string,NIFSkeletonLoader> LoaderMap;
static LoaderMap sLoaders;
public:
void loadResource(Ogre::Resource *resource)
{
Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!");
Nif::NIFFile::ptr pnif(Nif::NIFFile::create (skel->getName()));
Nif::NIFFile & nif = *pnif.get ();
const Nif::Node *node = dynamic_cast<const Nif::Node*>(nif.getRecord(0));
std::vector<Nif::NiKeyframeController const*> ctrls;
buildBones(skel, node, ctrls);
std::vector<std::string> targets;
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
if(ctrls.size() == 0) // No animations? Then we're done.
return;
float maxtime = 0.0f;
for(size_t i = 0;i < ctrls.size();i++)
{
Nif::NiKeyframeController const *ctrl = ctrls[i];
maxtime = std::max(maxtime, ctrl->timeStop);
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
if(target != NULL)
targets.push_back(target->name);
}
if(targets.size() != ctrls.size())
{
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
return;
}
Ogre::Animation *anim = skel->createAnimation(skel->getName(), maxtime);
/* HACK: Pre-create the node tracks by matching the track IDs with the
* bone IDs. Otherwise, Ogre animates the wrong bones. */
size_t bonecount = skel->getNumBones();
for(size_t i = 0;i < bonecount;i++)
anim->createNodeTrack(i, skel->getBone(i));
Ogre::Animation *anim = skel->createAnimation(name, stopTime-startTime);
for(size_t i = 0;i < ctrls.size();i++)
{
Nif::NiKeyframeController const *kfc = ctrls[i];
Nif::NiKeyframeData const *kf = kfc->data.getPtr();
const Nif::NiKeyframeController *kfc = ctrls[i];
if(kfc->data.empty())
continue;
const Nif::NiKeyframeData *kf = kfc->data.getPtr();
/* Get the keyframes and make sure they're sorted first to last */
Nif::QuaternionKeyList quatkeys = kf->mRotations;
Nif::Vector3KeyList trankeys = kf->mTranslations;
Nif::FloatKeyList scalekeys = kf->mScales;
std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort<Ogre::Quaternion>());
std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort<Ogre::Vector3>());
std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort<float>());
const Nif::QuaternionKeyList &quatkeys = kf->mRotations;
const Nif::Vector3KeyList &trankeys = kf->mTranslations;
const Nif::FloatKeyList &scalekeys = kf->mScales;
Nif::QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
Nif::Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
Nif::FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin();
Ogre::Bone *bone = skel->getBone(targets[i]);
const Ogre::Quaternion startquat = bone->getInitialOrientation();
const Ogre::Vector3 starttrans = bone->getInitialPosition();
const Ogre::Vector3 startscale = bone->getInitialScale();
Ogre::NodeAnimationTrack *nodetrack = anim->getNodeTrack(bone->getHandle());
// NOTE: For some reason, Ogre doesn't like the node track ID being different from
// the bone ID
Ogre::NodeAnimationTrack *nodetrack = anim->hasNodeTrack(bone->getHandle()) ?
anim->getNodeTrack(bone->getHandle()) :
anim->createNodeTrack(bone->getHandle(), bone);
Ogre::Quaternion lastquat, curquat;
Ogre::Vector3 lasttrans(0.0f), curtrans(0.0f);
Ogre::Vector3 lastscale(1.0f), curscale(1.0f);
if(quatiter != quatkeys.mKeys.end())
lastquat = curquat = startquat.Inverse() * quatiter->mValue;
lastquat = curquat = quatiter->mValue;
if(traniter != trankeys.mKeys.end())
lasttrans = curtrans = traniter->mValue - starttrans;
lasttrans = curtrans = traniter->mValue;
if(scaleiter != scalekeys.mKeys.end())
lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale;
lastscale = curscale = Ogre::Vector3(scaleiter->mValue);
float begTime = std::max(kfc->timeStart, startTime);
float endTime = std::min(kfc->timeStop, stopTime);
bool didlast = false;
while(!didlast)
{
float curtime = kfc->timeStop;
float curtime = std::numeric_limits<float>::max();
//Get latest time
if(quatiter != quatkeys.mKeys.end())
@ -334,11 +215,11 @@ void loadResource(Ogre::Resource *resource)
if(scaleiter != scalekeys.mKeys.end())
curtime = std::min(curtime, scaleiter->mTime);
curtime = std::max(curtime, kfc->timeStart);
if(curtime >= kfc->timeStop)
curtime = std::max(curtime, begTime);
if(curtime >= endTime)
{
didlast = true;
curtime = kfc->timeStop;
curtime = endTime;
}
// Get the latest quaternions, translations, and scales for the
@ -346,27 +227,24 @@ void loadResource(Ogre::Resource *resource)
while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
{
lastquat = curquat;
quatiter++;
if(quatiter != quatkeys.mKeys.end())
curquat = startquat.Inverse() * quatiter->mValue ;
if(++quatiter != quatkeys.mKeys.end())
curquat = quatiter->mValue;
}
while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
{
lasttrans = curtrans;
traniter++;
if(traniter != trankeys.mKeys.end())
curtrans = traniter->mValue - starttrans;
if(++traniter != trankeys.mKeys.end())
curtrans = traniter->mValue;
}
while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
{
lastscale = curscale;
scaleiter++;
if(scaleiter != scalekeys.mKeys.end())
curscale = Ogre::Vector3(scaleiter->mValue) / startscale;
if(++scaleiter != scalekeys.mKeys.end())
curscale = Ogre::Vector3(scaleiter->mValue);
}
Ogre::TransformKeyFrame *kframe;
kframe = nodetrack->createNodeKeyFrame(curtime);
kframe = nodetrack->createNodeKeyFrame(curtime-startTime);
if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
kframe->setRotation(curquat);
else
@ -396,18 +274,73 @@ void loadResource(Ogre::Resource *resource)
anim->optimise();
}
bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node)
static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk)
{
if(node->boneTrafo != NULL)
TextKeyMap textkeys;
for(size_t i = 0;i < tk->list.size();i++)
{
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
Ogre::SkeletonPtr skel = skelMgr.getByName(name);
if(skel.isNull())
const std::string &str = tk->list[i].text;
std::string::size_type pos = 0;
while(pos < str.length())
{
NIFSkeletonLoader *loader = &sLoaders[name];
skel = skelMgr.create(name, group, true, loader);
if(::isspace(str[pos]))
{
pos++;
continue;
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
pos = nextpos;
}
return true;
}
return textkeys;
}
void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, Ogre::Bone *&animroot, TextKeyMap &textkeys, std::vector<Nif::NiKeyframeController const*> &ctrls, Ogre::Bone *parent=NULL)
{
Ogre::Bone *bone;
if(!skel->hasBone(node->name))
bone = skel->createBone(node->name);
else
bone = skel->createBone();
if(parent) parent->addChild(bone);
bone->setOrientation(node->trafo.rotation);
bone->setPosition(node->trafo.pos);
bone->setScale(Ogre::Vector3(node->trafo.scale));
bone->setBindingPose();
if(!(node->recType == Nif::RC_NiNode || /* Nothing special; children traversed below */
node->recType == Nif::RC_RootCollisionNode || /* handled in nifbullet (hopefully) */
node->recType == Nif::RC_NiTriShape /* Handled in the mesh loader */
))
warn("Unhandled "+node->recName+" "+node->name+" in "+skel->getName());
Nif::ControllerPtr ctrl = node->controller;
while(!ctrl.empty())
{
if(ctrl->recType == Nif::RC_NiKeyframeController)
ctrls.push_back(static_cast<const Nif::NiKeyframeController*>(ctrl.getPtr()));
else
warn("Unhandled "+ctrl->recName+" from node "+node->name+" in "+skel->getName());
ctrl = ctrl->next;
}
Nif::ExtraPtr e = node->extra;
while(!e.empty())
{
if(e->recType == Nif::RC_NiTextKeyExtraData && !animroot)
{
const Nif::NiTextKeyExtraData *tk = static_cast<const Nif::NiTextKeyExtraData*>(e.getPtr());
textkeys = extractTextKeys(tk);
animroot = bone;
}
e = e->extra;
}
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
@ -417,13 +350,142 @@ bool createSkeleton(const std::string &name, const std::string &group, const Nif
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
buildBones(skel, children[i].getPtr(), animroot, textkeys, ctrls, bone);
}
}
}
typedef std::map<std::string,NIFSkeletonLoader> LoaderMap;
static LoaderMap sLoaders;
public:
void loadResource(Ogre::Resource *resource)
{
Ogre::Skeleton *skel = dynamic_cast<Ogre::Skeleton*>(resource);
OgreAssert(skel, "Attempting to load a skeleton into a non-skeleton resource!");
Nif::NIFFile::ptr nif(Nif::NIFFile::create(skel->getName()));
const Nif::Node *node = static_cast<const Nif::Node*>(nif->getRecord(0));
std::vector<const Nif::NiKeyframeController*> ctrls;
Ogre::Bone *animroot = NULL;
TextKeyMap textkeys;
try {
buildBones(skel, node, animroot, textkeys, ctrls);
}
catch(std::exception &e) {
std::cerr<< "Exception while loading "<<skel->getName() <<std::endl;
std::cerr<< e.what() <<std::endl;
return;
}
std::vector<std::string> targets;
// TODO: If ctrls.size() == 0, check for a .kf file sharing the name of the .nif file
if(ctrls.size() == 0) // No animations? Then we're done.
return;
float maxtime = 0.0f;
for(size_t i = 0;i < ctrls.size();i++)
{
const Nif::NiKeyframeController *ctrl = ctrls[i];
maxtime = std::max(maxtime, ctrl->timeStop);
Nif::Named *target = dynamic_cast<Nif::Named*>(ctrl->target.getPtr());
if(target != NULL)
targets.push_back(target->name);
}
if(targets.size() != ctrls.size())
{
warn("Target size mismatch ("+Ogre::StringConverter::toString(targets.size())+" targets, "+
Ogre::StringConverter::toString(ctrls.size())+" controllers)");
return;
}
if(!animroot)
{
warn(Ogre::StringConverter::toString(ctrls.size())+" animated node(s) in "+
skel->getName()+", but no text keys.");
return;
}
Ogre::UserObjectBindings &bindings = animroot->getUserObjectBindings();
bindings.setUserAny(sTextKeyExtraDataID, Ogre::Any(true));
std::string currentgroup;
TextKeyMap::const_iterator keyiter = textkeys.begin();
for(keyiter = textkeys.begin();keyiter != textkeys.end();keyiter++)
{
std::string::size_type sep = keyiter->second.find(':');
if((sep == currentgroup.length() && keyiter->second.compare(0, sep, currentgroup) == 0) ||
(sep == sizeof("soundgen")-1 && keyiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && keyiter->second.compare(0, sep, "sound") == 0))
continue;
currentgroup = keyiter->second.substr(0, sep);
if(skel->hasAnimation(currentgroup))
continue;
TextKeyMap::const_iterator lastkeyiter = textkeys.end();
while((--lastkeyiter)->first > keyiter->first)
{
if(lastkeyiter->second.find(':') == currentgroup.length() &&
lastkeyiter->second.compare(0, currentgroup.length(), currentgroup) == 0)
break;
}
buildAnimation(skel, currentgroup, ctrls, targets, keyiter->first, lastkeyiter->first);
TextKeyMap::const_iterator insiter = keyiter;
TextKeyMap groupkeys;
do {
sep = insiter->second.find(':');
if(sep == currentgroup.length() && insiter->second.compare(0, sep, currentgroup) == 0)
groupkeys.insert(std::make_pair(insiter->first - keyiter->first,
insiter->second.substr(sep+2)));
else if((sep == sizeof("soundgen")-1 && insiter->second.compare(0, sep, "soundgen") == 0) ||
(sep == sizeof("sound")-1 && insiter->second.compare(0, sep, "sound") == 0))
groupkeys.insert(std::make_pair(insiter->first - keyiter->first, insiter->second));
} while(insiter++ != lastkeyiter);
bindings.setUserAny(std::string(sTextKeyExtraDataID)+"@"+currentgroup, Ogre::Any(groupkeys));
}
}
bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node)
{
/* We need to be a little aggressive here, since some NIFs have a crap-ton
* of nodes and Ogre only supports 256 bones. We will skip a skeleton if:
* There are no bones used for skinning, there are no controllers on non-
* NiTriShape nodes, there are no nodes named "AttachLight", and the tree
* consists of NiNode, NiTriShape, and RootCollisionNode types only.
*/
if(!node->boneTrafo)
{
if(node->recType == Nif::RC_NiTriShape)
return false;
if(node->controller.empty() && node->name != "AttachLight")
{
if(node->recType == Nif::RC_NiNode || node->recType == Nif::RC_RootCollisionNode)
{
if(createSkeleton(name, group, children[i].getPtr()))
return true;
const Nif::NiNode *ninode = static_cast<const Nif::NiNode*>(node);
const Nif::NodeList &children = ninode->children;
for(size_t i = 0;i < children.length();i++)
{
if(!children[i].empty())
{
if(createSkeleton(name, group, children[i].getPtr()))
return true;
}
}
return false;
}
}
}
return false;
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
skelMgr.create(name, group, true, &sLoaders[name]);
return true;
}
};
@ -701,12 +763,15 @@ static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String
std::map<size_t,std::string> NIFMaterialLoader::MaterialMap;
/** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into
something Ogre can use.
*/
class NIFMeshLoader : Ogre::ManualResourceLoader
{
std::string mName;
std::string mGroup;
size_t mShapeIndex;
std::string mSkelName;
std::string mMaterialName;
std::string mShapeName;
@ -734,12 +799,11 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
{
// Only set a skeleton when skinning. Unskinned meshes with a skeleton will be
// explicitly attached later.
mesh->setSkeletonName(mSkelName);
mesh->setSkeletonName(mName);
// Get the skeleton resource, so vertices can be transformed into the bones' initial state.
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
skel = skelMgr->getByName(mSkelName);
skel->touch();
skel = skelMgr->getByName(mName);
// Convert vertices and normals to bone space from bind position. It would be
// better to transform the bones into bind position, but there doesn't seem to
@ -752,12 +816,10 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
for(size_t b = 0;b < bones.length();b++)
{
Ogre::Bone *bone = skel->getBone(bones[b]->name);
Ogre::Matrix4 mat, mat2;
Ogre::Matrix4 mat;
mat.makeTransform(data->bones[b].trafo.trans, Ogre::Vector3(data->bones[b].trafo.scale),
Ogre::Quaternion(data->bones[b].trafo.rotation));
mat2.makeTransform(bone->_getDerivedPosition(), bone->_getDerivedScale(),
bone->_getDerivedOrientation());
mat = mat2 * mat;
mat = bone->_getFullTransform() * mat;
const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
for(size_t i = 0;i < weights.size();i++)
@ -778,22 +840,26 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
srcVerts = newVerts;
srcNorms = newNorms;
}
else if(mSkelName.length() == 0)
else
{
// No skinning and no skeleton, so just transform the vertices and
// normals into position.
Ogre::Matrix4 mat4 = shape->getWorldTransform();
for(size_t i = 0;i < srcVerts.size();i++)
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(skelMgr->getByName(mName).isNull())
{
Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f);
vec4 = mat4*vec4;
srcVerts[i] = Ogre::Vector3(&vec4[0]);
}
for(size_t i = 0;i < srcNorms.size();i++)
{
Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f);
vec4 = mat4*vec4;
srcNorms[i] = Ogre::Vector3(&vec4[0]);
// No skinning and no skeleton, so just transform the vertices and
// normals into position.
Ogre::Matrix4 mat4 = shape->getWorldTransform();
for(size_t i = 0;i < srcVerts.size();i++)
{
Ogre::Vector4 vec4(srcVerts[i].x, srcVerts[i].y, srcVerts[i].z, 1.0f);
vec4 = mat4*vec4;
srcVerts[i] = Ogre::Vector3(&vec4[0]);
}
for(size_t i = 0;i < srcNorms.size();i++)
{
Ogre::Vector4 vec4(srcNorms[i].x, srcNorms[i].y, srcNorms[i].z, 0.0f);
vec4 = mat4*vec4;
srcNorms[i] = Ogre::Vector3(&vec4[0]);
}
}
}
@ -953,19 +1019,21 @@ class NIFMeshLoader : Ogre::ManualResourceLoader
public:
NIFMeshLoader()
{ }
NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName)
: mName(name), mGroup(group), mShapeIndex(~(size_t)0), mSkelName(skelName)
NIFMeshLoader(const std::string &name, const std::string &group)
: mName(name), mGroup(group), mShapeIndex(~(size_t)0)
{ }
virtual void loadResource(Ogre::Resource *resource)
{
Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
assert(mesh && "Attempting to load a mesh into a non-mesh resource!");
OgreAssert(mesh, "Attempting to load a mesh into a non-mesh resource!");
Nif::NIFFile::ptr nif = Nif::NIFFile::create(mName);
if(mShapeIndex >= nif->numRecords())
{
mesh->setSkeletonName(mSkelName);
Ogre::SkeletonManager *skelMgr = Ogre::SkeletonManager::getSingletonPtr();
if(!skelMgr->getByName(mName).isNull())
mesh->setSkeletonName(mName);
return;
}
@ -973,7 +1041,7 @@ public:
findTriShape(mesh, node);
}
void createMeshes(const Nif::Node *node, MeshPairList &meshes, int flags=0)
void createMeshes(const Nif::Node *node, MeshInfoList &meshes, int flags=0)
{
flags |= node->flags;
@ -986,7 +1054,6 @@ public:
while(!e.empty())
{
Nif::NiStringExtraData *sd;
Nif::NiTextKeyExtraData *td;
if((sd=dynamic_cast<Nif::NiStringExtraData*>(e.getPtr())) != NULL)
{
// String markers may contain important information
@ -998,16 +1065,10 @@ public:
flags |= 0x01;
}
}
else if((td=dynamic_cast<Nif::NiTextKeyExtraData*>(e.getPtr())) != NULL)
{
// TODO: Read and store text keys somewhere
}
else
warn("Unhandled extra data type "+e->recName);
e = e->extra;
}
if(node->recType == Nif::RC_NiTriShape)
if(node->recType == Nif::RC_NiTriShape && !(flags&0x01)) // Not hidden
{
const Nif::NiTriShape *shape = dynamic_cast<const Nif::NiTriShape*>(node);
mShapeName = shape->name;
@ -1016,8 +1077,6 @@ public:
std::string fullname = mName+"@index="+Ogre::StringConverter::toString(shape->recIndex);
if(mShapeName.length() > 0)
fullname += "@shape="+mShapeName;
if(mSkelName.length() > 0 && mName != mSkelName)
fullname += "@skel="+mSkelName;
Misc::StringUtils::toLower(fullname);
Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
@ -1025,21 +1084,15 @@ public:
{
NIFMeshLoader *loader = &sLoaders[fullname];
*loader = *this;
if(!(flags&0x01)) // Not hidden
{
loader->mShapeIndex = shape->recIndex;
loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup);
}
loader->mShapeIndex = shape->recIndex;
loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup);
mesh = meshMgr.createManual(fullname, mGroup, loader);
mesh->setAutoBuildEdgeLists(false);
}
meshes.push_back(std::make_pair(mesh->getName(), shape->name));
meshes.push_back(MeshInfo(mesh->getName(), shape->name));
}
else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode &&
node->recType != Nif::RC_NiRotatingParticles)
warn("Unhandled mesh node type: "+node->recName);
const Nif::NiNode *ninode = dynamic_cast<const Nif::NiNode*>(node);
if(ninode)
@ -1052,28 +1105,47 @@ public:
}
}
}
void createEmptyMesh(const Nif::Node *node, MeshInfoList &meshes)
{
/* This creates an empty mesh to which a skeleton gets attached. This
* is to ensure we have an entity with a skeleton instance, even if all
* other meshes are hidden or entities attached to a specific node
* instead of skinned. */
std::string fullname = mName;
Misc::StringUtils::toLower(fullname);
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
if(mesh.isNull())
{
NIFMeshLoader *loader = &sLoaders[fullname];
*loader = *this;
mesh = meshMgr.createManual(fullname, mGroup, loader);
mesh->setAutoBuildEdgeLists(false);
}
meshes.push_back(MeshInfo(mesh->getName(), node->name));
}
};
NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
typedef std::map<std::string,MeshPairList> MeshPairMap;
static MeshPairMap sMeshPairMap;
typedef std::map<std::string,MeshInfoList> MeshInfoMap;
static MeshInfoMap sMeshInfoMap;
MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group)
MeshInfoList Loader::load(const std::string &name, const std::string &group)
{
Misc::StringUtils::toLower(name);
Misc::StringUtils::toLower(skelName);
MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName);
if(meshiter != sMeshPairMap.end())
MeshInfoMap::const_iterator meshiter = sMeshInfoMap.find(name);
if(meshiter != sMeshInfoMap.end())
return meshiter->second;
MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName];
Nif::NIFFile::ptr pnif = Nif::NIFFile::create (name);
Nif::NIFFile &nif = *pnif.get ();
if (nif.numRecords() < 1)
MeshInfoList &meshes = sMeshInfoMap[name];
Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name);
Nif::NIFFile &nif = *pnif.get();
if(nif.numRecords() < 1)
{
nif.warn("Found no records in NIF.");
nif.warn("Found no NIF records in "+name+".");
return meshes;
}
@ -1084,109 +1156,87 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::
Nif::Node const *node = dynamic_cast<Nif::Node const *>(r);
if(node == NULL)
{
nif.warn("First record in file was not a node, but a "+
r->recName+". Skipping file.");
nif.warn("First record in "+name+" was not a node, but a "+
r->recName+".");
return meshes;
}
NIFSkeletonLoader skelldr;
bool hasSkel = skelldr.createSkeleton(name, group, node);
bool hasSkel = Ogre::SkeletonManager::getSingleton().resourceExists(name);
if(!hasSkel)
{
NIFSkeletonLoader skelldr;
hasSkel = skelldr.createSkeleton(name, group, node);
}
NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string()));
meshldr.createMeshes(node, meshes);
NIFMeshLoader meshldr(name, group);
if(hasSkel)
meshldr.createEmptyMesh(node, meshes);
meshldr.createMeshes(node, meshes, 0);
return meshes;
}
EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textkeys, const std::string &name, const std::string &group)
EntityList Loader::createEntities(Ogre::SceneNode *parentNode, std::string name, const std::string &group)
{
EntityList entitylist;
MeshPairList meshes = load(name, name, group);
Misc::StringUtils::toLower(name);
MeshInfoList meshes = load(name, group);
if(meshes.size() == 0)
return entitylist;
Ogre::SceneManager *sceneMgr = parent->getCreator();
Ogre::SceneManager *sceneMgr = parentNode->getCreator();
for(size_t i = 0;i < meshes.size();i++)
{
entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first));
entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].mMeshName));
Ogre::Entity *entity = entitylist.mEntities.back();
if(!entitylist.mSkelBase && entity->hasSkeleton())
entitylist.mSkelBase = entity;
}
if(entitylist.mSkelBase && textkeys)
{
// Would be nice if Ogre::SkeletonInstance allowed access to the 'master' Ogre::SkeletonPtr.
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
Ogre::SkeletonPtr skel = skelMgr.getByName(entitylist.mSkelBase->getSkeleton()->getName());
Ogre::Skeleton::BoneIterator iter = skel->getBoneIterator();
while(iter.hasMoreElements())
{
Ogre::Bone *bone = iter.getNext();
const Ogre::Any &data = bone->getUserObjectBindings().getUserAny("TextKeyExtraData");
if(!data.isEmpty())
{
*textkeys = Ogre::any_cast<TextKeyMap>(data);
break;
}
}
}
if(entitylist.mSkelBase)
{
parent->attachObject(entitylist.mSkelBase);
parentNode->attachObject(entitylist.mSkelBase);
for(size_t i = 0;i < entitylist.mEntities.size();i++)
{
Ogre::Entity *entity = entitylist.mEntities[i];
if(entity != entitylist.mSkelBase && entity->hasSkeleton())
{
entity->shareSkeletonInstanceWith(entitylist.mSkelBase);
parent->attachObject(entity);
parentNode->attachObject(entity);
}
else if(entity != entitylist.mSkelBase)
entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity);
entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity);
}
}
else
{
for(size_t i = 0;i < entitylist.mEntities.size();i++)
parent->attachObject(entitylist.mEntities[i]);
parentNode->attachObject(entitylist.mEntities[i]);
}
return entitylist;
}
EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
const std::string &name,
const std::string &group)
EntityList Loader::createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
std::string name, const std::string &group)
{
EntityList entitylist;
MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group);
Misc::StringUtils::toLower(name);
MeshInfoList meshes = load(name, group);
if(meshes.size() == 0)
return entitylist;
Ogre::SceneManager *sceneMgr = parentNode->getCreator();
std::string filter = "tri "+bonename;
std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower);
std::string filter = "@shape=tri "+bonename;
Misc::StringUtils::toLower(filter);
for(size_t i = 0;i < meshes.size();i++)
{
Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first);
if(ent->hasSkeleton())
{
Misc::StringUtils::toLower(meshes[i].second);
if(meshes[i].second.length() < filter.length() ||
meshes[i].second.compare(0, filter.length(), filter) != 0)
{
sceneMgr->destroyEntity(ent);
continue;
}
if(!entitylist.mSkelBase)
entitylist.mSkelBase = ent;
}
Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].mMeshName);
if(!entitylist.mSkelBase && ent->hasSkeleton())
entitylist.mSkelBase = ent;
entitylist.mEntities.push_back(ent);
}
@ -1196,20 +1246,20 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo
if(entitylist.mSkelBase)
{
entitylist.mSkelBase->shareSkeletonInstanceWith(parent);
parentNode->attachObject(entitylist.mSkelBase);
for(size_t i = 0;i < entitylist.mEntities.size();i++)
{
Ogre::Entity *entity = entitylist.mEntities[i];
if(entity != entitylist.mSkelBase && entity->hasSkeleton())
if(entity->hasSkeleton())
{
entity->shareSkeletonInstanceWith(parent);
parentNode->attachObject(entity);
if(entity != entitylist.mSkelBase)
entity->shareSkeletonInstanceWith(entitylist.mSkelBase);
if(entity->getMesh()->getName().find(filter) != std::string::npos)
parentNode->attachObject(entity);
}
else if(entity != entitylist.mSkelBase)
else
{
Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity);
tag->setScale(scale);
if(entity->getMesh()->getName().find(filter) != std::string::npos)
entitylist.mSkelBase->attachObjectToBone(meshes[i].mTargetNode, entity);
}
}
}
@ -1226,6 +1276,33 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo
}
bool Loader::createSkeleton(const std::string &name, const std::string &group)
{
Nif::NIFFile::ptr pnif = Nif::NIFFile::create(name);
Nif::NIFFile &nif = *pnif.get();
if(nif.numRecords() < 1)
{
nif.warn("Found no NIF records in "+name+".");
return false;
}
// The first record is assumed to be the root node
Nif::Record const *r = nif.getRecord(0);
assert(r != NULL);
Nif::Node const *node = dynamic_cast<Nif::Node const *>(r);
if(node == NULL)
{
nif.warn("First record in "+name+" was not a node, but a "+
r->recName+".");
return false;
}
NIFSkeletonLoader skelldr;
return skelldr.createSkeleton(name, group, node);
}
/* More code currently not in use, from the old D source. This was
used in the first attempt at loading NIF meshes, where each submesh
in the file was given a separate bone in a skeleton. Unfortunately

View file

@ -31,18 +31,14 @@
#include <vector>
#include <string>
namespace Nif
{
class Node;
class Transformation;
class NiTriShape;
}
// FIXME: This namespace really doesn't do anything Nif-specific. Any supportable
// model format should go through this.
namespace NifOgre
{
// FIXME: These should not be in NifOgre, it works agnostic of what model format is used
typedef std::multimap<float,std::string> TextKeyMap;
static const char sTextKeyExtraDataID[] = "TextKeyExtraData";
struct EntityList {
std::vector<Ogre::Entity*> mEntities;
Ogre::Entity *mSkelBase;
@ -52,39 +48,45 @@ struct EntityList {
};
/** This holds a list of mesh names along with the names of their parent nodes */
typedef std::vector< std::pair<std::string,std::string> > MeshPairList;
/* This holds a list of mesh names, the names of their parent nodes, and the offset
* from their parent nodes. */
struct MeshInfo {
std::string mMeshName;
std::string mTargetNode;
/** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into
something Ogre can use.
MeshInfo(const std::string &name, const std::string &target)
: mMeshName(name), mTargetNode(target)
{ }
};
typedef std::vector<MeshInfo> MeshInfoList;
You have to insert meshes manually into Ogre like this:
NIFLoader::load("somemesh.nif");
This returns a list of meshes used by the model, as well as the names of
their parent nodes (as they pertain to the skeleton, which is optionally
returned in the second argument if it exists).
*/
class NIFLoader
class Loader
{
static MeshPairList load(std::string name, std::string skelName, const std::string &group);
static MeshInfoList load(const std::string &name, const std::string &group);
public:
static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename,
Ogre::SceneNode *parentNode,
const std::string &name,
std::string name,
const std::string &group="General");
static EntityList createEntities(Ogre::SceneNode *parent,
TextKeyMap *textkeys,
const std::string &name,
static EntityList createEntities(Ogre::SceneNode *parentNode,
std::string name,
const std::string &group="General");
static bool createSkeleton(const std::string &name, const std::string &group="General");
};
}
namespace std
{
// These operators allow extra data types to be stored in an Ogre::Any
// object, which can then be stored in user object bindings on the nodes
ostream& operator<<(ostream &o, const NifOgre::TextKeyMap&);
}
#endif