2010-01-12 13:46:44 +00:00
|
|
|
/*
|
|
|
|
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/ .
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2010-12-13 03:05:47 +00:00
|
|
|
//loadResource->handleNode->handleNiTriShape->createSubMesh
|
|
|
|
|
2010-06-03 18:44:55 +00:00
|
|
|
#include "ogre_nif_loader.hpp"
|
2011-11-23 23:18:51 +00:00
|
|
|
|
2012-07-03 13:32:38 +00:00
|
|
|
#include <OgreMaterialManager.h>
|
|
|
|
#include <OgreMeshManager.h>
|
|
|
|
#include <OgreHardwareBufferManager.h>
|
|
|
|
#include <OgreSkeletonManager.h>
|
|
|
|
#include <OgreTechnique.h>
|
|
|
|
#include <OgreSubMesh.h>
|
|
|
|
#include <OgreRoot.h>
|
|
|
|
|
2012-04-01 14:59:35 +00:00
|
|
|
#include <components/settings/settings.hpp>
|
2012-04-11 18:13:15 +00:00
|
|
|
#include <components/nifoverrides/nifoverrides.hpp>
|
2010-06-22 07:49:44 +00:00
|
|
|
|
2010-01-15 19:34:31 +00:00
|
|
|
typedef unsigned char ubyte;
|
|
|
|
|
2010-01-12 13:46:44 +00:00
|
|
|
using namespace std;
|
|
|
|
using namespace Nif;
|
2011-06-19 17:14:14 +00:00
|
|
|
using namespace NifOgre;
|
2010-01-12 13:46:44 +00:00
|
|
|
|
2010-08-16 17:31:59 +00:00
|
|
|
|
2010-06-22 07:49:44 +00:00
|
|
|
// Helper class that computes the bounding box and of a mesh
|
|
|
|
class BoundsFinder
|
|
|
|
{
|
2010-08-08 15:20:55 +00:00
|
|
|
struct MaxMinFinder
|
|
|
|
{
|
|
|
|
float max, min;
|
2010-06-22 07:49:44 +00:00
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
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)
|
2010-06-22 07:49:44 +00:00
|
|
|
{
|
2010-08-08 15:20:55 +00:00
|
|
|
for (int i=0;i<verts;i++)
|
|
|
|
{
|
|
|
|
X.add(*(data++));
|
|
|
|
Y.add(*(data++));
|
|
|
|
Z.add(*(data++));
|
|
|
|
}
|
2010-06-22 07:49:44 +00:00
|
|
|
}
|
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
// True if this structure has valid values
|
|
|
|
bool isValid()
|
2010-06-22 07:49:44 +00:00
|
|
|
{
|
2010-08-08 15:20:55 +00:00
|
|
|
return
|
|
|
|
minX() <= maxX() &&
|
|
|
|
minY() <= maxY() &&
|
|
|
|
minZ() <= maxZ();
|
2010-06-22 07:49:44 +00:00
|
|
|
}
|
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
// Compute radius
|
|
|
|
float getRadius()
|
2010-06-22 07:49:44 +00:00
|
|
|
{
|
2010-08-08 15:20:55 +00:00
|
|
|
assert(isValid());
|
2010-06-22 07:49:44 +00:00
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
// The radius is computed from the origin, not from the geometric
|
|
|
|
// center of the mesh.
|
|
|
|
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
|
|
|
|
}
|
2010-06-22 07:49:44 +00:00
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
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;
|
|
|
|
}
|
2010-06-22 07:49:44 +00:00
|
|
|
};
|
|
|
|
|
2012-07-13 03:12:18 +00:00
|
|
|
|
2012-07-15 18:11:49 +00:00
|
|
|
struct 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();
|
|
|
|
}
|
|
|
|
|
|
|
|
void loadResource(Ogre::Resource *resource)
|
|
|
|
{
|
|
|
|
warn("Found no records in NIF for "+resource->getName());
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool createSkeleton(const std::string &name, const std::string &group, Nif::Node *node, Ogre::SkeletonPtr *skel)
|
|
|
|
{
|
|
|
|
if(node->boneTrafo != NULL)
|
|
|
|
{
|
|
|
|
Ogre::SkeletonManager &skelMgr = Ogre::SkeletonManager::getSingleton();
|
|
|
|
|
|
|
|
Ogre::SkeletonPtr tmp = skelMgr.getByName(name);
|
|
|
|
if(tmp.isNull())
|
|
|
|
{
|
|
|
|
static NIFSkeletonLoader loader;
|
|
|
|
tmp = skelMgr.create(name, group, true, &loader);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(skel) *skel = tmp;
|
|
|
|
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(createSkeleton(name, group, children[i].getPtr(), skel))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-06-25 13:23:21 +00:00
|
|
|
// Conversion of blend / test mode from NIF -> OGRE.
|
2010-08-21 17:40:08 +00:00
|
|
|
// Not in use yet, so let's comment it out.
|
|
|
|
/*
|
2010-01-29 13:42:59 +00:00
|
|
|
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;
|
2010-06-25 13:23:21 +00:00
|
|
|
// [Comment from Chris Robinson:] Can't handle this mode? :/
|
|
|
|
// case 10: return SBF_SOURCE_ALPHA_SATURATE;
|
2010-01-29 13:42:59 +00:00
|
|
|
default:
|
|
|
|
return SBF_SOURCE_ALPHA;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-21 17:40:08 +00:00
|
|
|
|
|
|
|
// This is also unused
|
2010-01-29 13:42:59 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2010-06-25 13:23:21 +00:00
|
|
|
*/
|
2010-01-29 13:42:59 +00:00
|
|
|
|
2012-07-13 03:12:18 +00:00
|
|
|
#if 0
|
2012-07-10 09:38:35 +00:00
|
|
|
void NIFLoader::createMaterial(const Ogre::String &name,
|
|
|
|
const Ogre::Vector3 &ambient,
|
|
|
|
const Ogre::Vector3 &diffuse,
|
|
|
|
const Ogre::Vector3 &specular,
|
|
|
|
const Ogre::Vector3 &emissive,
|
2010-01-14 11:50:13 +00:00
|
|
|
float glossiness, float alpha,
|
2010-08-21 17:40:08 +00:00
|
|
|
int alphaFlags, float alphaTest,
|
2012-07-10 09:38:35 +00:00
|
|
|
const Ogre::String &texName)
|
2010-01-14 11:50:13 +00:00
|
|
|
{
|
2012-07-10 09:38:35 +00:00
|
|
|
Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton().create(name, resourceGroup);
|
2010-08-08 15:20:55 +00:00
|
|
|
|
2012-01-06 02:45:17 +00:00
|
|
|
|
|
|
|
// This assigns the texture to this material. If the texture name is
|
|
|
|
// a file name, and this file exists (in a resource directory), it
|
|
|
|
// will automatically be loaded when needed. If not (such as for
|
|
|
|
// internal NIF textures that we might support later), we should
|
|
|
|
// already have inserted a manual loader for the texture.
|
2012-01-17 14:10:53 +00:00
|
|
|
|
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
if (!texName.empty())
|
2010-01-14 11:50:13 +00:00
|
|
|
{
|
2012-07-10 09:38:35 +00:00
|
|
|
Ogre::Pass *pass = material->getTechnique(0)->getPass(0);
|
2010-08-08 15:20:55 +00:00
|
|
|
/*TextureUnitState *txt =*/
|
|
|
|
pass->createTextureUnitState(texName);
|
|
|
|
|
2012-07-10 09:38:35 +00:00
|
|
|
pass->setVertexColourTracking(Ogre::TVC_DIFFUSE);
|
2012-03-05 17:50:56 +00:00
|
|
|
|
2010-08-21 17:40:08 +00:00
|
|
|
// As of yet UNTESTED code from Chris:
|
|
|
|
/*pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
|
2010-08-08 15:20:55 +00:00
|
|
|
pass->setDepthFunction(Ogre::CMPF_LESS_EQUAL);
|
|
|
|
pass->setDepthCheckEnabled(true);
|
|
|
|
|
|
|
|
// Add transparency if NiAlphaProperty was present
|
2010-08-21 17:40:08 +00:00
|
|
|
if (alphaFlags != -1)
|
|
|
|
{
|
|
|
|
std::cout << "Alpha flags set!" << endl;
|
|
|
|
if ((alphaFlags&1))
|
|
|
|
{
|
2010-08-08 15:20:55 +00:00
|
|
|
pass->setDepthWriteEnabled(false);
|
|
|
|
pass->setSceneBlending(getBlendFactor((alphaFlags>>1)&0xf),
|
|
|
|
getBlendFactor((alphaFlags>>5)&0xf));
|
2010-08-21 17:40:08 +00:00
|
|
|
}
|
2010-08-08 15:20:55 +00:00
|
|
|
else
|
2010-08-21 17:40:08 +00:00
|
|
|
pass->setDepthWriteEnabled(true);
|
2010-08-08 15:20:55 +00:00
|
|
|
|
2010-08-21 17:40:08 +00:00
|
|
|
if ((alphaFlags>>9)&1)
|
|
|
|
pass->setAlphaRejectSettings(getTestMode((alphaFlags>>10)&0x7),
|
|
|
|
alphaTest);
|
2010-08-08 15:20:55 +00:00
|
|
|
|
|
|
|
pass->setTransparentSortingEnabled(!((alphaFlags>>13)&1));
|
2010-08-21 17:40:08 +00:00
|
|
|
}
|
2010-08-08 15:20:55 +00:00
|
|
|
else
|
2010-08-21 17:40:08 +00:00
|
|
|
pass->setDepthWriteEnabled(true); */
|
2011-06-07 22:57:24 +00:00
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
|
|
|
|
// Add transparency if NiAlphaProperty was present
|
|
|
|
if (alphaFlags != -1)
|
2010-01-14 11:50:13 +00:00
|
|
|
{
|
2010-08-08 15:20:55 +00:00
|
|
|
// 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)
|
2010-01-14 11:50:13 +00:00
|
|
|
{
|
2012-04-11 18:13:15 +00:00
|
|
|
NifOverrides::TransparencyResult result = NifOverrides::Overrides::getTransparencyOverride(texName);
|
|
|
|
if (result.first)
|
|
|
|
{
|
2012-07-10 09:38:35 +00:00
|
|
|
pass->setAlphaRejectFunction(Ogre::CMPF_GREATER_EQUAL);
|
2012-04-11 18:13:15 +00:00
|
|
|
pass->setAlphaRejectValue(result.second);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Enable transparency
|
2012-07-10 09:38:35 +00:00
|
|
|
pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
|
2012-04-11 18:13:15 +00:00
|
|
|
|
|
|
|
//pass->setDepthCheckEnabled(false);
|
|
|
|
pass->setDepthWriteEnabled(false);
|
2012-04-12 14:33:36 +00:00
|
|
|
//std::cout << "alpha 237; material: " << name << " texName: " << texName << std::endl;
|
2012-04-11 18:13:15 +00:00
|
|
|
}
|
2010-01-14 11:50:13 +00:00
|
|
|
}
|
2010-08-08 15:20:55 +00:00
|
|
|
else
|
|
|
|
warn("Unhandled alpha setting for texture " + texName);
|
2010-01-14 11:50:13 +00:00
|
|
|
}
|
2012-04-11 16:53:13 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
material->getTechnique(0)->setShadowCasterMaterial("depth_shadow_caster_noalpha");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Settings::Manager::getBool("enabled", "Shadows"))
|
|
|
|
{
|
|
|
|
bool split = Settings::Manager::getBool("split", "Shadows");
|
|
|
|
const int numsplits = 3;
|
2012-07-13 03:12:18 +00:00
|
|
|
for (int i = 0; i < (split ? numsplits : 1); ++i)
|
|
|
|
{
|
2012-07-10 09:38:35 +00:00
|
|
|
Ogre::TextureUnitState* tu = material->getTechnique(0)->getPass(0)->createTextureUnitState();
|
|
|
|
tu->setName("shadowMap" + Ogre::StringConverter::toString(i));
|
|
|
|
tu->setContentType(Ogre::TextureUnitState::CONTENT_SHADOW);
|
|
|
|
tu->setTextureAddressingMode(Ogre::TextureUnitState::TAM_BORDER);
|
|
|
|
tu->setTextureBorderColour(Ogre::ColourValue::White);
|
2012-07-13 03:12:18 +00:00
|
|
|
}
|
2012-04-11 16:53:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (Settings::Manager::getBool("shaders", "Objects"))
|
|
|
|
{
|
|
|
|
material->getTechnique(0)->getPass(0)->setVertexProgram("main_vp");
|
|
|
|
material->getTechnique(0)->getPass(0)->setFragmentProgram("main_fp");
|
2012-05-13 10:19:49 +00:00
|
|
|
|
|
|
|
material->getTechnique(0)->getPass(0)->setFog(true); // force-disable fixed function fog, it is calculated in shader
|
2012-04-11 16:53:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a fallback technique without shadows and without mrt
|
2012-07-10 09:38:35 +00:00
|
|
|
Ogre::Technique* tech2 = material->createTechnique();
|
2012-04-11 16:53:13 +00:00
|
|
|
tech2->setSchemeName("Fallback");
|
2012-07-10 09:38:35 +00:00
|
|
|
Ogre::Pass* pass2 = tech2->createPass();
|
2012-04-11 16:53:13 +00:00
|
|
|
pass2->createTextureUnitState(texName);
|
2012-07-10 09:38:35 +00:00
|
|
|
pass2->setVertexColourTracking(Ogre::TVC_DIFFUSE);
|
2012-04-11 16:53:13 +00:00
|
|
|
if (Settings::Manager::getBool("shaders", "Objects"))
|
|
|
|
{
|
|
|
|
pass2->setVertexProgram("main_fallback_vp");
|
|
|
|
pass2->setFragmentProgram("main_fallback_fp");
|
2012-05-13 10:19:49 +00:00
|
|
|
pass2->setFog(true); // force-disable fixed function fog, it is calculated in shader
|
2010-01-14 11:50:13 +00:00
|
|
|
}
|
|
|
|
|
2010-08-08 15:20:55 +00:00
|
|
|
// Add material bells and whistles
|
2012-07-10 09:38:35 +00:00
|
|
|
material->setAmbient(ambient[0], ambient[1], ambient[2]);
|
|
|
|
material->setDiffuse(diffuse[0], diffuse[1], diffuse[2], alpha);
|
|
|
|
material->setSpecular(specular[0], specular[1], specular[2], alpha);
|
|
|
|
material->setSelfIllumination(emissive[0], emissive[1], emissive[2]);
|
2010-08-08 15:20:55 +00:00
|
|
|
material->setShininess(glossiness);
|
2010-01-14 11:50:13 +00:00
|
|
|
}
|
2012-07-13 03:12:18 +00:00
|
|
|
#endif
|
2010-01-14 11:50:13 +00:00
|
|
|
|
2010-01-13 14:28:28 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
class NIFMeshLoader : Ogre::ManualResourceLoader
|
2010-01-12 13:46:44 +00:00
|
|
|
{
|
2012-07-15 18:37:36 +00:00
|
|
|
std::string mName;
|
|
|
|
std::string mGroup;
|
|
|
|
std::string mShapeName;
|
|
|
|
bool mHasSkel;
|
2011-11-27 07:27:06 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
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();
|
|
|
|
}
|
2012-07-15 18:11:49 +00:00
|
|
|
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 19:02:47 +00:00
|
|
|
bool findTriShape(Ogre::Mesh *mesh, Nif::Node *node)
|
|
|
|
{
|
|
|
|
if(node->recType == Nif::RC_NiTriShape && mShapeName == node->name)
|
|
|
|
{
|
|
|
|
warn("Not loading shape \""+mShapeName+"\" in "+mName);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
typedef std::map<std::string,NIFMeshLoader,ciLessBoost> LoaderMap;
|
|
|
|
static LoaderMap sLoaders;
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
public:
|
|
|
|
NIFMeshLoader()
|
|
|
|
: mHasSkel(false)
|
|
|
|
{ }
|
|
|
|
NIFMeshLoader(const std::string &name, const std::string &group, bool hasSkel)
|
|
|
|
: mName(name), mGroup(group), mHasSkel(hasSkel)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void loadResource(Ogre::Resource *resource)
|
2012-07-15 13:45:21 +00:00
|
|
|
{
|
2012-07-15 19:02:47 +00:00
|
|
|
Ogre::Mesh *mesh = dynamic_cast<Ogre::Mesh*>(resource);
|
|
|
|
assert(mesh && "Attempting to load a mesh into a non-mesh resource!");
|
|
|
|
|
|
|
|
Nif::NIFFile nif(mName);
|
|
|
|
Nif::Node *node = dynamic_cast<Nif::Node*>(nif.getRecord(0));
|
|
|
|
findTriShape(mesh, node);
|
2012-07-15 18:37:36 +00:00
|
|
|
}
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
void createMeshes(Nif::Node *node, MeshPairList &meshes, int flags=0)
|
|
|
|
{
|
|
|
|
flags |= node->flags;
|
|
|
|
|
|
|
|
// TODO: Check for extra data
|
|
|
|
|
|
|
|
if(node->recType == Nif::RC_NiTriShape)
|
2012-07-15 13:45:21 +00:00
|
|
|
{
|
2012-07-15 18:37:36 +00:00
|
|
|
Ogre::MeshManager &meshMgr = Ogre::MeshManager::getSingleton();
|
|
|
|
std::string fullname = mName+"@"+node->name;
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
Ogre::MeshPtr mesh = meshMgr.getByName(fullname);
|
|
|
|
if(mesh.isNull())
|
|
|
|
{
|
|
|
|
NIFMeshLoader *loader = &sLoaders[fullname];
|
|
|
|
*loader = *this;
|
|
|
|
loader->mShapeName = node->name;
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
mesh = meshMgr.createManual(fullname, mGroup, loader);
|
|
|
|
}
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 18:37:36 +00:00
|
|
|
meshes.push_back(std::make_pair(mesh, (node->parent ? node->parent->name : std::string())));
|
|
|
|
}
|
|
|
|
else if(node->recType != Nif::RC_NiNode && node->recType != Nif::RC_RootCollisionNode &&
|
|
|
|
node->recType != Nif::RC_NiRotatingParticles)
|
|
|
|
warn("Unhandled mesh node type: "+node->recName);
|
|
|
|
|
|
|
|
Nif::NiNode *ninode = dynamic_cast<Nif::NiNode*>(node);
|
|
|
|
if(ninode)
|
2012-07-15 13:45:21 +00:00
|
|
|
{
|
2012-07-15 18:37:36 +00:00
|
|
|
Nif::NodeList &children = ninode->children;
|
|
|
|
for(size_t i = 0;i < children.length();i++)
|
|
|
|
{
|
|
|
|
if(!children[i].empty())
|
|
|
|
createMeshes(children[i].getPtr(), meshes, flags);
|
|
|
|
}
|
2012-07-15 13:45:21 +00:00
|
|
|
}
|
|
|
|
}
|
2012-07-15 18:37:36 +00:00
|
|
|
};
|
|
|
|
NIFMeshLoader::LoaderMap NIFMeshLoader::sLoaders;
|
2012-07-15 13:45:21 +00:00
|
|
|
|
2012-07-15 18:11:49 +00:00
|
|
|
|
2012-07-14 01:25:35 +00:00
|
|
|
MeshPairList NIFLoader::load(const std::string &name, Ogre::SkeletonPtr *skel, const std::string &group)
|
2010-01-12 13:46:44 +00:00
|
|
|
{
|
2012-07-15 13:45:21 +00:00
|
|
|
MeshPairList meshes;
|
2012-07-14 01:25:35 +00:00
|
|
|
|
|
|
|
if(skel != NULL)
|
|
|
|
skel->setNull();
|
2012-01-17 14:10:53 +00:00
|
|
|
|
2012-07-15 13:45:21 +00:00
|
|
|
Nif::NIFFile nif(name);
|
|
|
|
if (nif.numRecords() < 1)
|
2011-01-05 21:18:21 +00:00
|
|
|
{
|
2012-07-15 13:45:21 +00:00
|
|
|
nif.warn("Found no records in NIF.");
|
|
|
|
return meshes;
|
2011-01-05 21:18:21 +00:00
|
|
|
}
|
2012-07-14 01:25:35 +00:00
|
|
|
|
2012-07-15 13:45:21 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2012-07-15 18:11:49 +00:00
|
|
|
bool hasSkel = NIFSkeletonLoader::createSkeleton(name, group, node, skel);
|
2012-07-15 18:37:36 +00:00
|
|
|
|
|
|
|
NIFMeshLoader meshldr(name, group, hasSkel);
|
|
|
|
meshldr.createMeshes(node, meshes);
|
2012-07-15 13:45:21 +00:00
|
|
|
|
|
|
|
return meshes;
|
2010-12-13 03:05:47 +00:00
|
|
|
}
|
2011-06-07 22:57:24 +00:00
|
|
|
|
2010-01-13 18:28:12 +00:00
|
|
|
|
2010-06-22 06:58:09 +00:00
|
|
|
/* 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(
|
2011-01-05 21:18:21 +00:00
|
|
|
name, // name
|
|
|
|
"General", // group
|
|
|
|
TEX_TYPE_2D, // type
|
2010-06-22 06:58:09 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
*/
|