forked from teamnwah/openmw-tes3coop
Implemented correct bounding box computation in NIFLoader
This commit is contained in:
parent
782a90066d
commit
3a0600b84c
1 changed files with 138 additions and 46 deletions
|
@ -34,6 +34,9 @@
|
||||||
// For warning messages
|
// For warning messages
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
// float infinity
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
typedef unsigned char ubyte;
|
typedef unsigned char ubyte;
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -58,6 +61,78 @@ static void warn(const string &msg)
|
||||||
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
|
cout << "WARNING (NIF:" << errName << "): " << msg << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper class that computes the bounding box and of a mesh
|
||||||
|
class BoundsFinder
|
||||||
|
{
|
||||||
|
struct MaxMinFinder
|
||||||
|
{
|
||||||
|
float max, min;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
for(int i=0;i<verts;i++)
|
||||||
|
{
|
||||||
|
X.add(*(data++));
|
||||||
|
Y.add(*(data++));
|
||||||
|
Z.add(*(data++));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if this structure has valid values
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
minX() <= maxX() &&
|
||||||
|
minY() <= maxY() &&
|
||||||
|
minZ() <= maxZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute radius
|
||||||
|
float getRadius()
|
||||||
|
{
|
||||||
|
assert(isValid());
|
||||||
|
|
||||||
|
// The radius is computed from the origin, not from the geometric
|
||||||
|
// center of the mesh.
|
||||||
|
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; }
|
||||||
|
};
|
||||||
|
|
||||||
// Conversion of blend / test mode from NIF -> OGRE. Not in use yet.
|
// Conversion of blend / test mode from NIF -> OGRE. Not in use yet.
|
||||||
static SceneBlendFactor getBlendFactor(int mode)
|
static SceneBlendFactor getBlendFactor(int mode)
|
||||||
{
|
{
|
||||||
|
@ -357,8 +432,10 @@ static void vectorMul(const Matrix &A, float *C)
|
||||||
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)
|
static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags, BoundsFinder &bounds)
|
||||||
{
|
{
|
||||||
|
assert(shape != NULL);
|
||||||
|
|
||||||
// Interpret flags
|
// Interpret flags
|
||||||
bool hidden = (flags & 0x01) != 0; // Not displayed
|
bool hidden = (flags & 0x01) != 0; // Not displayed
|
||||||
bool collide = (flags & 0x02) != 0; // Use mesh for collision
|
bool collide = (flags & 0x02) != 0; // Use mesh for collision
|
||||||
|
@ -478,9 +555,8 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
||||||
alphaFlags, alphaTest, texName);
|
alphaFlags, alphaTest, texName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // End of material block, if(!hidden) ...
|
||||||
|
|
||||||
{
|
|
||||||
/* Do in-place transformation of all the vertices and normals. This
|
/* Do in-place transformation of all the vertices and normals. This
|
||||||
is pretty messy stuff, but we need it to make the sub-meshes
|
is pretty messy stuff, but we need it to make the sub-meshes
|
||||||
appear in the correct place. Neither Ogre nor Bullet support
|
appear in the correct place. Neither Ogre nor Bullet support
|
||||||
|
@ -491,6 +567,7 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
||||||
int numVerts = data->vertices.length / 3;
|
int numVerts = data->vertices.length / 3;
|
||||||
|
|
||||||
float *ptr = (float*)data->vertices.ptr;
|
float *ptr = (float*)data->vertices.ptr;
|
||||||
|
float *optr = ptr;
|
||||||
|
|
||||||
// Rotate, scale and translate all the vertices
|
// Rotate, scale and translate all the vertices
|
||||||
const Matrix &rot = shape->trafo->rotation;
|
const Matrix &rot = shape->trafo->rotation;
|
||||||
|
@ -512,13 +589,19 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
||||||
ptr += 3;
|
ptr += 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(!hidden)
|
if(!hidden)
|
||||||
|
{
|
||||||
|
// Add this vertex set to the bounding box
|
||||||
|
bounds.add(optr, numVerts);
|
||||||
|
|
||||||
|
// Create the submesh
|
||||||
createOgreMesh(mesh, shape, material);
|
createOgreMesh(mesh, shape, material);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformation *trafo = NULL)
|
static void 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
|
// 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.
|
||||||
|
@ -554,7 +637,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat
|
||||||
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.
|
// overwriting it. TODO: Is this necessary?
|
||||||
Transformation &final = *((Transformation*)node->trafo);
|
Transformation &final = *((Transformation*)node->trafo);
|
||||||
|
|
||||||
// For both position and rotation we have that:
|
// For both position and rotation we have that:
|
||||||
|
@ -566,7 +649,7 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat
|
||||||
matrixMul(trafo->rotation, final.rotation);
|
matrixMul(trafo->rotation, final.rotation);
|
||||||
|
|
||||||
// Scalar values are so nice to deal with. Why can't everything
|
// Scalar values are so nice to deal with. Why can't everything
|
||||||
// just be scalars?
|
// just be scalar?
|
||||||
final.scale *= trafo->scale;
|
final.scale *= trafo->scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,12 +661,12 @@ static void handleNode(Mesh* mesh, Nif::Node *node, int flags, const Transformat
|
||||||
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);
|
handleNode(mesh, &list[i], flags, node->trafo, bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(node->recType == RC_NiTriShape)
|
else if(node->recType == RC_NiTriShape)
|
||||||
// For shapes
|
// For shapes
|
||||||
handleNiTriShape(mesh, (NiTriShape*)node, flags);
|
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NIFLoader::loadResource(Resource *resource)
|
void NIFLoader::loadResource(Resource *resource)
|
||||||
|
@ -604,6 +687,9 @@ void NIFLoader::loadResource(Resource *resource)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper that computes bounding boxes for us.
|
||||||
|
BoundsFinder bounds;
|
||||||
|
|
||||||
// Load the NIF. TODO: Wrap this in a try-catch block once we're out
|
// Load the NIF. TODO: Wrap this in a try-catch block once we're out
|
||||||
// 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
|
||||||
|
@ -620,19 +706,25 @@ void NIFLoader::loadResource(Resource *resource)
|
||||||
Record *r = nif.getRecord(0);
|
Record *r = nif.getRecord(0);
|
||||||
assert(r != NULL);
|
assert(r != NULL);
|
||||||
|
|
||||||
if(r->recType != RC_NiNode && r->recType != RC_NiTriShape)
|
Nif::Node *node = dynamic_cast<Nif::Node*>(r);
|
||||||
|
|
||||||
|
if(node == NULL)
|
||||||
{
|
{
|
||||||
warn("First record in file was not a NiNode, but a " +
|
warn("First record in file was not a node, but a " +
|
||||||
r->recName.toString() + ". Skipping file.");
|
r->recName.toString() + ". Skipping file.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle the node
|
// Handle the node
|
||||||
handleNode(mesh, (Nif::Node*)r, 0);
|
handleNode(mesh, node, 0, NULL, bounds);
|
||||||
|
|
||||||
// Finally, set the bounding value. Just use bogus info right now.
|
// Finally, set the bounding value.
|
||||||
mesh->_setBounds(AxisAlignedBox(-10,-10,-10,10,10,10));
|
if(bounds.isValid())
|
||||||
mesh->_setBoundingSphereRadius(10);
|
{
|
||||||
|
mesh->_setBounds(AxisAlignedBox(bounds.minX(), bounds.minY(), bounds.minZ(),
|
||||||
|
bounds.maxX(), bounds.maxY(), bounds.maxZ()));
|
||||||
|
mesh->_setBoundingSphereRadius(bounds.getRadius());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshPtr NIFLoader::load(const std::string &name,
|
MeshPtr NIFLoader::load(const std::string &name,
|
||||||
|
|
Loading…
Reference in a new issue