1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-20 07:53:51 +00:00

Merge commit 'ape/master'

This commit is contained in:
Marc Zinnschlag 2010-08-16 20:39:16 +02:00
commit e69a924036
4 changed files with 706 additions and 531 deletions

View file

@ -14,6 +14,11 @@ IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
SET(ICONV_FIND_QUIETLY TRUE) SET(ICONV_FIND_QUIETLY TRUE)
ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
IF(WIN32)
SET(ICONV_INCLUDE_DIR $ENV{ICONV_INCLUDE_DIR})
SET(ICONV_LIBRARIES $ENV{ICONV_LIBRARIES})
ENDIF(WIN32)
FIND_PATH(ICONV_INCLUDE_DIR iconv.h) FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv c) FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv c)

View file

@ -9,7 +9,10 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <errno.h> #include <errno.h>
#include <iconv.h>
#ifndef __WIN32__
#include <iconv.h>
#endif
#include <libs/mangle/stream/stream.hpp> #include <libs/mangle/stream/stream.hpp>
#include <libs/mangle/stream/servers/file_stream.hpp> #include <libs/mangle/stream/servers/file_stream.hpp>
@ -619,12 +622,15 @@ public:
} }
// Convert a string from the encoding used by Morrowind to UTF-8 // Convert a string from the encoding used by Morrowind to UTF-8
std::string convertToUTF8(std::string input) std::string convertToUTF8 (std::string input)
{ {
#ifdef __WIN32__
return input;
#else
std::string output = ""; std::string output = "";
//create convert description //create convert description
iconv_t cd = iconv_open("UTF-8", "WINDOWS-1252"); iconv_t cd = iconv_open ("UTF-8", "WINDOWS-1252");
if (cd == (iconv_t)-1) //error handling if (cd == (iconv_t)-1) //error handling
{ {
@ -645,7 +651,7 @@ public:
errMsg += "Unknown Error\n"; errMsg += "Unknown Error\n";
} }
fail(errMsg); fail (errMsg);
} }
else else
@ -655,7 +661,7 @@ public:
if (inputSize) //input is not empty if (inputSize) //input is not empty
{ {
//convert function doesn't accept const char *, therefore copy content into an char * //convert function doesn't accept const char *, therefore copy content into an char *
std::vector<char> inputBuffer(input.begin(), input.end()); std::vector<char> inputBuffer (input.begin(), input.end());
char *inputBufferBegin = &inputBuffer[0]; char *inputBufferBegin = &inputBuffer[0];
size_t inputBytesLeft = inputSize; //bytes to convert size_t inputBytesLeft = inputSize; //bytes to convert
@ -666,33 +672,33 @@ public:
char outputBuffer[outputSize]; char outputBuffer[outputSize];
char *outputBufferBegin; char *outputBufferBegin;
while (inputBytesLeft > 0 ) while (inputBytesLeft > 0)
{ {
outputBytesLeft = outputSize; outputBytesLeft = outputSize;
outputBufferBegin = outputBuffer; outputBufferBegin = outputBuffer;
if (iconv(cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1) if (iconv (cd, &inputBufferBegin, &inputBytesLeft, &outputBufferBegin, &outputBytesLeft) == (size_t)-1)
{ {
switch (errno) switch (errno)
{ {
case E2BIG: //outputBuffer is full case E2BIG: //outputBuffer is full
output += std::string(outputBuffer, outputSize); output += std::string (outputBuffer, outputSize);
break; break;
case EILSEQ: case EILSEQ:
fail("Iconv: Invalid multibyte sequence.\n"); fail ("Iconv: Invalid multibyte sequence.\n");
break; break;
case EINVAL: case EINVAL:
fail("Iconv: Incomplete multibyte sequence.\n"); fail ("Iconv: Incomplete multibyte sequence.\n");
break; break;
default: default:
fail("Iconv: Unknown Error\n"); fail ("Iconv: Unknown Error\n");
} }
} }
} }
//read only relevant bytes from outputBuffer //read only relevant bytes from outputBuffer
output += std::string(outputBuffer, outputSize - outputBytesLeft); output += std::string (outputBuffer, outputSize - outputBytesLeft);
} }
} }
@ -701,6 +707,7 @@ public:
return output; return output;
} }
#endif
void skip(int bytes) { esm->seek(esm->tell()+bytes); } void skip(int bytes) { esm->seek(esm->tell()+bytes); }
uint64_t getOffset() { return esm->tell(); } uint64_t getOffset() { return esm->tell(); }

