|
|
|
@ -26,11 +26,11 @@
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
#include <libs/mangle/vfs/servers/ogre_vfs.hpp>
|
|
|
|
|
#include "components/nif/nif_file.hpp"
|
|
|
|
|
#include "components/nif/node.hpp"
|
|
|
|
|
#include "components/nif/data.hpp"
|
|
|
|
|
#include "components/nif/property.hpp"
|
|
|
|
|
#include "libs/platform/strings.h"
|
|
|
|
|
#include "../nif/nif_file.hpp"
|
|
|
|
|
#include "../nif/node.hpp"
|
|
|
|
|
#include "../nif/data.hpp"
|
|
|
|
|
#include "../nif/property.hpp"
|
|
|
|
|
#include <libs/platform/strings.h>
|
|
|
|
|
|
|
|
|
|
// For warning messages
|
|
|
|
|
#include <iostream>
|
|
|
|
@ -45,21 +45,27 @@ using namespace Ogre;
|
|
|
|
|
using namespace Nif;
|
|
|
|
|
using namespace Mangle::VFS;
|
|
|
|
|
|
|
|
|
|
// This is the interface to the Ogre resource system. It allows us to
|
|
|
|
|
// load NIFs from BSAs, in the file system and in any other place we
|
|
|
|
|
// tell Ogre to look (eg. in zip or rar files.) It's also used to
|
|
|
|
|
// check for the existence of texture files, so we can exchange the
|
|
|
|
|
// extension from .tga to .dds if the texture is missing.
|
|
|
|
|
static OgreVFS *vfs;
|
|
|
|
|
NIFLoader& NIFLoader::getSingleton()
|
|
|
|
|
{
|
|
|
|
|
static NIFLoader instance;
|
|
|
|
|
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NIFLoader* NIFLoader::getSingletonPtr()
|
|
|
|
|
{
|
|
|
|
|
return &getSingleton();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Singleton instance used by load()
|
|
|
|
|
static NIFLoader g_sing;
|
|
|
|
|
void NIFLoader::warn(string msg)
|
|
|
|
|
{
|
|
|
|
|
std::cerr << "NIFLoader: Warn:" << msg << "\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Makeshift error reporting system
|
|
|
|
|
static string errName;
|
|
|
|
|
static void warn(const string &msg)
|
|
|
|
|
void NIFLoader::fail(string msg)
|
|
|
|
|
{
|
|
|
|
|
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
|
|
|
|
|
std::cerr << "NIFLoader: Fail: "<< msg << std::endl;
|
|
|
|
|
assert(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper class that computes the bounding box and of a mesh
|
|
|
|
@ -77,8 +83,8 @@ class BoundsFinder
|
|
|
|
|
|
|
|
|
|
void add(float f)
|
|
|
|
|
{
|
|
|
|
|
if(f > max) max = f;
|
|
|
|
|
if(f < min) min = f;
|
|
|
|
|
if (f > max) max = f;
|
|
|
|
|
if (f < min) min = f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return Max(max**2, min**2)
|
|
|
|
@ -86,7 +92,7 @@ class BoundsFinder
|
|
|
|
|
{
|
|
|
|
|
float m1 = max*max;
|
|
|
|
|
float m2 = min*min;
|
|
|
|
|
if(m1 >= m2) return m1;
|
|
|
|
|
if (m1 >= m2) return m1;
|
|
|
|
|
return m2;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
@ -99,7 +105,7 @@ public:
|
|
|
|
|
// point.
|
|
|
|
|
void add(float *data, int verts)
|
|
|
|
|
{
|
|
|
|
|
for(int i=0;i<verts;i++)
|
|
|
|
|
for (int i=0;i<verts;i++)
|
|
|
|
|
{
|
|
|
|
|
X.add(*(data++));
|
|
|
|
|
Y.add(*(data++));
|
|
|
|
@ -126,12 +132,24 @@ public:
|
|
|
|
|
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; }
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Conversion of blend / test mode from NIF -> OGRE.
|
|
|
|
@ -177,7 +195,7 @@ static CompareFunction getTestMode(int mode)
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void createMaterial(const String &name,
|
|
|
|
|
void NIFLoader::createMaterial(const String &name,
|
|
|
|
|
const Vector &ambient,
|
|
|
|
|
const Vector &diffuse,
|
|
|
|
|
const Vector &specular,
|
|
|
|
@ -186,17 +204,18 @@ static void createMaterial(const String &name,
|
|
|
|
|
float alphaFlags, float alphaTest,
|
|
|
|
|
const String &texName)
|
|
|
|
|
{
|
|
|
|
|
MaterialPtr material = MaterialManager::getSingleton().create(name, "General");
|
|
|
|
|
MaterialPtr material = MaterialManager::getSingleton().create(name, resourceGroup);
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
if(!texName.empty())
|
|
|
|
|
if (!texName.empty())
|
|
|
|
|
{
|
|
|
|
|
Pass *pass = material->getTechnique(0)->getPass(0);
|
|
|
|
|
/*TextureUnitState *txt =*/ pass->createTextureUnitState(texName);
|
|
|
|
|
/*TextureUnitState *txt =*/
|
|
|
|
|
pass->createTextureUnitState(texName);
|
|
|
|
|
|
|
|
|
|
/* As of yet UNTESTED code from Chris:
|
|
|
|
|
pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
|
|
|
|
@ -226,12 +245,12 @@ static void createMaterial(const String &name,
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Add transparency if NiAlphaProperty was present
|
|
|
|
|
if(alphaFlags != -1)
|
|
|
|
|
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)
|
|
|
|
|
if (alphaFlags == 237)
|
|
|
|
|
{
|
|
|
|
|
// Enable transparency
|
|
|
|
|
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
|
|
|
@ -254,14 +273,14 @@ static void createMaterial(const String &name,
|
|
|
|
|
|
|
|
|
|
// Takes a name and adds a unique part to it. This is just used to
|
|
|
|
|
// make sure that all materials are given unique names.
|
|
|
|
|
static String getUniqueName(const String &input)
|
|
|
|
|
String NIFLoader::getUniqueName(const String &input)
|
|
|
|
|
{
|
|
|
|
|
static int addon = 0;
|
|
|
|
|
static char buf[8];
|
|
|
|
|
snprintf(buf, 8, "_%d", addon++);
|
|
|
|
|
|
|
|
|
|
// Don't overflow the buffer
|
|
|
|
|
if(addon > 999999) addon = 0;
|
|
|
|
|
if (addon > 999999) addon = 0;
|
|
|
|
|
|
|
|
|
|
return input + buf;
|
|
|
|
|
}
|
|
|
|
@ -270,13 +289,13 @@ static String getUniqueName(const String &input)
|
|
|
|
|
// does not, change the string IN PLACE to say .dds instead and try
|
|
|
|
|
// that. The texture may still not exist, but no information of value
|
|
|
|
|
// is lost in that case.
|
|
|
|
|
static void findRealTexture(String &texName)
|
|
|
|
|
void NIFLoader::findRealTexture(String &texName)
|
|
|
|
|
{
|
|
|
|
|
assert(vfs);
|
|
|
|
|
if(vfs->isFile(texName)) return;
|
|
|
|
|
if (vfs->isFile(texName)) return;
|
|
|
|
|
|
|
|
|
|
int len = texName.size();
|
|
|
|
|
if(len < 4) return;
|
|
|
|
|
if (len < 4) return;
|
|
|
|
|
|
|
|
|
|
// Change texture extension to .dds
|
|
|
|
|
texName[len-3] = 'd';
|
|
|
|
@ -286,7 +305,7 @@ static void findRealTexture(String &texName)
|
|
|
|
|
|
|
|
|
|
// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given
|
|
|
|
|
// mesh.
|
|
|
|
|
static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material)
|
|
|
|
|
void NIFLoader::createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material)
|
|
|
|
|
{
|
|
|
|
|
NiTriShapeData *data = shape->data.getPtr();
|
|
|
|
|
SubMesh *sub = mesh->createSubMesh(shape->name.toString());
|
|
|
|
@ -312,7 +331,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
bind->setBinding(nextBuf++, vbuf);
|
|
|
|
|
|
|
|
|
|
// Vertex normals
|
|
|
|
|
if(data->normals.length)
|
|
|
|
|
if (data->normals.length)
|
|
|
|
|
{
|
|
|
|
|
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL);
|
|
|
|
|
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
|
|
|
|
@ -323,13 +342,13 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vertex colors
|
|
|
|
|
if(data->colors.length)
|
|
|
|
|
if (data->colors.length)
|
|
|
|
|
{
|
|
|
|
|
const float *colors = data->colors.ptr;
|
|
|
|
|
RenderSystem* rs = Root::getSingleton().getRenderSystem();
|
|
|
|
|
std::vector<RGBA> colorsRGB(numVerts);
|
|
|
|
|
RGBA *pColour = &colorsRGB.front();
|
|
|
|
|
for(int i=0; i<numVerts; i++)
|
|
|
|
|
for (int i=0; i<numVerts; i++)
|
|
|
|
|
{
|
|
|
|
|
rs->convertColourValue(ColourValue(colors[0],colors[1],colors[2],
|
|
|
|
|
colors[3]),pColour++);
|
|
|
|
@ -344,7 +363,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Texture UV coordinates
|
|
|
|
|
if(data->uvlist.length)
|
|
|
|
|
if (data->uvlist.length)
|
|
|
|
|
{
|
|
|
|
|
decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);
|
|
|
|
|
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
|
|
|
|
@ -357,7 +376,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
|
|
|
|
|
// Triangle faces
|
|
|
|
|
int numFaces = data->triangles.length;
|
|
|
|
|
if(numFaces)
|
|
|
|
|
if (numFaces)
|
|
|
|
|
{
|
|
|
|
|
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
|
|
|
|
|
createIndexBuffer(HardwareIndexBuffer::IT_16BIT,
|
|
|
|
@ -370,11 +389,12 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set material if one was given
|
|
|
|
|
if(!material.empty()) sub->setMaterialName(material);
|
|
|
|
|
if (!material.empty()) sub->setMaterialName(material);
|
|
|
|
|
|
|
|
|
|
/* Old commented D code. Might be useful when reimplementing
|
|
|
|
|
animation.
|
|
|
|
|
// Assign this submesh to the given bone
|
|
|
|
|
//TODO: Must use niskininstance!
|
|
|
|
|
/*if (bone)
|
|
|
|
|
{
|
|
|
|
|
VertexBoneAssignment v;
|
|
|
|
|
v.boneIndex = ((Bone*)bone)->getHandle();
|
|
|
|
|
v.weight = 1.0;
|
|
|
|
@ -386,7 +406,8 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
v.vertexIndex = i;
|
|
|
|
|
sub->addBoneAssignment(v);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper math functions. Reinventing linear algebra for the win!
|
|
|
|
@ -394,7 +415,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
|
|
|
|
|
// Computes B = AxB (matrix*matrix)
|
|
|
|
|
static void matrixMul(const Matrix &A, Matrix &B)
|
|
|
|
|
{
|
|
|
|
|
for(int i=0;i<3;i++)
|
|
|
|
|
for (int i=0;i<3;i++)
|
|
|
|
|
{
|
|
|
|
|
float a = B.v[0].array[i];
|
|
|
|
|
float b = B.v[1].array[i];
|
|
|
|
@ -415,7 +436,7 @@ static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale
|
|
|
|
|
float c = C[2];
|
|
|
|
|
|
|
|
|
|
// Perform matrix multiplication, scaling and addition
|
|
|
|
|
for(int i=0;i<3;i++)
|
|
|
|
|
for (int i=0;i<3;i++)
|
|
|
|
|
C[i] = B.array[i] + (a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2])*scale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -428,11 +449,11 @@ static void vectorMul(const Matrix &A, float *C)
|
|
|
|
|
float c = C[2];
|
|
|
|
|
|
|
|
|
|
// Perform matrix multiplication, scaling and addition
|
|
|
|
|
for(int i=0;i<3;i++)
|
|
|
|
|
for (int i=0;i<3;i++)
|
|
|
|
|
C[i] = a*A.v[i].array[0] + b*A.v[i].array[1] + c*A.v[i].array[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds)
|
|
|
|
|
void NIFLoader::handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds)
|
|
|
|
|
{
|
|
|
|
|
assert(shape != NULL);
|
|
|
|
|
|
|
|
|
@ -442,7 +463,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision
|
|
|
|
|
|
|
|
|
|
// Bounding box collision isn't implemented, always use mesh for now.
|
|
|
|
|
if(bbcollide)
|
|
|
|
|
if (bbcollide)
|
|
|
|
|
{
|
|
|
|
|
collide = true;
|
|
|
|
|
bbcollide = false;
|
|
|
|
@ -450,10 +471,13 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
|
|
|
|
|
// If the object was marked "NCO" earlier, it shouldn't collide with
|
|
|
|
|
// anything.
|
|
|
|
|
if(flags & 0x800)
|
|
|
|
|
{ collide = false; bbcollide = false; }
|
|
|
|
|
if (flags & 0x800)
|
|
|
|
|
{
|
|
|
|
|
collide = false;
|
|
|
|
|
bbcollide = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!collide && !bbcollide && hidden)
|
|
|
|
|
if (!collide && !bbcollide && hidden)
|
|
|
|
|
// This mesh apparently isn't being used for anything, so don't
|
|
|
|
|
// bother setting it up.
|
|
|
|
|
return;
|
|
|
|
@ -462,7 +486,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
String material;
|
|
|
|
|
|
|
|
|
|
// Skip the entire material phase for hidden nodes
|
|
|
|
|
if(!hidden)
|
|
|
|
|
if (!hidden)
|
|
|
|
|
{
|
|
|
|
|
// These are set below if present
|
|
|
|
|
NiTexturingProperty *t = NULL;
|
|
|
|
@ -472,27 +496,27 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
// Scan the property list for material information
|
|
|
|
|
PropertyList &list = shape->props;
|
|
|
|
|
int n = list.length();
|
|
|
|
|
for(int i=0; i<n; i++)
|
|
|
|
|
for (int i=0; i<n; i++)
|
|
|
|
|
{
|
|
|
|
|
// Entries may be empty
|
|
|
|
|
if(!list.has(i)) continue;
|
|
|
|
|
if (!list.has(i)) continue;
|
|
|
|
|
|
|
|
|
|
Property *pr = &list[i];
|
|
|
|
|
|
|
|
|
|
if(pr->recType == RC_NiTexturingProperty)
|
|
|
|
|
if (pr->recType == RC_NiTexturingProperty)
|
|
|
|
|
t = (NiTexturingProperty*)pr;
|
|
|
|
|
else if(pr->recType == RC_NiMaterialProperty)
|
|
|
|
|
else if (pr->recType == RC_NiMaterialProperty)
|
|
|
|
|
m = (NiMaterialProperty*)pr;
|
|
|
|
|
else if(pr->recType == RC_NiAlphaProperty)
|
|
|
|
|
else if (pr->recType == RC_NiAlphaProperty)
|
|
|
|
|
a = (NiAlphaProperty*)pr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Texture
|
|
|
|
|
String texName;
|
|
|
|
|
if(t && t->textures[0].inUse)
|
|
|
|
|
if (t && t->textures[0].inUse)
|
|
|
|
|
{
|
|
|
|
|
NiSourceTexture *st = t->textures[0].texture.getPtr();
|
|
|
|
|
if(st->external)
|
|
|
|
|
if (st->external)
|
|
|
|
|
{
|
|
|
|
|
SString tname = st->filename;
|
|
|
|
|
|
|
|
|
@ -518,14 +542,14 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
// Alpha modifiers
|
|
|
|
|
int alphaFlags = -1;
|
|
|
|
|
ubyte alphaTest = 0;
|
|
|
|
|
if(a)
|
|
|
|
|
if (a)
|
|
|
|
|
{
|
|
|
|
|
alphaFlags = a->flags;
|
|
|
|
|
alphaTest = a->data->threshold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Material
|
|
|
|
|
if(m || !texName.empty())
|
|
|
|
|
if (m || !texName.empty())
|
|
|
|
|
{
|
|
|
|
|
// If we're here, then this mesh has a material. Thus we
|
|
|
|
|
// need to calculate a snappy material name. It should
|
|
|
|
@ -533,7 +557,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
// be unique. One mesh may use many materials.
|
|
|
|
|
material = getUniqueName(mesh->getName());
|
|
|
|
|
|
|
|
|
|
if(m)
|
|
|
|
|
if (m)
|
|
|
|
|
{
|
|
|
|
|
// Use NiMaterialProperty data to create the data
|
|
|
|
|
const S_MaterialProperty *d = m->data;
|
|
|
|
@ -545,7 +569,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
// We only have a texture name. Create a default
|
|
|
|
|
// material for it.
|
|
|
|
|
Vector zero, one;
|
|
|
|
|
for(int i=0; i<3;i++)
|
|
|
|
|
for (int i=0; i<3;i++)
|
|
|
|
|
{
|
|
|
|
|
zero.array[i] = 0.0;
|
|
|
|
|
one.array[i] = 1.0;
|
|
|
|
@ -573,24 +597,24 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
const Matrix &rot = shape->trafo->rotation;
|
|
|
|
|
const Vector &pos = shape->trafo->pos;
|
|
|
|
|
float scale = shape->trafo->scale;
|
|
|
|
|
for(int i=0; i<numVerts; i++)
|
|
|
|
|
for (int i=0; i<numVerts; i++)
|
|
|
|
|
{
|
|
|
|
|
vectorMulAdd(rot, pos, ptr, scale);
|
|
|
|
|
ptr += 3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remember to rotate all the vertex normals as well
|
|
|
|
|
if(data->normals.length)
|
|
|
|
|
if (data->normals.length)
|
|
|
|
|
{
|
|
|
|
|
ptr = (float*)data->normals.ptr;
|
|
|
|
|
for(int i=0; i<numVerts; i++)
|
|
|
|
|
for (int i=0; i<numVerts; i++)
|
|
|
|
|
{
|
|
|
|
|
vectorMul(rot, ptr);
|
|
|
|
|
ptr += 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!hidden)
|
|
|
|
|
if (!hidden)
|
|
|
|
|
{
|
|
|
|
|
// Add this vertex set to the bounding box
|
|
|
|
|
bounds.add(optr, numVerts);
|
|
|
|
@ -600,7 +624,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
|
|
|
|
void NIFLoader::handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
|
|
|
|
const Transformation *trafo, BoundsFinder &bounds)
|
|
|
|
|
{
|
|
|
|
|
// Accumulate the flags from all the child nodes. This works for all
|
|
|
|
@ -609,22 +633,22 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
|
|
|
|
|
|
|
|
|
// Check for extra data
|
|
|
|
|
Extra *e = node;
|
|
|
|
|
while(!e->extra.empty())
|
|
|
|
|
while (!e->extra.empty())
|
|
|
|
|
{
|
|
|
|
|
// Get the next extra data in the list
|
|
|
|
|
e = e->extra.getPtr();
|
|
|
|
|
assert(e != NULL);
|
|
|
|
|
|
|
|
|
|
if(e->recType == RC_NiStringExtraData)
|
|
|
|
|
if (e->recType == RC_NiStringExtraData)
|
|
|
|
|
{
|
|
|
|
|
// String markers may contain important information
|
|
|
|
|
// affecting the entire subtree of this node
|
|
|
|
|
NiStringExtraData *sd = (NiStringExtraData*)e;
|
|
|
|
|
|
|
|
|
|
if(sd->string == "NCO")
|
|
|
|
|
if (sd->string == "NCO")
|
|
|
|
|
// No collision. Use an internal flag setting to mark this.
|
|
|
|
|
flags |= 0x800;
|
|
|
|
|
else if(sd->string == "MRK")
|
|
|
|
|
else if (sd->string == "MRK")
|
|
|
|
|
// Marker objects. These are only visible in the
|
|
|
|
|
// editor. Until and unless we add an editor component to
|
|
|
|
|
// the engine, just skip this entire node.
|
|
|
|
@ -634,7 +658,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
|
|
|
|
|
|
|
|
|
// Apply the parent transformation to this node. We overwrite the
|
|
|
|
|
// existing data with the final transformation.
|
|
|
|
|
if(trafo)
|
|
|
|
|
if (trafo)
|
|
|
|
|
{
|
|
|
|
|
// Get a non-const reference to the node's data, since we're
|
|
|
|
|
// overwriting it. TODO: Is this necessary?
|
|
|
|
@ -654,17 +678,19 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For NiNodes, loop through children
|
|
|
|
|
if(node->recType == RC_NiNode)
|
|
|
|
|
if (node->recType == RC_NiNode)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NodeList &list = ((NiNode*)node)->children;
|
|
|
|
|
int n = list.length();
|
|
|
|
|
for(int i=0; i<n; i++)
|
|
|
|
|
for (int i=0; i<n; i++)
|
|
|
|
|
{
|
|
|
|
|
if(list.has(i))
|
|
|
|
|
if (list.has(i))
|
|
|
|
|
handleNode(mesh, &list[i], flags, node->trafo, bounds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(node->recType == RC_NiTriShape)
|
|
|
|
|
else if (node->recType == RC_NiTriShape)
|
|
|
|
|
// For shapes
|
|
|
|
|
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
|
|
|
|
|
}
|
|
|
|
@ -672,7 +698,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
|
|
|
|
|
void NIFLoader::loadResource(Resource *resource)
|
|
|
|
|
{
|
|
|
|
|
// Set up the VFS if it hasn't been done already
|
|
|
|
|
if(!vfs) vfs = new OgreVFS("General");
|
|
|
|
|
if (!vfs) vfs = new OgreVFS(resourceGroup);
|
|
|
|
|
|
|
|
|
|
// Get the mesh
|
|
|
|
|
Mesh *mesh = dynamic_cast<Mesh*>(resource);
|
|
|
|
@ -680,8 +706,8 @@ void NIFLoader::loadResource(Resource *resource)
|
|
|
|
|
|
|
|
|
|
// Look it up
|
|
|
|
|
const String &name = mesh->getName();
|
|
|
|
|
errName = name; // Set name for error messages
|
|
|
|
|
if(!vfs->isFile(name))
|
|
|
|
|
|
|
|
|
|
if (!vfs->isFile(name))
|
|
|
|
|
{
|
|
|
|
|
warn("File not found.");
|
|
|
|
|
return;
|
|
|
|
@ -696,7 +722,7 @@ void NIFLoader::loadResource(Resource *resource)
|
|
|
|
|
// likely a sign of incomplete code rather than faulty input.
|
|
|
|
|
NIFFile nif(vfs->open(name), name);
|
|
|
|
|
|
|
|
|
|
if(nif.numRecords() < 1)
|
|
|
|
|
if (nif.numRecords() < 1)
|
|
|
|
|
{
|
|
|
|
|
warn("Found no records in NIF.");
|
|
|
|
|
return;
|
|
|
|
@ -708,7 +734,7 @@ void NIFLoader::loadResource(Resource *resource)
|
|
|
|
|
|
|
|
|
|
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
|
|
|
|
|
|
|
|
|
|
if(node == NULL)
|
|
|
|
|
if (node == NULL)
|
|
|
|
|
{
|
|
|
|
|
warn("First record in file was not a node, but a " +
|
|
|
|
|
r->recName.toString() + ". Skipping file.");
|
|
|
|
@ -719,7 +745,7 @@ void NIFLoader::loadResource(Resource *resource)
|
|
|
|
|
handleNode(mesh, node, 0, NULL, bounds);
|
|
|
|
|
|
|
|
|
|
// Finally, set the bounding value.
|
|
|
|
|
if(bounds.isValid())
|
|
|
|
|
if (bounds.isValid())
|
|
|
|
|
{
|
|
|
|
|
mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(),
|
|
|
|
|
bounds.maxX(), bounds.maxY(), bounds.maxZ()));
|
|
|
|
@ -734,11 +760,11 @@ MeshPtr NIFLoader::load(const std::string &name,
|
|
|
|
|
|
|
|
|
|
// Check if the resource already exists
|
|
|
|
|
ResourcePtr ptr = m->getByName(name, group);
|
|
|
|
|
if(!ptr.isNull())
|
|
|
|
|
if (!ptr.isNull())
|
|
|
|
|
return MeshPtr(ptr);
|
|
|
|
|
|
|
|
|
|
// Nope, create a new one.
|
|
|
|
|
return MeshManager::getSingleton().createManual(name, group, &g_sing);
|
|
|
|
|
return MeshManager::getSingleton().createManual(name, group, NIFLoader::getSingletonPtr());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* More code currently not in use, from the old D source. This was
|
|
|
|
|