mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 17:56:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1218 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1218 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|   OpenMW - The completely unofficial reimplementation of Morrowind
 | |
|   Copyright (C) 2008-2010  Nicolay Korslund
 | |
|   Email: < korslund@gmail.com >
 | |
|   WWW: http://openmw.sourceforge.net/
 | |
| 
 | |
|   This file (ogre_nif_loader.cpp) is part of the OpenMW package.
 | |
| 
 | |
|   OpenMW is distributed as free software: you can redistribute it
 | |
|   and/or modify it under the terms of the GNU General Public License
 | |
|   version 3, as published by the Free Software Foundation.
 | |
| 
 | |
|   This program is distributed in the hope that it will be useful, but
 | |
|   WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|   General Public License for more details.
 | |
| 
 | |
|   You should have received a copy of the GNU General Public License
 | |
|   version 3 along with this program. If not, see
 | |
|   http://www.gnu.org/licenses/ .
 | |
| 
 | |
|  */
 | |
| 
 | |
| //loadResource->handleNode->handleNiTriShape->createSubMesh
 | |
| 
 | |
| #include "ogre_nif_loader.hpp"
 | |
| 
 | |
| #include <algorithm>
 | |
| 
 | |
| #include <OgreMaterialManager.h>
 | |
| #include <OgreMeshManager.h>
 | |
| #include <OgreHardwareBufferManager.h>
 | |
| #include <OgreSkeletonManager.h>
 | |
| #include <OgreTechnique.h>
 | |
| #include <OgreSubMesh.h>
 | |
| #include <OgreRoot.h>
 | |
| #include <OgreEntity.h>
 | |
| #include <OgreTagPoint.h>
 | |
| 
 | |
| #include <boost/lexical_cast.hpp>
 | |
| #include <boost/algorithm/string.hpp>
 | |
| #include <boost/functional/hash.hpp>
 | |
| 
 | |
| #include <extern/shiny/Main/Factory.hpp>
 | |
| 
 | |
| #include <components/settings/settings.hpp>
 | |
| #include <components/nifoverrides/nifoverrides.hpp>
 | |
| 
 | |
| typedef unsigned char ubyte;
 | |
| 
 | |
| using namespace std;
 | |
| using namespace Nif;
 | |
| using namespace NifOgre;
 | |
| 
 | |
| 
 | |
| // Helper class that computes the bounding box and of a mesh
 | |
| class BoundsFinder
 | |