View file

@ -26,11 +26,11 @@
#include <stdio.h> #include <stdio.h>
#include <libs/mangle/vfs/servers/ogre_vfs.hpp> #include <libs/mangle/vfs/servers/ogre_vfs.hpp>
#include "components/nif/nif_file.hpp" #include "../nif/nif_file.hpp"
#include "components/nif/node.hpp" #include "../nif/node.hpp"
#include "components/nif/data.hpp" #include "../nif/data.hpp"
#include "components/nif/property.hpp" #include "../nif/property.hpp"
#include "libs/platform/strings.h" #include <libs/platform/strings.h>
// For warning messages // For warning messages
#include <iostream> #include <iostream>
@ -45,21 +45,43 @@ using namespace Ogre;
using namespace Nif; using namespace Nif;
using namespace Mangle::VFS; using namespace Mangle::VFS;
// This is the interface to the Ogre resource system. It allows us to NIFLoader& NIFLoader::getSingleton()
// 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;
// Singleton instance used by load()
static NIFLoader g_sing;
// Makeshift error reporting system
static string errName;
static void warn(const string &msg)
{ {
cout << "WARNING (NIF:" << errName << "): " << msg << endl; static NIFLoader instance;
return instance;
}
NIFLoader* NIFLoader::getSingletonPtr()
{
return &getSingleton();
}
void NIFLoader::warn(string msg)
{
std::cerr << "NIFLoader: Warn:" << msg << "\n";
}
void NIFLoader::fail(string msg)
{
std::cerr << "NIFLoader: Fail: "<< msg << std::endl;
assert(1);
}
Vector3 NIFLoader::convertVector3(const Nif::Vector& vec)
{
return Ogre::Vector3(vec.array);
}
Quaternion NIFLoader::convertRotation(const Nif::Matrix& rot)
{
Real matrix[3][3];
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
matrix[i][j] = rot.v[i].array[j];
return Quaternion(Matrix3(matrix));
} }
// Helper class that computes the bounding box and of a mesh // Helper class that computes the bounding box and of a mesh
@ -77,8 +99,8 @@ class BoundsFinder
void add(float f) void add(float f)
{ {
if(f > max) max = f; if (f > max) max = f;
if(f < min) min = f; if (f < min) min = f;
} }
// Return Max(max**2, min**2) // Return Max(max**2, min**2)
@ -86,7 +108,7 @@ class BoundsFinder
{ {
float m1 = max*max; float m1 = max*max;
float m2 = min*min; float m2 = min*min;
if(m1 >= m2) return m1; if (m1 >= m2) return m1;
return m2; return m2;
} }
}; };
@ -99,7 +121,7 @@ public:
// point. // point.
void add(float *data, int verts) void add(float *data, int verts)
{ {
for(int i=0;i<verts;i++) for (int i=0;i<verts;i++)
{ {
X.add(*(data++)); X.add(*(data++));
Y.add(*(data++)); Y.add(*(data++));
@ -126,12 +148,24 @@ public:
return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared()); return sqrt(X.getMaxSquared() + Y.getMaxSquared() + Z.getMaxSquared());
} }
float minX() { return X.min; } float minX() {
float maxX() { return X.max; } return X.min;
float minY() { return Y.min; } }
float maxY() { return Y.max; } float maxX() {
float minZ() { return Z.min; } return X.max;
float maxZ() { return Z.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. // Conversion of blend / test mode from NIF -> OGRE.
@ -177,7 +211,7 @@ static CompareFunction getTestMode(int mode)
} }
*/ */
static void createMaterial(const String &name, void NIFLoader::createMaterial(const String &name,
const Vector &ambient, const Vector &ambient,
const Vector &diffuse, const Vector &diffuse,
const Vector &specular, const Vector &specular,
@ -186,17 +220,18 @@ static void createMaterial(const String &name,
float alphaFlags, float alphaTest, float alphaFlags, float alphaTest,
const String &texName) 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 // This assigns the texture to this material. If the texture name is
// a file name, and this file exists (in a resource directory), it // a file name, and this file exists (in a resource directory), it
// will automatically be loaded when needed. If not (such as for // will automatically be loaded when needed. If not (such as for
// internal NIF textures that we might support later), we should // internal NIF textures that we might support later), we should
// already have inserted a manual loader for the texture. // already have inserted a manual loader for the texture.
if(!texName.empty()) if (!texName.empty())
{ {
Pass *pass = material->getTechnique(0)->getPass(0); Pass *pass = material->getTechnique(0)->getPass(0);
/*TextureUnitState *txt =*/ pass->createTextureUnitState(texName); /*TextureUnitState *txt =*/
pass->createTextureUnitState(texName);
/* As of yet UNTESTED code from Chris: /* As of yet UNTESTED code from Chris:
pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC); pass->setTextureFiltering(Ogre::TFO_ANISOTROPIC);
@ -226,12 +261,12 @@ static void createMaterial(const String &name,
*/ */
// Add transparency if NiAlphaProperty was present // Add transparency if NiAlphaProperty was present
if(alphaFlags != -1) if (alphaFlags != -1)
{ {
// The 237 alpha flags are by far the most common. Check // The 237 alpha flags are by far the most common. Check
// NiAlphaProperty in nif/property.h if you need to decode // NiAlphaProperty in nif/property.h if you need to decode
// other values. 237 basically means normal transparencly. // other values. 237 basically means normal transparencly.
if(alphaFlags == 237) if (alphaFlags == 237)
{ {
// Enable transparency // Enable transparency
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA); pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
@ -254,14 +289,14 @@ static void createMaterial(const String &name,
// Takes a name and adds a unique part to it. This is just used to // Takes a name and adds a unique part to it. This is just used to
// make sure that all materials are given unique names. // 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 int addon = 0;
static char buf[8]; static char buf[8];
snprintf(buf, 8, "_%d", addon++); snprintf(buf, 8, "_%d", addon++);
// Don't overflow the buffer // Don't overflow the buffer
if(addon > 999999) addon = 0; if (addon > 999999) addon = 0;
return input + buf; return input + buf;
} }
@ -270,13 +305,13 @@ static String getUniqueName(const String &input)
// does not, change the string IN PLACE to say .dds instead and try // 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 // that. The texture may still not exist, but no information of value
// is lost in that case. // is lost in that case.
static void findRealTexture(String &texName) void NIFLoader::findRealTexture(String &texName)
{ {
assert(vfs); assert(vfs);
if(vfs->isFile(texName)) return; if (vfs->isFile(texName)) return;
int len = texName.size(); int len = texName.size();
if(len < 4) return; if (len < 4) return;
// Change texture extension to .dds // Change texture extension to .dds
texName[len-3] = 'd'; texName[len-3] = 'd';
@ -286,7 +321,7 @@ static void findRealTexture(String &texName)
// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given // Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given
// mesh. // mesh.
static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material) void NIFLoader::createOgreSubMesh(NiTriShape *shape, const String &material)
{ {
NiTriShapeData *data = shape->data.getPtr(); NiTriShapeData *data = shape->data.getPtr();
SubMesh *sub = mesh->createSubMesh(shape->name.toString()); SubMesh *sub = mesh->createSubMesh(shape->name.toString());
@ -312,7 +347,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
bind->setBinding(nextBuf++, vbuf); bind->setBinding(nextBuf++, vbuf);
// Vertex normals // Vertex normals
if(data->normals.length) if (data->normals.length)
{ {
decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL); decl->addElement(nextBuf, 0, VET_FLOAT3, VES_NORMAL);
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
@ -323,13 +358,13 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
} }
// Vertex colors // Vertex colors
if(data->colors.length) if (data->colors.length)
{ {
const float *colors = data->colors.ptr; const float *colors = data->colors.ptr;
RenderSystem* rs = Root::getSingleton().getRenderSystem(); RenderSystem* rs = Root::getSingleton().getRenderSystem();
std::vector<RGBA> colorsRGB(numVerts); std::vector<RGBA> colorsRGB(numVerts);
RGBA *pColour = &colorsRGB.front(); 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], rs->convertColourValue(ColourValue(colors[0],colors[1],colors[2],
colors[3]),pColour++); colors[3]),pColour++);
@ -344,7 +379,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
} }
// Texture UV coordinates // Texture UV coordinates
if(data->uvlist.length) if (data->uvlist.length)
{ {
decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES); decl->addElement(nextBuf, 0, VET_FLOAT2, VES_TEXTURE_COORDINATES);
vbuf = HardwareBufferManager::getSingleton().createVertexBuffer( vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
@ -357,7 +392,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
// Triangle faces // Triangle faces
int numFaces = data->triangles.length; int numFaces = data->triangles.length;
if(numFaces) if (numFaces)
{ {
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton(). HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
createIndexBuffer(HardwareIndexBuffer::IT_16BIT, createIndexBuffer(HardwareIndexBuffer::IT_16BIT,
@ -370,23 +405,35 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
} }
// Set material if one was given // 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
VertexBoneAssignment v;
v.boneIndex = ((Bone*)bone)->getHandle();
v.weight = 1.0;
std::cerr << "+ Assigning bone index " << v.boneIndex << "\n"; // assigning bones if skindata is not empty
if (!shape->skin.empty())
for(int i=0; i < numVerts; i++)
{ {
v.vertexIndex = i; NodeList *boneList = &shape->skin->bones;
sub->addBoneAssignment(v);
for (int i=0; i<boneList->length(); i++)
{
if (boneList->has(i))
{
Nif::Node *bone = &(*boneList)[i];
SkeletonPtr skel = SkeletonManager::getSingleton().getByName(getSkeletonName());
VertexBoneAssignment vba;
vba.boneIndex = skel->getBone(bone->name.toString())->getHandle();
vba.weight = 1.0;
for (unsigned int j=0; j<sub->vertexData->vertexCount; j++) //assing every vertex
{
vba.vertexIndex = j;
sub->addBoneAssignment(vba);
} }
*/ }
}
}
} }
// Helper math functions. Reinventing linear algebra for the win! // Helper math functions. Reinventing linear algebra for the win!
@ -394,7 +441,7 @@ static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material
// Computes B = AxB (matrix*matrix) // Computes B = AxB (matrix*matrix)
static void matrixMul(const Matrix &A, Matrix &B) 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 a = B.v[0].array[i];
float b = B.v[1].array[i]; float b = B.v[1].array[i];
@ -415,7 +462,7 @@ static void vectorMulAdd(const Matrix &A, const Vector &B, float *C, float scale
float c = C[2]; float c = C[2];
// Perform matrix multiplication, scaling and addition // 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; 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 +475,11 @@ static void vectorMul(const Matrix &A, float *C)
float c = C[2]; float c = C[2];
// Perform matrix multiplication, scaling and addition // 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]; 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(NiTriShape *shape, int flags, BoundsFinder &bounds)
{ {
assert(shape != NULL); assert(shape != NULL);
@ -442,7 +489,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision bool bbcollide = (flags & 0x04) != 0; // Use bounding box for collision
// Bounding box collision isn't implemented, always use mesh for now. // Bounding box collision isn't implemented, always use mesh for now.
if(bbcollide) if (bbcollide)
{ {
collide = true; collide = true;
bbcollide = false; bbcollide = false;
@ -450,10 +497,13 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// If the object was marked "NCO" earlier, it shouldn't collide with // If the object was marked "NCO" earlier, it shouldn't collide with
// anything. // anything.
if(flags & 0x800) if (flags & 0x800)
{ collide = false; bbcollide = false; } {
collide = false;
bbcollide = false;
}
if(!collide && !bbcollide && hidden) if (!collide && !bbcollide && hidden)
// This mesh apparently isn't being used for anything, so don't // This mesh apparently isn't being used for anything, so don't
// bother setting it up. // bother setting it up.
return; return;
@ -462,7 +512,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
String material; String material;
// Skip the entire material phase for hidden nodes // Skip the entire material phase for hidden nodes
if(!hidden) if (!hidden)
{ {
// These are set below if present // These are set below if present
NiTexturingProperty *t = NULL; NiTexturingProperty *t = NULL;
@ -472,27 +522,27 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// Scan the property list for material information // Scan the property list for material information
PropertyList &list = shape->props; PropertyList &list = shape->props;
int n = list.length(); int n = list.length();
for(int i=0; i<n; i++) for (int i=0; i<n; i++)
{ {
// Entries may be empty // Entries may be empty
if(!list.has(i)) continue; if (!list.has(i)) continue;
Property *pr = &list[i]; Property *pr = &list[i];
if(pr->recType == RC_NiTexturingProperty) if (pr->recType == RC_NiTexturingProperty)
t = (NiTexturingProperty*)pr; t = (NiTexturingProperty*)pr;
else if(pr->recType == RC_NiMaterialProperty) else if (pr->recType == RC_NiMaterialProperty)
m = (NiMaterialProperty*)pr; m = (NiMaterialProperty*)pr;
else if(pr->recType == RC_NiAlphaProperty) else if (pr->recType == RC_NiAlphaProperty)
a = (NiAlphaProperty*)pr; a = (NiAlphaProperty*)pr;
} }
// Texture // Texture
String texName; String texName;
if(t && t->textures[0].inUse) if (t && t->textures[0].inUse)
{ {
NiSourceTexture *st = t->textures[0].texture.getPtr(); NiSourceTexture *st = t->textures[0].texture.getPtr();
if(st->external) if (st->external)
{ {
SString tname = st->filename; SString tname = st->filename;
@ -518,14 +568,14 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// Alpha modifiers // Alpha modifiers
int alphaFlags = -1; int alphaFlags = -1;
ubyte alphaTest = 0; ubyte alphaTest = 0;
if(a) if (a)
{ {
alphaFlags = a->flags; alphaFlags = a->flags;
alphaTest = a->data->threshold; alphaTest = a->data->threshold;
} }
// Material // Material
if(m || !texName.empty()) if (m || !texName.empty())
{ {
// If we're here, then this mesh has a material. Thus we // If we're here, then this mesh has a material. Thus we
// need to calculate a snappy material name. It should // need to calculate a snappy material name. It should
@ -533,7 +583,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// be unique. One mesh may use many materials. // be unique. One mesh may use many materials.
material = getUniqueName(mesh->getName()); material = getUniqueName(mesh->getName());
if(m) if (m)
{ {
// Use NiMaterialProperty data to create the data // Use NiMaterialProperty data to create the data
const S_MaterialProperty *d = m->data; const S_MaterialProperty *d = m->data;
@ -545,7 +595,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
// We only have a texture name. Create a default // We only have a texture name. Create a default
// material for it. // material for it.
Vector zero, one; Vector zero, one;
for(int i=0; i<3;i++) for (int i=0; i<3;i++)
{ {
zero.array[i] = 0.0; zero.array[i] = 0.0;
one.array[i] = 1.0; one.array[i] = 1.0;
@ -573,35 +623,35 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFin
const Matrix &rot = shape->trafo->rotation; const Matrix &rot = shape->trafo->rotation;
const Vector &pos = shape->trafo->pos; const Vector &pos = shape->trafo->pos;
float scale = shape->trafo->scale; float scale = shape->trafo->scale;
for(int i=0; i<numVerts; i++) for (int i=0; i<numVerts; i++)
{ {
vectorMulAdd(rot, pos, ptr, scale); vectorMulAdd(rot, pos, ptr, scale);
ptr += 3; ptr += 3;
} }
// Remember to rotate all the vertex normals as well // Remember to rotate all the vertex normals as well
if(data->normals.length) if (data->normals.length)
{ {
ptr = (float*)data->normals.ptr; ptr = (float*)data->normals.ptr;
for(int i=0; i<numVerts; i++) for (int i=0; i<numVerts; i++)
{ {
vectorMul(rot, ptr); vectorMul(rot, ptr);
ptr += 3; ptr += 3;
} }
} }
if(!hidden) if (!hidden)
{ {
// Add this vertex set to the bounding box // Add this vertex set to the bounding box
bounds.add(optr, numVerts); bounds.add(optr, numVerts);
// Create the submesh // Create the submesh
createOgreMesh(mesh, shape, material); createOgreSubMesh(shape, material);
} }
} }
static void handleNode(Mesh* mesh, Nif::Node *node, int flags, void NIFLoader::handleNode(Nif::Node *node, int flags,
const Transformation *trafo, BoundsFinder &bounds) const Transformation *trafo, BoundsFinder &bounds, Bone *parentBone)
{ {
// Accumulate the flags from all the child nodes. This works for all // Accumulate the flags from all the child nodes. This works for all
// the flags we currently use, at least. // the flags we currently use, at least.
@ -609,22 +659,22 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
// Check for extra data // Check for extra data
Extra *e = node; Extra *e = node;
while(!e->extra.empty()) while (!e->extra.empty())
{ {
// Get the next extra data in the list // Get the next extra data in the list
e = e->extra.getPtr(); e = e->extra.getPtr();
assert(e != NULL); assert(e != NULL);
if(e->recType == RC_NiStringExtraData) if (e->recType == RC_NiStringExtraData)
{ {
// String markers may contain important information // String markers may contain important information
// affecting the entire subtree of this node // affecting the entire subtree of this node
NiStringExtraData *sd = (NiStringExtraData*)e; NiStringExtraData *sd = (NiStringExtraData*)e;
if(sd->string == "NCO") if (sd->string == "NCO")
// No collision. Use an internal flag setting to mark this. // No collision. Use an internal flag setting to mark this.
flags |= 0x800; flags |= 0x800;
else if(sd->string == "MRK") else if (sd->string == "MRK")
// Marker objects. These are only visible in the // Marker objects. These are only visible in the
// editor. Until and unless we add an editor component to // editor. Until and unless we add an editor component to
// the engine, just skip this entire node. // the engine, just skip this entire node.
@ -632,9 +682,39 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
} }
} }
Bone *bone = 0;
// create skeleton or add bones
if (node->recType == RC_NiNode)
{
if (node->name == "Bip01") //root node, create a skeleton
{
skel = SkeletonManager::getSingleton().create(getSkeletonName(), resourceGroup, true);
}
if (!skel.isNull()) //if there is a skeleton
{
bone = skel->createBone(node->name.toString());
if (parentBone)
parentBone->addChild(bone);
bone->setInheritOrientation(true);
bone->setPosition(convertVector3(node->trafo->pos));
bone->setOrientation(convertRotation(node->trafo->rotation));
}
//output for debuging purpose
// Ogre::Vector3 vec(node->trafo->pos.array);
// Ogre::Quaternion q = convertRotation(node->trafo->rotation);
// std::cout << node->name.toString() << ": " << vec.x << " " << vec.y << " " << vec.z << "\n";
// std::cout << " Y: " << q.getYaw().valueDegrees() << " P: " << q.getPitch().valueDegrees() << " R: " << q.getRoll().valueDegrees() << "\n";
}
// Apply the parent transformation to this node. We overwrite the // Apply the parent transformation to this node. We overwrite the
// existing data with the final transformation. // existing data with the final transformation.
if(trafo) if (trafo)
{ {
// Get a non-const reference to the node's data, since we're // Get a non-const reference to the node's data, since we're
// overwriting it. TODO: Is this necessary? // overwriting it. TODO: Is this necessary?
@ -654,34 +734,38 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags,
} }
// For NiNodes, loop through children // For NiNodes, loop through children
if(node->recType == RC_NiNode) if (node->recType == RC_NiNode)
{ {
NodeList &list = ((NiNode*)node)->children; NodeList &list = ((NiNode*)node)->children;
int n = list.length(); 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); handleNode(&list[i], flags, node->trafo, bounds, bone);
} }
} }
else if(node->recType == RC_NiTriShape) else if (node->recType == RC_NiTriShape)
// For shapes // For shapes
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds); handleNiTriShape(dynamic_cast<NiTriShape*>(node), flags, bounds);
} }
void NIFLoader::loadResource(Resource *resource) void NIFLoader::loadResource(Resource *resource)
{ {
resourceName = "";
mesh = 0;
skel.setNull();
// Set up the VFS if it hasn't been done already // 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 // Get the mesh
Mesh *mesh = dynamic_cast<Mesh*>(resource); mesh = dynamic_cast<Mesh*>(resource);
assert(mesh); assert(mesh);
// Look it up // Look it up
const String &name = mesh->getName(); resourceName = mesh->getName();
errName = name; // Set name for error messages
if(!vfs->isFile(name)) if (!vfs->isFile(resourceName))
{ {
warn("File not found."); warn("File not found.");
return; return;
@ -694,9 +778,9 @@ void NIFLoader::loadResource(Resource *resource)
// of the early stages of development. Right now we WANT to catch // of the early stages of development. Right now we WANT to catch
// every error as early and intrusively as possible, as it's most // every error as early and intrusively as possible, as it's most
// likely a sign of incomplete code rather than faulty input. // likely a sign of incomplete code rather than faulty input.
NIFFile nif(vfs->open(name), name); NIFFile nif(vfs->open(resourceName), resourceName);
if(nif.numRecords() < 1) if (nif.numRecords() < 1)
{ {
warn("Found no records in NIF."); warn("Found no records in NIF.");
return; return;
@ -708,7 +792,7 @@ void NIFLoader::loadResource(Resource *resource)
Nif::Node *node = dynamic_cast<Nif::Node*>(r); 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 " + warn("First record in file was not a node, but a " +
r->recName.toString() + ". Skipping file."); r->recName.toString() + ". Skipping file.");
@ -716,10 +800,14 @@ void NIFLoader::loadResource(Resource *resource)
} }
// Handle the node // Handle the node
handleNode(mesh, node, 0, NULL, bounds); handleNode(node, 0, NULL, bounds, 0);
//set skeleton
if (!skel.isNull())
mesh->setSkeletonName(getSkeletonName());
// Finally, set the bounding value. // Finally, set the bounding value.
if(bounds.isValid()) if (bounds.isValid())
{ {
mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(), mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(),
bounds.maxX(), bounds.maxY(), bounds.maxZ())); bounds.maxX(), bounds.maxY(), bounds.maxZ()));
@ -734,11 +822,11 @@ MeshPtr NIFLoader::load(const std::string &name,
// Check if the resource already exists // Check if the resource already exists
ResourcePtr ptr = m->getByName(name, group); ResourcePtr ptr = m->getByName(name, group);
if(!ptr.isNull()) if (!ptr.isNull())
return MeshPtr(ptr); return MeshPtr(ptr);
// Nope, create a new one. // 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 /* More code currently not in use, from the old D source. This was

View file

@ -27,6 +27,27 @@
#include <OgreResource.h> #include <OgreResource.h>
#include <OgreMesh.h> #include <OgreMesh.h>
#include <assert.h> #include <assert.h>
#include <string>
class BoundsFinder;
namespace Nif
{
class Node;
class Transformation;
class NiTriShape;
class Vector;
class Matrix;
}
namespace Mangle
{
namespace VFS
{
class OgreVFS;
}
}
/** Manual resource loader for NIF meshes. This is the main class /** Manual resource loader for NIF meshes. This is the main class
responsible for translating the internal NIF mesh structure into responsible for translating the internal NIF mesh structure into
@ -43,12 +64,66 @@
very resource intensive, and can safely be done for a large number very resource intensive, and can safely be done for a large number
of meshes at load time. of meshes at load time.
*/ */
struct NIFLoader : Ogre::ManualResourceLoader class NIFLoader : Ogre::ManualResourceLoader
{ {
void loadResource(Ogre::Resource *resource); public:
static NIFLoader& getSingleton();
static NIFLoader* getSingletonPtr();
virtual void loadResource(Ogre::Resource *resource);
static Ogre::MeshPtr load(const std::string &name, static Ogre::MeshPtr load(const std::string &name,
const std::string &group="General"); const std::string &group="General");
Ogre::Vector3 convertVector3(const Nif::Vector& vec);
Ogre::Quaternion convertRotation(const Nif::Matrix& rot);
private:
NIFLoader() : resourceGroup("General") {}
NIFLoader(NIFLoader& n) {}
void warn(std::string msg);
void fail(std::string msg);
void handleNode( Nif::Node *node, int flags,
const Nif::Transformation *trafo, BoundsFinder &bounds, Ogre::Bone *parentBone);
void handleNiTriShape(Nif::NiTriShape *shape, int flags, BoundsFinder &bounds);
void createOgreSubMesh(Nif::NiTriShape *shape, const Ogre::String &material);
void createMaterial(const Ogre::String &name,
const Nif::Vector &ambient,
const Nif::Vector &diffuse,
const Nif::Vector &specular,
const Nif::Vector &emissive,
float glossiness, float alpha,
float alphaFlags, float alphaTest,
const Ogre::String &texName);
void findRealTexture(Ogre::String &texName);
Ogre::String getUniqueName(const Ogre::String &input);
//returns the skeleton name of this mesh
std::string getSkeletonName()
{
return resourceName + ".skel";
}
// 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.
Mangle::VFS::OgreVFS *vfs;
std::string resourceName;
std::string resourceGroup;
// pointer to the ogre mesh which is currently build
Ogre::Mesh *mesh;
Ogre::SkeletonPtr skel;
}; };
#endif #endif