diff --git a/components/nifogre/ogre_nif_loader.cpp b/components/nifogre/ogre_nif_loader.cpp index dca459119..31d873489 100644 --- a/components/nifogre/ogre_nif_loader.cpp +++ b/components/nifogre/ogre_nif_loader.cpp @@ -43,16 +43,26 @@ #include +#include #include #include typedef unsigned char ubyte; -using namespace std; -using namespace Nif; -using namespace NifOgre; +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; } +} +namespace NifOgre +{ // Helper class that computes the bounding box and of a mesh class BoundsFinder { @@ -62,7 +72,7 @@ class BoundsFinder MaxMinFinder() { - min = numeric_limits::infinity(); + min = std::numeric_limits::infinity(); max = -min; } @@ -152,8 +162,9 @@ static void fail(const std::string &msg) } -static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textkeys) +static TextKeyMap extractTextKeys(const Nif::NiTextKeyExtraData *tk) { + TextKeyMap textkeys; for(size_t i = 0;i < tk->list.size();i++) { const std::string &str = tk->list[i].text; @@ -166,11 +177,12 @@ static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, TextKeyMap *textke 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))); + textkeys.insert(std::make_pair(tk->list[i].time, str.substr(pos, nextpos-pos))); pos = nextpos; } } + return textkeys; } @@ -197,6 +209,17 @@ void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vectornext; } + Nif::ExtraPtr e = node->extra; + while(!e.empty()) + { + if(e->recType == Nif::RC_NiTextKeyExtraData) + { + const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); + bone->getUserObjectBindings().setUserAny("TextKeyExtraData", Ogre::Any(extractTextKeys(tk))); + } + e = e->extra; + } + const Nif::NiNode *ninode = dynamic_cast(node); if(ninode) { @@ -219,7 +242,7 @@ struct KeyTimeSort }; -typedef std::map LoaderMap; +typedef std::map LoaderMap; static LoaderMap sLoaders; public: @@ -269,16 +292,16 @@ void loadResource(Ogre::Resource *resource) Nif::NiKeyframeData *kf = kfc->data.getPtr(); /* Get the keyframes and make sure they're sorted first to last */ - QuaternionKeyList quatkeys = kf->mRotations; - Vector3KeyList trankeys = kf->mTranslations; - FloatKeyList scalekeys = kf->mScales; + Nif::QuaternionKeyList quatkeys = kf->mRotations; + Nif::Vector3KeyList trankeys = kf->mTranslations; + Nif::FloatKeyList scalekeys = kf->mScales; std::sort(quatkeys.mKeys.begin(), quatkeys.mKeys.end(), KeyTimeSort()); std::sort(trankeys.mKeys.begin(), trankeys.mKeys.end(), KeyTimeSort()); std::sort(scalekeys.mKeys.begin(), scalekeys.mKeys.end(), KeyTimeSort()); - QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin(); - Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin(); - FloatKeyList::VecType::const_iterator scaleiter = scalekeys.mKeys.begin(); + 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(); @@ -346,7 +369,7 @@ void loadResource(Ogre::Resource *resource) kframe->setRotation(curquat); else { - QuaternionKeyList::VecType::const_iterator last = quatiter-1; + Nif::QuaternionKeyList::VecType::const_iterator last = quatiter-1; float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime); kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat)); } @@ -354,7 +377,7 @@ void loadResource(Ogre::Resource *resource) kframe->setTranslate(curtrans); else { - Vector3KeyList::VecType::const_iterator last = traniter-1; + Nif::Vector3KeyList::VecType::const_iterator last = traniter-1; float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime); kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff)); } @@ -362,7 +385,7 @@ void loadResource(Ogre::Resource *resource) kframe->setScale(curscale); else { - FloatKeyList::VecType::const_iterator last = scaleiter-1; + Nif::FloatKeyList::VecType::const_iterator last = scaleiter-1; float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime); kframe->setScale(lastscale + ((curscale-lastscale)*diff)); } @@ -371,35 +394,18 @@ void loadResource(Ogre::Resource *resource) anim->optimise(); } -bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, const Nif::Node *node) +bool createSkeleton(const std::string &name, const std::string &group, const Nif::Node *node) { - if(textkeys) - { - Nif::ExtraPtr e = node->extra; - while(!e.empty()) - { - if(e->recType == Nif::RC_NiTextKeyExtraData) - { - const Nif::NiTextKeyExtraData *tk = static_cast(e.getPtr()); - insertTextKeys(tk, textkeys); - } - e = e->extra; - } - } - if(node->boneTrafo != NULL) { Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton(); - Ogre::SkeletonPtr skel = skelMgr.getByName(name); if(skel.isNull()) { NIFSkeletonLoader *loader = &sLoaders[name]; skel = skelMgr.create(name, group, true, loader); } - - if(!textkeys || textkeys->size() > 0) - return true; + return true; } const Nif::NiNode *ninode = dynamic_cast(node); @@ -410,7 +416,7 @@ bool createSkeleton(const std::string &name, const std::string &group, TextKeyMa { if(!children[i].empty()) { - if(createSkeleton(name, group, textkeys, children[i].getPtr())) + if(createSkeleton(name, group, children[i].getPtr())) return true; } } @@ -484,7 +490,7 @@ static void fail(const std::string &msg) public: -static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) +static Ogre::String getMaterial(const Nif::NiTriShape *shape, const Ogre::String &name, const Ogre::String &group) { Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton(); Ogre::MaterialPtr material = matMgr.getByName(name); @@ -504,24 +510,24 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam bool vertexColour = (shape->data->colors.size() != 0); // These are set below if present - const NiTexturingProperty *t = NULL; - const NiMaterialProperty *m = NULL; - const NiAlphaProperty *a = NULL; + const Nif::NiTexturingProperty *t = NULL; + const Nif::NiMaterialProperty *m = NULL; + const Nif::NiAlphaProperty *a = NULL; // Scan the property list for material information - const PropertyList &list = shape->props; + const Nif::PropertyList &list = shape->props; for (size_t i = 0;i < list.length();i++) { // Entries may be empty if (list[i].empty()) continue; - const Property *pr = list[i].getPtr(); - if (pr->recType == RC_NiTexturingProperty) - t = static_cast(pr); - else if (pr->recType == RC_NiMaterialProperty) - m = static_cast(pr); - else if (pr->recType == RC_NiAlphaProperty) - a = static_cast(pr); + const Nif::Property *pr = list[i].getPtr(); + if (pr->recType == Nif::RC_NiTexturingProperty) + t = static_cast(pr); + else if (pr->recType == Nif::RC_NiMaterialProperty) + m = static_cast(pr); + else if (pr->recType == Nif::RC_NiAlphaProperty) + a = static_cast(pr); else warn("Skipped property type: "+pr->recName); } @@ -529,21 +535,18 @@ static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &nam // Texture if (t && t->textures[0].inUse) { - NiSourceTexture *st = t->textures[0].texture.getPtr(); + Nif::NiSourceTexture *st = t->textures[0].texture.getPtr(); if (st->external) { /* Bethesda at some at some point converted all their BSA * textures from tga to dds for increased load speed, but all * texture file name references were kept as .tga. */ - - static const char * path = "textures\\"; + static const char path[] = "textures\\"; texName = path + st->filename; - Ogre::String::size_type pos = texName.rfind('.'); - - if (texName.compare (pos, texName.size () - pos, ".dds") != 0) + if(pos != Ogre::String::npos && texName.compare(pos, texName.length() - pos, ".dds") != 0) { // since we know all (GOTY edition or less) textures end // in .dds, we change the extension @@ -930,7 +933,7 @@ class NIFMeshLoader : Ogre::ManualResourceLoader } - typedef std::map LoaderMap; + typedef std::map LoaderMap; static LoaderMap sLoaders; public: @@ -993,7 +996,7 @@ public: if(node->recType == Nif::RC_NiTriShape) { - const NiTriShape *shape = dynamic_cast(node); + const Nif::NiTriShape *shape = dynamic_cast(node); Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton(); std::string fullname = mName+"@shape="+shape->name; @@ -1016,7 +1019,7 @@ public: mesh->setAutoBuildEdgeLists(false); } - meshes.push_back(std::make_pair(mesh, shape->name)); + meshes.push_back(std::make_pair(mesh->getName(), shape->name)); } else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode && node->recType != Nif::RC_NiRotatingParticles) @@ -1037,13 +1040,19 @@ public: NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders; -MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group) -{ - MeshPairList meshes; +typedef std::map MeshPairMap; +static MeshPairMap sMeshPairMap; +MeshPairList NIFLoader::load(std::string name, std::string skelName, const std::string &group) +{ std::transform(name.begin(), name.end(), name.begin(), ::tolower); std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower); + MeshPairMap::const_iterator meshiter = sMeshPairMap.find(name+"@skel="+skelName); + if(meshiter != sMeshPairMap.end()) + return meshiter->second; + + MeshPairList &meshes = sMeshPairMap[name+"@skel="+skelName]; Nif::NIFFile nif(name); if (nif.numRecords() < 1) { @@ -1064,7 +1073,7 @@ MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap } NIFSkeletonLoader skelldr; - bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node); + bool hasSkel = skelldr.createSkeleton(name, group, node); NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string())); meshldr.createMeshes(node, meshes); @@ -1076,19 +1085,37 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke { EntityList entitylist; - MeshPairList meshes = load(name, name, textkeys, group); + MeshPairList meshes = load(name, name, group); if(meshes.size() == 0) return entitylist; Ogre::SceneManager *sceneMgr = parent->getCreator(); for(size_t i = 0;i < meshes.size();i++) { - entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first->getName())); + entitylist.mEntities.push_back(sceneMgr->createEntity(meshes[i].first)); 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(data); + break; + } + } + } + if(entitylist.mSkelBase) { parent->attachObject(entitylist.mSkelBase); @@ -1113,13 +1140,6 @@ EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textke return entitylist; } -struct checklow { - bool operator()(const char &a, const char &b) const - { - return ::tolower(a) == ::tolower(b); - } -}; - EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bonename, Ogre::SceneNode *parentNode, const std::string &name, @@ -1127,23 +1147,24 @@ EntityList NIFLoader::createEntities(Ogre::Entity *parent, const std::string &bo { EntityList entitylist; - MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group); + MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), group); if(meshes.size() == 0) return entitylist; Ogre::SceneManager *sceneMgr = parentNode->getCreator(); - std::string filter = "Tri "+bonename; + std::string filter = "tri "+bonename; + std::transform(filter.begin()+4, filter.end(), filter.begin()+4, ::tolower); for(size_t i = 0;i < meshes.size();i++) { - Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName()); + Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first); if(ent->hasSkeleton()) { + std::transform(meshes[i].second.begin(), meshes[i].second.end(), meshes[i].second.begin(), ::tolower); + if(meshes[i].second.length() < filter.length() || - std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end()) + meshes[i].second.compare(0, filter.length(), filter) != 0) { sceneMgr->destroyEntity(ent); - meshes.erase(meshes.begin()+i); - i--; continue; } if(!entitylist.mSkelBase) @@ -1228,3 +1249,5 @@ extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, */ + +} // nsmaepace NifOgre diff --git a/components/nifogre/ogre_nif_loader.hpp b/components/nifogre/ogre_nif_loader.hpp index a203112b5..3e05c5873 100644 --- a/components/nifogre/ogre_nif_loader.hpp +++ b/components/nifogre/ogre_nif_loader.hpp @@ -30,23 +30,6 @@ #include #include -#include -#include - -#include "../nif/node.hpp" - -#include - -class BoundsFinder; - -struct ciLessBoost : std::binary_function -{ - bool operator() (const std::string & s1, const std::string & s2) const - { - //case insensitive version of is_less - return boost::algorithm::lexicographical_compare(s1, s2, boost::algorithm::is_iless()); - } -}; namespace Nif { @@ -69,9 +52,8 @@ struct EntityList { }; -/** This holds a list of meshes along with the names of their parent nodes - */ -typedef std::vector< std::pair > MeshPairList; +/** This holds a list of mesh names along with the names of their parent nodes */ +typedef std::vector< std::pair > MeshPairList; /** Manual resource loader for NIF meshes. This is the main class responsible for translating the internal NIF mesh structure into @@ -87,7 +69,7 @@ typedef std::vector< std::pair > MeshPairList; */ class NIFLoader { - static MeshPairList load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group); + static MeshPairList load(std::string name, std::string skelName, const std::string &group); public: static EntityList createEntities(Ogre::Entity *parent, const std::string &bonename,