mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 18:19:55 +00:00
Completed material code. Not tested.
This commit is contained in:
parent
9b7c995ae6
commit
752237e245
1 changed files with 167 additions and 12 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
// Convert Nif::NiTriShape to Ogre::SubMesh, attached the given mesh.
|
||||
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 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…
Reference in a new issue