Completed material code. Not tested.

pull/7/head
Nicolay Korslund 15 years ago
parent 9b7c995ae6
commit 752237e245

@ -23,6 +23,7 @@
#include "ogre_nif_loader.h"
#include <Ogre.h>
#include <stdio.h>
#include "../mangle/vfs/servers/ogre_vfs.h"
#include "../nif/nif_file.h"
@ -40,18 +41,104 @@ 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.)
// 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.
OgreVFS *vfs;
// Singleton instance used by load()
static NIFLoader g_sing;
static string errName;
static void warn(const string &msg)
{
cout << "WARNING (NIF): " << msg << endl;
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
}
static void createMaterial(const String &material,
const Vector &ambient,
const Vector &diffuse,
const Vector &specular,
const Vector &emissive,
float glossiness, float alpha,
float alphaFlags, float alphaTest,
const String &texName)
{
MaterialPtr material = MaterialManager::getSingleton().create(material, "General");
// 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())
{
Pass *pass = material->getTechnique(0)->getPass(0);
TextureUnitState *txt = pass->createTextureUnitState(texName);
// 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)
{
// Enable transparency
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
//pass->setDepthCheckEnabled(false);
pass->setDepthWriteEnabled(false);
}
else
warn("Unhandled alpha setting for texture " + texName);
}
}
// Add material bells and whistles
material->setAmbient(ambient.array[0], ambient.array[1], ambient.array[2]);
material->setDiffuse(diffuse.array[0], diffuse.array[1], diffuse.array[2], alpha);
material->setSpecular(specular.array[0], specular.array[1], specular.array[2], alpha);
material->setSelfIllumination(emissive.array[0], emissive.array[1], emissive.array[2]);
material->setShininess(glossiness);
}
// 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)
{
static int addon = 0;
static char buf[8];
snprintf(buf,8,"_%d", addon++);
// Don't overflow the buffer
if(addon > 1999999) addon = 0;
return input + buf;
}
// Check if the given texture name exists in the real world. If it
// 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 findRealTexture(String &texName)
{
assert(vfs);
if(vfs.isFile(texName)) return;
int len = texName.size();
if(len < 4) return;
// In-place string changing hack
char *ptr = (char*)texName.c_str();
strcpy(ptr-3, "dds");
cout << "Replaced with " << texName << endl;
}
// Convert Nif::NiTriShape to Ogre::SubMesh, attached the given mesh.
// Convert Nif::NiTriShape to Ogre::SubMesh, attached to the given
// mesh.
static void createOgreMesh(Mesh *mesh, NiTriShape *shape, const String &material)
{
NiTriShapeData *data = shape->data.getPtr();
@ -170,7 +257,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
if(!hidden)
{
// These are set below if present
NiTexturingProperty *p = NULL;
NiTexturingProperty *t = NULL;
NiMaterialProperty *m = NULL;
NiAlphaProperty *a = NULL;
@ -179,20 +266,87 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
int n = list.length();
for(int i=0; i<n; i++)
{
// Entries may be empty
if(!list.has(i)) continue;
Property *pr = &list[i];
if(pr->recType == RC_NiTexturingProperty)
p = (NiTexturingProperty*)pr;
t = (NiTexturingProperty*)pr;
else if(pr->recType == RC_NiMaterialProperty)
m = (NiMaterialProperty*)pr;
else if(pr->recType == RC_NiAlphaProperty)
a = (NiAlphaProperty*)pr;
}
if(p) cout << "texture present\n";
if(m) cout << "material present\n";
if(a) cout << "alpha present\n";
// Texture
String texName;
if(t && t->textures[0].inUse)
{
NiSourceTexture *st = t->textures[0].texture.getPtr();
if(st->external)
{
SString tname = st->filename;
/* findRealTexture checks if the file actually
exists. If it doesn't, and the name ends in .tga, it
will try replacing the extension with .dds instead
and search for that. 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.
The function replaces the name in place (that's why
we cast away the const modifier), but this is no
problem since all the nif data is stored in a local
throwaway buffer.
*/
texName = tname.toString();
findRealTexture(texName);
}
else warn("Found internal texture, ignoring.");
}
// Alpha modifiers
int alphaFlags = -1;
ubyte alphaTest;
if(a)
{
alphaFlags = a->flags;
alphaTest = a->data->threshold;
}
// Material
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
// contain the mesh name (mesh->getName()) but also has to
// be unique. One mesh may use many materials.
material = getUniqueName(mesh->getName());
if(m)
{
// Use NiMaterialProperty data to create the data
const S_MaterialProperty *d = m->data;
createMaterial(material, d->ambient, d->diffuse, d->specular, d->emissive,
d->glossiness, d->alpha, alphaFlags, alphaTest, texName);
}
else
{
// We only have a texture name. Create a default
// material for it.
Vector zero, one;
for(int i=0; i<3;i++)
{
zero[i] = 0.0;
one[i] = 1.0;
}
createMaterial(material, one, one, zero, zero, 0.0, 1.0,
alphaFlags, alphaTest, texName);
}
}
}
// TODO: Do in-place transformation of all the vertices and
@ -204,7 +358,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
// are const.)
if(!hidden)
createOgreMesh(mesh, shape, "");
createOgreMesh(mesh, shape, material);
}
static void handleNode(Mesh* mesh, Nif::Node *node, int flags)
@ -265,9 +419,10 @@ void NIFLoader::loadResource(Resource *resource)
// Look it up
const String &name = mesh->getName();
errName = name; // Set name for error messages
if(!vfs->isFile(name))
{
warn("File not found: " + name);
warn("File not found.");
return;
}
@ -276,7 +431,7 @@ void NIFLoader::loadResource(Resource *resource)
if(nif.numRecords() < 1)
{
warn("Found no records in " + name);
warn("Found no records in NIF.");
return;
}
@ -286,7 +441,7 @@ void NIFLoader::loadResource(Resource *resource)
if(r->recType != RC_NiNode)
{
warn("First record in " + name + " was not a NiNode, but a " +
warn("First record in file was not a NiNode, but a " +
r->recName.toString() + ". Skipping file.");
return;
}

Loading…
Cancel
Save