| {
 | |
|     struct MaxMinFinder
 | |
|     {
 | |
|         float max, min;
 | |
| 
 | |
|         MaxMinFinder()
 | |
|         {
 | |
|             min = numeric_limits<float>::infinity();
 | |
|             max = -min;
 | |
|         }
 | |
| 
 | |
|         void add(float f)
 | |
|         {
 | |
|             if (f > max) max = f;
 | |
|             if (f < min) min = f;
 | |
|         }
 | |
| 
 | |
|         // Return Max(max**2, min**2)
 | |
|         float getMaxSquared()
 | |
|         {
 | |
|             float m1 = max*max;
 | |
|             float m2 = min*min;
 | |
|             if (m1 >= m2) return m1;
 | |
|             return m2;
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     MaxMinFinder X, Y, Z;
 | |
| 
 | |
| public:
 | |
|     // Add 'verts' vertices to the calculation. The 'data' pointer is
 | |
|     // expected to point to 3*verts floats representing x,y,z for each
 | |
|     // point.
 | |
|     void add(float *data, int verts)
 | |
|     {
 | |
|         for (int i=0;i<verts;i++)
 | |
|         {
 | |
|             X.add(*(data++));
 | |
|             Y.add(*(data++));
 | |
|             Z.add(*(data++));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // True if this structure has valid values
 | |
|     bool isValid()
 | |
|     {
 | |
|         return
 | |
|             minX() <= maxX() &&
 | |
|             minY() <= maxY() &&
 | |
|             minZ() <= maxZ();
 | |
|     }
 | |
| 
 | |
|     // Compute radius
 | |
|     float getRadius()
 | |
|     {
 | |
|         assert(isValid());
 | |
| 
 | |
|         // The radius is computed from the origin, not from the geometric
 | |
|         // center of the mesh.
 | |
|         return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
 | |
|     }
 | |
| 
 | |
|     float minX() {
 | |
|         return X.min;
 | |
|     }
 | |
|     float maxX() {
 | |
|         return X.max;
 | |
|     }
 | |
|     float minY() {
 | |
|         return Y.min;
 | |
|     }
 | |
|     float maxY() {
 | |
|         return Y.max;
 | |
|     }
 | |
|     float minZ() {
 | |
|         return Z.min;
 | |
|     }
 | |
|     float maxZ() {
 | |
|         return Z.max;
 | |
|     }
 | |
| };
 | |
| 
 | |
| 
 | |
| class NIFSkeletonLoader : public Ogre::ManualResourceLoader {
 | |
| 
 | |
| static void warn(const std::string &msg)
 | |
| {
 | |
|     std::cerr << "NIFSkeletonLoader: Warn: " << msg << std::endl;
 | |
| }
 | |
| 
 | |
| static void fail(const std::string &msg)
 | |
| {
 | |
|     std::cerr << "NIFSkeletonLoader: Fail: "<< msg << std::endl;
 | |
|     abort();
 | |
| }
 | |
| 
 | |
| 
 | |
| static void insertTextKeys(const Nif::NiTextKeyExtraData *tk, 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;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| void buildBones(Ogre::Skeleton *skel, const Nif::Node *node, std::vector<Nif::NiKeyframeController*> &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;
 | |
|     }
 | |
| 
 | |
|     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,ciLessBoost> 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 nif(skel->getName());
 | |
|     const Nif::Node *node = dynamic_cast<const Nif::Node*>(nif.getRecord(0));
 | |
| 
 | |
|     std::vector<Nif::NiKeyframeController*> 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 *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));
 | |
| 
 | |
|     for(size_t i = 0;i < ctrls.size();i++)
 | |
|     {
 | |
|         Nif::NiKeyframeController *kfc = ctrls[i];
 | |
|         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;
 | |
|         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>());
 | |
| 
 | |
|         QuaternionKeyList::VecType::const_iterator quatiter = quatkeys.mKeys.begin();
 | |
|         Vector3KeyList::VecType::const_iterator traniter = trankeys.mKeys.begin();
 | |
|         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());
 | |
| 
 | |
|         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;
 | |
|         if(traniter != trankeys.mKeys.end())
 | |
|             lasttrans = curtrans = traniter->mValue - starttrans;
 | |
|         if(scaleiter != scalekeys.mKeys.end())
 | |
|             lastscale = curscale = Ogre::Vector3(scaleiter->mValue) / startscale;
 | |
|         bool didlast = false;
 | |
| 
 | |
|         while(!didlast)
 | |
|         {
 | |
|             float curtime = kfc->timeStop;
 | |
| 
 | |
|             //Get latest time
 | |
|             if(quatiter != quatkeys.mKeys.end())
 | |
|                 curtime = std::min(curtime, quatiter->mTime);
 | |
|             if(traniter != trankeys.mKeys.end())
 | |
|                 curtime = std::min(curtime, traniter->mTime);
 | |
|             if(scaleiter != scalekeys.mKeys.end())
 | |
|                 curtime = std::min(curtime, scaleiter->mTime);
 | |
| 
 | |
|             curtime = std::max(curtime, kfc->timeStart);
 | |
|             if(curtime >= kfc->timeStop)
 | |
|             {
 | |
|                 didlast = true;
 | |
|                 curtime = kfc->timeStop;
 | |
|             }
 | |
| 
 | |
|             // Get the latest quaternions, translations, and scales for the
 | |
|             // current time
 | |
|             while(quatiter != quatkeys.mKeys.end() && curtime >= quatiter->mTime)
 | |
|             {
 | |
|                 lastquat = curquat;
 | |
|                 quatiter++;
 | |
|                 if(quatiter != quatkeys.mKeys.end())
 | |
|                     curquat = startquat.Inverse() * quatiter->mValue ;
 | |
|             }
 | |
|             while(traniter != trankeys.mKeys.end() && curtime >= traniter->mTime)
 | |
|             {
 | |
|                 lasttrans = curtrans;
 | |
|                 traniter++;
 | |
|                 if(traniter != trankeys.mKeys.end())
 | |
|                     curtrans = traniter->mValue - starttrans;
 | |
|             }
 | |
|             while(scaleiter != scalekeys.mKeys.end() && curtime >= scaleiter->mTime)
 | |
|             {
 | |
|                 lastscale = curscale;
 | |
|                 scaleiter++;
 | |
|                 if(scaleiter != scalekeys.mKeys.end())
 | |
|                     curscale = Ogre::Vector3(scaleiter->mValue) / startscale;
 | |
|             }
 | |
| 
 | |
|             Ogre::TransformKeyFrame *kframe;
 | |
|             kframe = nodetrack->createNodeKeyFrame(curtime);
 | |
|             if(quatiter == quatkeys.mKeys.end() || quatiter == quatkeys.mKeys.begin())
 | |
|                 kframe->setRotation(curquat);
 | |
|             else
 | |
|             {
 | |
|                 QuaternionKeyList::VecType::const_iterator last = quatiter-1;
 | |
|                 float diff = (curtime-last->mTime) / (quatiter->mTime-last->mTime);
 | |
|                 kframe->setRotation(Ogre::Quaternion::nlerp(diff, lastquat, curquat));
 | |
|             }
 | |
|             if(traniter == trankeys.mKeys.end() || traniter == trankeys.mKeys.begin())
 | |
|                 kframe->setTranslate(curtrans);
 | |
|             else
 | |
|             {
 | |
|                 Vector3KeyList::VecType::const_iterator last = traniter-1;
 | |
|                 float diff = (curtime-last->mTime) / (traniter->mTime-last->mTime);
 | |
|                 kframe->setTranslate(lasttrans + ((curtrans-lasttrans)*diff));
 | |
|             }
 | |
|             if(scaleiter == scalekeys.mKeys.end() || scaleiter == scalekeys.mKeys.begin())
 | |
|                 kframe->setScale(curscale);
 | |
|             else
 | |
|             {
 | |
|                 FloatKeyList::VecType::const_iterator last = scaleiter-1;
 | |
|                 float diff = (curtime-last->mTime) / (scaleiter->mTime-last->mTime);
 | |
|                 kframe->setScale(lastscale + ((curscale-lastscale)*diff));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     anim->optimise();
 | |
| }
 | |
| 
 | |
| bool createSkeleton(const std::string &name, const std::string &group, TextKeyMap *textkeys, 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<const Nif::NiTextKeyExtraData*>(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;
 | |
|     }
 | |
| 
 | |
|     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())
 | |
|             {
 | |
|                 if(createSkeleton(name, group, textkeys, children[i].getPtr()))
 | |
|                     return true;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| };
 | |
| NIFSkeletonLoader::LoaderMap NIFSkeletonLoader::sLoaders;
 | |
| 
 | |
| 
 | |
| // Conversion of blend / test mode from NIF -> OGRE.
 | |
| // Not in use yet, so let's comment it out.
 | |
| /*
 | |
| static SceneBlendFactor getBlendFactor(int mode)
 | |
| {
 | |
|   switch(mode)
 | |
|     {
 | |
|     case 0: return SBF_ONE;
 | |
|     case 1: return SBF_ZERO;
 | |
|     case 2: return SBF_SOURCE_COLOUR;
 | |
|     case 3: return SBF_ONE_MINUS_SOURCE_COLOUR;
 | |
|     case 4: return SBF_DEST_COLOUR;
 | |
|     case 5: return SBF_ONE_MINUS_DEST_COLOUR;
 | |
|     case 6: return SBF_SOURCE_ALPHA;
 | |
|     case 7: return SBF_ONE_MINUS_SOURCE_ALPHA;
 | |
|     case 8: return SBF_DEST_ALPHA;
 | |
|     case 9: return SBF_ONE_MINUS_DEST_ALPHA;
 | |
|       // [Comment from Chris Robinson:] Can't handle this mode? :/
 | |
|       // case 10: return SBF_SOURCE_ALPHA_SATURATE;
 | |
|     default:
 | |
|       return SBF_SOURCE_ALPHA;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| // This is also unused
 | |
| static CompareFunction getTestMode(int mode)
 | |
| {
 | |
|   switch(mode)
 | |
|     {
 | |
|     case 0: return CMPF_ALWAYS_PASS;
 | |
|     case 1: return CMPF_LESS;
 | |
|     case 2: return CMPF_EQUAL;
 | |
|     case 3: return CMPF_LESS_EQUAL;
 | |
|     case 4: return CMPF_GREATER;
 | |
|     case 5: return CMPF_NOT_EQUAL;
 | |
|     case 6: return CMPF_GREATER_EQUAL;
 | |
|     case 7: return CMPF_ALWAYS_FAIL;
 | |
|     default:
 | |
|       return CMPF_ALWAYS_PASS;
 | |
|     }
 | |
| }
 | |
| */
 | |
| 
 | |
| 
 | |
| class NIFMaterialLoader {
 | |
| 
 | |
| static std::map<size_t,std::string> MaterialMap;
 | |
| 
 | |
| static void warn(const std::string &msg)
 | |
| {
 | |
|     std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl;
 | |
| }
 | |
| 
 | |
| static void fail(const std::string &msg)
 | |
| {
 | |
|     std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl;
 | |
|     abort();
 | |
| }
 | |
| 
 | |
| 
 | |
| public:
 | |
| static Ogre::String getMaterial(const NiTriShape *shape, const Ogre::String &name, const Ogre::String &group)
 | |
| {
 | |
|     Ogre::MaterialManager &matMgr = Ogre::MaterialManager::getSingleton();
 | |
|     Ogre::MaterialPtr material = matMgr.getByName(name);
 | |
|     if(!material.isNull())
 | |
|         return name;
 | |
| 
 | |
|     Ogre::Vector3 ambient(1.0f);
 | |
|     Ogre::Vector3 diffuse(1.0f);
 | |
|     Ogre::Vector3 specular(0.0f);
 | |
|     Ogre::Vector3 emissive(0.0f);
 | |
|     float glossiness = 0.0f;
 | |
|     float alpha = 1.0f;
 | |
|     int alphaFlags = -1;
 | |
| //    ubyte alphaTest = 0;
 | |
|     Ogre::String texName;
 | |
| 
 | |
|     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;
 | |
| 
 | |
|     // Scan the property list for material information
 | |
|     const 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<const NiTexturingProperty*>(pr);
 | |
|         else if (pr->recType == RC_NiMaterialProperty)
 | |
|             m = static_cast<const NiMaterialProperty*>(pr);
 | |
|         else if (pr->recType == RC_NiAlphaProperty)
 | |
|             a = static_cast<const NiAlphaProperty*>(pr);
 | |
|         else
 | |
|             warn("Skipped property type: "+pr->recName);
 | |
|     }
 | |
| 
 | |
|     // Texture
 | |
|     if (t && t->textures[0].inUse)
 | |
|     {
 | |
|         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.
 | |
|              */
 | |
|             texName = "textures\\" + st->filename;
 | |
|             if(!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(texName))
 | |
|             {
 | |
|                 Ogre::String::size_type pos = texName.rfind('.');
 | |
|                 texName.replace(pos, texName.length(), ".dds");
 | |
|             }
 | |
|         }
 | |
|         else warn("Found internal texture, ignoring.");
 | |
|     }
 | |
| 
 | |
|     // Alpha modifiers
 | |
|     if (a)
 | |
|     {
 | |
|         alphaFlags = a->flags;
 | |
| //        alphaTest = a->data.threshold;
 | |
|     }
 | |
| 
 | |
|     // Material
 | |
|     if(m)
 | |
|     {
 | |
|         ambient = m->data.ambient;
 | |
|         diffuse = m->data.diffuse;
 | |
|         specular = m->data.specular;
 | |
|         emissive = m->data.emissive;
 | |
|         glossiness = m->data.glossiness;
 | |
|         alpha = m->data.alpha;
 | |
|     }
 | |
| 
 | |
|     Ogre::String matname = name;
 | |
|     if (m || !texName.empty())
 | |
|     {
 | |
|         // Generate a hash out of all properties that can affect the material.
 | |
|         size_t h = 0;
 | |
|         boost::hash_combine(h, ambient.x);
 | |
|         boost::hash_combine(h, ambient.y);
 | |
|         boost::hash_combine(h, ambient.z);
 | |
|         boost::hash_combine(h, diffuse.x);
 | |
|         boost::hash_combine(h, diffuse.y);
 | |
|         boost::hash_combine(h, diffuse.z);
 | |
|         boost::hash_combine(h, specular.x);
 | |
|         boost::hash_combine(h, specular.y);
 | |
|         boost::hash_combine(h, specular.z);
 | |
|         boost::hash_combine(h, emissive.x);
 | |
|         boost::hash_combine(h, emissive.y);
 | |
|         boost::hash_combine(h, emissive.z);
 | |
|         boost::hash_combine(h, texName);
 | |
|         boost::hash_combine(h, vertexColour);
 | |
|         boost::hash_combine(h, alphaFlags);
 | |
| 
 | |
|         std::map<size_t,std::string>::iterator itr = MaterialMap.find(h);
 | |
|         if (itr != MaterialMap.end())
 | |
|         {
 | |
|             // a suitable material exists already - use it
 | |
|             return itr->second;
 | |
|         }
 | |
|         // not found, create a new one
 | |
|         MaterialMap.insert(std::make_pair(h, matname));
 | |
|     }
 | |
| 
 | |
|     // No existing material like this. Create a new one.
 | |
|     sh::MaterialInstance* instance = sh::Factory::getInstance ().createMaterialInstance (matname, "openmw_objects_base");
 | |
|     instance->setProperty ("ambient", sh::makeProperty<sh::Vector3> (
 | |
|         new sh::Vector3(ambient.x, ambient.y, ambient.z)));
 | |
| 
 | |
|     instance->setProperty ("diffuse", sh::makeProperty<sh::Vector4> (
 | |
|         new sh::Vector4(diffuse.x, diffuse.y, diffuse.z, alpha)));
 | |
| 
 | |
|     instance->setProperty ("specular", sh::makeProperty<sh::Vector4> (
 | |
|         new sh::Vector4(specular.x, specular.y, specular.z, glossiness)));
 | |
| 
 | |
|     instance->setProperty ("emissive", sh::makeProperty<sh::Vector3> (
 | |
|         new sh::Vector3(emissive.x, emissive.y, emissive.z)));
 | |
| 
 | |
|     instance->setProperty ("diffuseMap", sh::makeProperty(texName));
 | |
| 
 | |
|     if (vertexColour)
 | |
|         instance->setProperty ("has_vertex_colour", sh::makeProperty<sh::BooleanValue>(new sh::BooleanValue(true)));
 | |
| 
 | |
|     // Add transparency if NiAlphaProperty was present
 | |
|     if (alphaFlags != -1)
 | |
|     {
 | |
|         // The 237 alpha flags are by far the most common. Check
 | |
|         // NiAlphaProperty in nif/property.h if you need to decode
 | |
|         // other values. 237 basically means normal transparencly.
 | |
|         if (alphaFlags == 237)
 | |
|         {
 | |
|             NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName);
 | |
|             if (result.first)
 | |
|             {
 | |
|                 instance->setProperty("alpha_rejection",
 | |
|                     sh::makeProperty<sh::StringValue>(new sh::StringValue("greater_equal " + boost::lexical_cast<std::string>(result.second))));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 // Enable transparency
 | |
|                 instance->setProperty("scene_blend", sh::makeProperty<sh::StringValue>(new sh::StringValue("alpha_blend")));
 | |
|                 instance->setProperty("depth_write", sh::makeProperty<sh::StringValue>(new sh::StringValue("off")));
 | |
|             }
 | |
|         }
 | |
|         else
 | |
|             warn("Unhandled alpha setting for texture " + texName);
 | |
|     }
 | |
|     else
 | |
|         instance->getMaterial ()->setShadowCasterMaterial ("openmw_shadowcaster_noalpha");
 | |
| 
 | |
|     // As of yet UNTESTED code from Chris:
 | |
|     /*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
 | |
|     pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL);
 | |
|     pass->setDepthCheckEnabled(true);
 | |
| 
 | |
|     // Add transparency if NiAlphaProperty was present
 | |
|     if (alphaFlags != -1)
 | |
|     {
 | |
|         std::cout << "Alpha flags set!" << endl;
 | |
|         if ((alphaFlags&1))
 | |
|         {
 | |
|             pass->setDepthWriteEnabled(false);
 | |
|             pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf),
 | |
|                                    getBlendFactor((alphaFlags>>5)&0xf));
 | |
|         }
 | |
|         else
 | |
|             pass->setDepthWriteEnabled(true);
 | |
| 
 | |
|         if ((alphaFlags>>9)&1)
 | |
|             pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7),
 | |
|                                          alphaTest);
 | |
| 
 | |
|         pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1));
 | |
|     }
 | |
| */
 | |
| 
 | |
|     return matname;
 | |
| }
 | |
| 
 | |
| };
 | |
| std::map<size_t,std::string> NIFMaterialLoader::MaterialMap;
 | |
| 
 | |
| 
 | |
| class NIFMeshLoader : Ogre::ManualResourceLoader
 | |
| {
 | |
|     std::string mName;
 | |
|     std::string mGroup;
 | |
|     std::string mShapeName;
 | |
|     std::string mMaterialName;
 | |
|     std::string mSkelName;
 | |
| 
 | |
|     void warn(const std::string &msg)
 | |
|     {
 | |
|         std::cerr << "NIFMeshLoader: Warn: " << msg << std::endl;
 | |
|     }
 | |
| 
 | |
|     void fail(const std::string &msg)
 | |
|     {
 | |
|         std::cerr << "NIFMeshLoader: Fail: "<< msg << std::endl;
 | |
|         abort();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // Convert NiTriShape to Ogre::SubMesh
 | |
|     void handleNiTriShape(Ogre::Mesh *mesh, Nif::NiTriShape *shape)
 | |
|     {
 | |
|         Ogre::SkeletonPtr skel;
 | |
|         const Nif::NiTriShapeData *data = shape->data.getPtr();
 | |
|         const Nif::NiSkinInstance *skin = (shape->skin.empty() ? NULL : shape->skin.getPtr());
 | |
|         std::vector<Ogre::Vector3> srcVerts = data->vertices;
 | |
|         std::vector<Ogre::Vector3> srcNorms = data->normals;
 | |
|         if(skin != NULL)
 | |
|         {
 | |
|             // Only set a skeleton when skinning. Unskinned meshes with a skeleton will be
 | |
|             // explicitly attached later.
 | |
|             mesh->setSkeletonName(mSkelName);
 | |
| 
 | |
|             // 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();
 | |
| 
 | |
|             // 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
 | |
|             // be a reliable way to do that.
 | |
|             std::vector<Ogre::Vector3> newVerts(srcVerts.size(), Ogre::Vector3(0.0f));
 | |
|             std::vector<Ogre::Vector3> newNorms(srcNorms.size(), Ogre::Vector3(0.0f));
 | |
| 
 | |
|             const Nif::NiSkinData *data = skin->data.getPtr();
 | |
|             const Nif::NodeList &bones = skin->bones;
 | |
|             for(size_t b = 0;b < bones.length();b++)
 | |
|             {
 | |
|                 Ogre::Bone *bone = skel->getBone(bones[b]->name);
 | |
|                 Ogre::Matrix4 mat, mat2;
 | |
|                 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;
 | |
| 
 | |
|                 const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[b].weights;
 | |
|                 for(size_t i = 0;i < weights.size();i++)
 | |
|                 {
 | |
|                     size_t index = weights[i].vertex;
 | |
|                     float weight = weights[i].weight;
 | |
| 
 | |
|                     newVerts.at(index) += (mat*srcVerts[index]) * weight;
 | |
|                     if(newNorms.size() > index)
 | |
|                     {
 | |
|                         Ogre::Vector4 vec4(srcNorms[index][0], srcNorms[index][1], srcNorms[index][2], 0.0f);
 | |
|                         vec4 = mat*vec4 * weight;
 | |
|                         newNorms[index] += Ogre::Vector3(&vec4[0]);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             srcVerts = newVerts;
 | |
|             srcNorms = newNorms;
 | |
|         }
 | |
|         else if(mSkelName.length() == 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]);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Set the bounding box first
 | |
|         BoundsFinder bounds;
 | |
|         bounds.add(&srcVerts[0][0], srcVerts.size());
 | |
|         // No idea why this offset is needed. It works fine without it if the
 | |
|         // vertices weren't transformed first, but otherwise it fails later on
 | |
|         // when the object is being inserted into the scene.
 | |
|         mesh->_setBounds(Ogre::AxisAlignedBox(bounds.minX()-0.5f, bounds.minY()-0.5f, bounds.minZ()-0.5f,
 | |
|                                               bounds.maxX()+0.5f, bounds.maxY()+0.5f, bounds.maxZ()+0.5f));
 | |
|         mesh->_setBoundingSphereRadius(bounds.getRadius());
 | |
| 
 | |
|         // This function is just one long stream of Ogre-barf, but it works
 | |
|         // great.
 | |
|         Ogre::HardwareBufferManager *hwBufMgr = Ogre::HardwareBufferManager::getSingletonPtr();
 | |
|         Ogre::HardwareVertexBufferSharedPtr vbuf;
 | |
|         Ogre::HardwareIndexBufferSharedPtr ibuf;
 | |
|         Ogre::VertexBufferBinding *bind;
 | |
|         Ogre::VertexDeclaration *decl;
 | |
|         int nextBuf = 0;
 | |
| 
 | |
|         Ogre::SubMesh *sub = mesh->createSubMesh(shape->name);
 | |
| 
 | |
|         // Add vertices
 | |
|         sub->useSharedVertices = false;
 | |
|         sub->vertexData = new Ogre::VertexData();
 | |
|         sub->vertexData->vertexStart = 0;
 | |
|         sub->vertexData->vertexCount = srcVerts.size();
 | |
| 
 | |
|         decl = sub->vertexData->vertexDeclaration;
 | |
|         bind = sub->vertexData->vertexBufferBinding;
 | |
|         if(srcVerts.size())
 | |
|         {
 | |
|             vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
 | |
|                                                 srcVerts.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY,
 | |
|                                                 true);
 | |
|             vbuf->writeData(0, vbuf->getSizeInBytes(), &srcVerts[0][0], true);
 | |
| 
 | |
|             decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
 | |
|             bind->setBinding(nextBuf++, vbuf);
 | |
|         }
 | |
| 
 | |
|         // Vertex normals
 | |
|         if(srcNorms.size())
 | |
|         {
 | |
|             vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3),
 | |
|                                                 srcNorms.size(), Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY,
 | |
|                                                 true);
 | |
|             vbuf->writeData(0, vbuf->getSizeInBytes(), &srcNorms[0][0], true);
 | |
| 
 | |
|             decl->addElement(nextBuf, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
 | |
|             bind->setBinding(nextBuf++, vbuf);
 | |
|         }
 | |
| 
 | |
|         // Vertex colors
 | |
|         const std::vector<Ogre::Vector4> &colors = data->colors;
 | |
|         if(colors.size())
 | |
|         {
 | |
|             Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem();
 | |
|             std::vector<Ogre::RGBA> colorsRGB(colors.size());
 | |
|             for(size_t i = 0;i < colorsRGB.size();i++)
 | |
|             {
 | |
|                 Ogre::ColourValue clr(colors[i][0], colors[i][1], colors[i][2], colors[i][3]);
 | |
|                 rs->convertColourValue(clr, &colorsRGB[i]);
 | |
|             }
 | |
|             vbuf = hwBufMgr->createVertexBuffer(Ogre::VertexElement::getTypeSize(Ogre::VET_COLOUR),
 | |
|                                                 colorsRGB.size(), Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,
 | |
|                                                 true);
 | |
|             vbuf->writeData(0, vbuf->getSizeInBytes(), &colorsRGB[0], true);
 | |
|             decl->addElement(nextBuf, 0, Ogre::VET_COLOUR, Ogre::VES_DIFFUSE);
 | |
|             bind->setBinding(nextBuf++, vbuf);
 | |
|         }
 | |
| 
 | |
|         // Texture UV coordinates
 | |
|         size_t numUVs = data->uvlist.size();
 | |
|         if(numUVs)
 | |
|         {
 | |
|             size_t elemSize = Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT2);
 | |
|             vbuf = hwBufMgr->createVertexBuffer(elemSize, srcVerts.size()*numUVs,
 | |
|                                                 Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, true);
 | |
|             for(size_t i = 0;i < numUVs;i++)
 | |
|             {
 | |
|                 const std::vector<Ogre::Vector2> &uvlist = data->uvlist[i];
 | |
|                 vbuf->writeData(i*srcVerts.size()*elemSize, elemSize*srcVerts.size(), &uvlist[0], true);
 | |
|                 decl->addElement(nextBuf, i*srcVerts.size()*elemSize, Ogre::VET_FLOAT2,
 | |
|                                  Ogre::VES_TEXTURE_COORDINATES, i);
 | |
|             }
 | |
|             bind->setBinding(nextBuf++, vbuf);
 | |
|         }
 | |
| 
 | |
|         // Triangle faces
 | |
|         const std::vector<short> &srcIdx = data->triangles;
 | |
|         if(srcIdx.size())
 | |
|         {
 | |
|             ibuf = hwBufMgr->createIndexBuffer(Ogre::HardwareIndexBuffer::IT_16BIT, srcIdx.size(),
 | |
|                                                Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);
 | |
|             ibuf->writeData(0, ibuf->getSizeInBytes(), &srcIdx[0], true);
 | |
|             sub->indexData->indexBuffer = ibuf;
 | |
|             sub->indexData->indexCount = srcIdx.size();
 | |
|             sub->indexData->indexStart = 0;
 | |
|         }
 | |
| 
 | |
|         // Assign bone weights for this TriShape
 | |
|         if(skin != NULL)
 | |
|         {
 | |
|             const Nif::NiSkinData *data = skin->data.getPtr();
 | |
|             const Nif::NodeList &bones = skin->bones;
 | |
|             for(size_t i = 0;i < bones.length();i++)
 | |
|             {
 | |
|                 Ogre::VertexBoneAssignment boneInf;
 | |
|                 boneInf.boneIndex = skel->getBone(bones[i]->name)->getHandle();
 | |
| 
 | |
|                 const std::vector<Nif::NiSkinData::VertWeight> &weights = data->bones[i].weights;
 | |
|                 for(size_t j = 0;j < weights.size();j++)
 | |
|                 {
 | |
|                     boneInf.vertexIndex = weights[j].vertex;
 | |
|                     boneInf.weight = weights[j].weight;
 | |
|                     sub->addBoneAssignment(boneInf);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if(mMaterialName.length() > 0)
 | |
|             sub->setMaterialName(mMaterialName);
 | |
|     }
 | |
| 
 | |
|     bool findTriShape(Ogre::Mesh *mesh, Nif::Node *node)
 | |
|     {
 | |
|         if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name)
 | |
|         {
 | |
|             handleNiTriShape(mesh, dynamic_cast<Nif::NiTriShape*>(node));
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         Nif::NiNode *ninode = dynamic_cast<Nif::NiNode*>(node);
 | |
|         if(ninode)
 | |
|         {
 | |
|             Nif::NodeList &children = ninode->children;
 | |
|             for(size_t i = 0;i < children.length();i++)
 | |
|             {
 | |
|                 if(!children[i].empty())
 | |
|                 {
 | |
|                     if(findTriShape(mesh, children[i].getPtr()))
 | |
|                         return true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     typedef std::map<std::string,NIFMeshLoader,ciLessBoost> LoaderMap;
 | |
|     static LoaderMap sLoaders;
 | |
| 
 | |
| public:
 | |
|     NIFMeshLoader()
 | |
|     { }
 | |
|     NIFMeshLoader(const std::string &name, const std::string &group, const std::string skelName)
 | |
|       : mName(name), mGroup(group), mSkelName(skelName)
 | |
|     { }
 | |
| 
 | |
|     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!");
 | |
| 
 | |
|         if(!mShapeName.length())
 | |
|         {
 | |
|             if(mSkelName.length() > 0)
 | |
|                 mesh->setSkeletonName(mSkelName);
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         Nif::NIFFile nif(mName);
 | |
|         Nif::Node *node = dynamic_cast<Nif::Node*>(nif.getRecord(0));
 | |
|         findTriShape(mesh, node);
 | |
|     }
 | |
| 
 | |
|     void createMeshes(const Nif::Node *node, MeshPairList &meshes, int flags=0)
 | |
|     {
 | |
|         flags |= node->flags;
 | |
| 
 | |
|         // Marker objects: just skip the entire node
 | |
|         /// \todo don't do this in the editor
 | |
|         if (node->name.find("marker") != std::string::npos)
 | |
|             return;
 | |
| 
 | |
|         Nif::ExtraPtr e = node->extra;
 | |
|         while(!e.empty())
 | |
|         {
 | |
|             Nif::NiStringExtraData *sd;
 | |
|             Nif::NiTextKeyExtraData *td;
 | |
|             if((sd=dynamic_cast<Nif::NiStringExtraData*>(e.getPtr())) != NULL)
 | |
|             {
 | |
|                 // String markers may contain important information
 | |
|                 // affecting the entire subtree of this obj
 | |
|                 if(sd->string == "MRK")
 | |
|                 {
 | |
|                     // Marker objects. These are only visible in the
 | |
|                     // editor.
 | |
|                     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)
 | |
|         {
 | |
|             const NiTriShape *shape = dynamic_cast<const NiTriShape*>(node);
 | |
| 
 | |
|             Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
 | |
|             std::string fullname = mName+"@shape="+shape->name;
 | |
|             if(mSkelName.length() > 0 && mName != mSkelName)
 | |
|                 fullname += "@skel="+mSkelName;
 | |
| 
 | |
|             std::transform(fullname.begin(), fullname.end(), fullname.begin(), ::tolower);
 | |
|             Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
 | |
|             if(mesh.isNull())
 | |
|             {
 | |
|                 NIFMeshLoader *loader = &sLoaders[fullname];
 | |
|                 *loader = *this;
 | |
|                 if(!(flags&0x01)) // Not hidden
 | |
|                 {
 | |
|                     loader->mShapeName = shape->name;
 | |
|                     loader->mMaterialName = NIFMaterialLoader::getMaterial(shape, fullname, mGroup);
 | |
|                 }
 | |
| 
 | |
|                 mesh = meshMgr.createManual(fullname, mGroup, loader);
 | |
|                 mesh->setAutoBuildEdgeLists(false);
 | |
|             }
 | |
| 
 | |
|             meshes.push_back(std::make_pair(mesh, 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)
 | |
|         {
 | |
|             const Nif::NodeList &children = ninode->children;
 | |
|             for(size_t i = 0;i < children.length();i++)
 | |
|             {
 | |
|                 if(!children[i].empty())
 | |
|                     createMeshes(children[i].getPtr(), meshes, flags);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| };
 | |
| NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
 | |
| 
 | |
| 
 | |
| MeshPairList NIFLoader::load(std::string name, std::string skelName, TextKeyMap *textkeys, const std::string &group)
 | |
| {
 | |
|     MeshPairList meshes;
 | |
| 
 | |
|     std::transform(name.begin(), name.end(), name.begin(), ::tolower);
 | |
|     std::transform(skelName.begin(), skelName.end(), skelName.begin(), ::tolower);
 | |
| 
 | |
|     Nif::NIFFile nif(name);
 | |
|     if (nif.numRecords() < 1)
 | |
|     {
 | |
|         nif.warn("Found no records in NIF.");
 | |
|         return meshes;
 | |
|     }
 | |
| 
 | |
|     // The first record is assumed to be the root node
 | |
|     Nif::Record *r = nif.getRecord(0);
 | |
|     assert(r != NULL);
 | |
| 
 | |
|     Nif::Node *node = dynamic_cast<Nif::Node*>(r);
 | |
|     if(node == NULL)
 | |
|     {
 | |
|         nif.warn("First record in file was not a node, but a "+
 | |
|                  r->recName+". Skipping file.");
 | |
|         return meshes;
 | |
|     }
 | |
| 
 | |
|     NIFSkeletonLoader skelldr;
 | |
|     bool hasSkel = skelldr.createSkeleton(skelName, group, textkeys, node);
 | |
| 
 | |
|     NIFMeshLoader meshldr(name, group, (hasSkel ? skelName : std::string()));
 | |
|     meshldr.createMeshes(node, meshes);
 | |
| 
 | |
|     return meshes;
 | |
| }
 | |
| 
 | |
| EntityList NIFLoader::createEntities(Ogre::SceneNode *parent, TextKeyMap *textkeys, const std::string &name, const std::string &group)
 | |
| {
 | |
|     EntityList entitylist;
 | |
| 
 | |
|     MeshPairList meshes = load(name, name, textkeys, 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()));
 | |
|         Ogre::Entity *entity = entitylist.mEntities.back();
 | |
|         if(!entitylist.mSkelBase && entity->hasSkeleton())
 | |
|             entitylist.mSkelBase = entity;
 | |
|     }
 | |
| 
 | |
|     if(entitylist.mSkelBase)
 | |
|     {
 | |
|         parent->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);
 | |
|             }
 | |
|             else if(entity != entitylist.mSkelBase)
 | |
|                 entitylist.mSkelBase->attachObjectToBone(meshes[i].second, entity);
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         for(size_t i = 0;i < entitylist.mEntities.size();i++)
 | |
|             parent->attachObject(entitylist.mEntities[i]);
 | |
|     }
 | |
| 
 | |
|     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,
 | |
|                                      const std::string &group)
 | |
| {
 | |
|     EntityList entitylist;
 | |
| 
 | |
|     MeshPairList meshes = load(name, parent->getMesh()->getSkeletonName(), NULL, group);
 | |
|     if(meshes.size() == 0)
 | |
|         return entitylist;
 | |
| 
 | |
|     Ogre::SceneManager *sceneMgr = parentNode->getCreator();
 | |
|     std::string filter = "Tri "+bonename;
 | |
|     for(size_t i = 0;i < meshes.size();i++)
 | |
|     {
 | |
|         Ogre::Entity *ent = sceneMgr->createEntity(meshes[i].first->getName());
 | |
|         if(ent->hasSkeleton())
 | |
|         {
 | |
|             if(meshes[i].second.length() < filter.length() ||
 | |
|                std::mismatch(filter.begin(), filter.end(), meshes[i].second.begin(), checklow()).first != filter.end())
 | |
|             {
 | |
|                 sceneMgr->destroyEntity(ent);
 | |
|                 meshes.erase(meshes.begin()+i);
 | |
|                 i--;
 | |
|                 continue;
 | |
|             }
 | |
|             if(!entitylist.mSkelBase)
 | |
|                 entitylist.mSkelBase = ent;
 | |
|         }
 | |
|         entitylist.mEntities.push_back(ent);
 | |
|     }
 | |
| 
 | |
|     Ogre::Vector3 scale(1.0f);
 | |
|     if(bonename.find("Left") != std::string::npos)
 | |
|         scale.x *= -1.0f;
 | |
| 
 | |
|     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())
 | |
|             {
 | |
|                 entity->shareSkeletonInstanceWith(parent);
 | |
|                 parentNode->attachObject(entity);
 | |
|             }
 | |
|             else if(entity != entitylist.mSkelBase)
 | |
|             {
 | |
|                 Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entity);
 | |
|                 tag->setScale(scale);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         for(size_t i = 0;i < entitylist.mEntities.size();i++)
 | |
|         {
 | |
|             Ogre::TagPoint *tag = parent->attachObjectToBone(bonename, entitylist.mEntities[i]);
 | |
|             tag->setScale(scale);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return entitylist;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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
 | |
|    the OGRE skeletons can't hold more than 256 bones, and some NIFs go
 | |
|    way beyond that. The code might be of use if we implement animated
 | |
|    submeshes like this (the part of the NIF that is animated is
 | |
|    usually much less than the entire file, but the method might still
 | |
|    not be water tight.)
 | |
| 
 | |
| // Insert a raw RGBA image into the texture system.
 | |
| extern "C" void ogre_insertTexture(char* name, uint32_t width, uint32_t height, void *data)
 | |
| {
 | |
|   TexturePtr texture = TextureManager::getSingleton().createManual(
 | |
|       name,         // name
 | |
|       "General",    // group
 | |
|       TEX_TYPE_2D,      // type
 | |
|       width, height,    // width & height
 | |
|       0,                // number of mipmaps
 | |
|       PF_BYTE_RGBA,     // pixel format
 | |
|       TU_DEFAULT);      // usage; should be TU_DYNAMIC_WRITE_ONLY_DISCARDABLE for
 | |
|                         // textures updated very often (e.g. each frame)
 | |
| 
 | |
|   // Get the pixel buffer
 | |
|   HardwarePixelBufferSharedPtr pixelBuffer = texture->getBuffer();
 | |
| 
 | |
|   // Lock the pixel buffer and get a pixel box
 | |
|   pixelBuffer->lock(HardwareBuffer::HBL_NORMAL); // for best performance use HBL_DISCARD!
 | |
|   const PixelBox& pixelBox = pixelBuffer->getCurrentLock();
 | |
| 
 | |
|   void *dest = pixelBox.data;
 | |
| 
 | |
|   // Copy the data
 | |
|   memcpy(dest, data, width*height*4);
 | |
| 
 | |
|   // Unlock the pixel buffer
 | |
|   pixelBuffer->unlock();
 | |
| }
 | |
| 
 | |
| 
 | |
| */
 |