mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:53:51 +00:00
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
|
||||
#include <iostream>
|
||||
|
||||
// float infinity
|
||||
#include <limits>
|
||||
|
||||
typedef unsigned char ubyte;
|
||||
|
||||
using namespace std;
|
||||
|
@ -58,6 +61,78 @@ static void warn(const string &msg)
|
|||
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.
|
||||
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];
|
||||
}
|
||||
|
||||
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
|
||||
bool hidden = (flags & 0x01) != 0; // Not displayed
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // End of material block, if(!hidden) ...
|
||||
|
||||
{
|
||||
/* Do in-place transformation of all the vertices and normals. This
|
||||
is pretty messy stuff, but we need it to make the sub-meshes
|
||||
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;
|
||||
|
||||
float *ptr = (float*)data->vertices.ptr;
|
||||
float *optr = ptr;
|
||||
|
||||
// Rotate, scale and translate all the vertices
|
||||
const Matrix &rot = shape->trafo->rotation;
|
||||
|
@ -512,13 +589,19 @@ static void handleNiTriShape(Mesh *mesh, NiTriShape *shape, int flags)
|
|||
ptr += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!hidden)
|
||||
{
|
||||
// Add this vertex set to the bounding box
|
||||
bounds.add(optr, numVerts);
|
||||
|
||||
// Create the submesh
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Scalar values are so nice to deal with. Why can't everything
|
||||
// just be scalars?
|
||||
// just be scalar?
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
// For shapes
|
||||
handleNiTriShape(mesh, (NiTriShape*)node, flags);
|
||||
handleNiTriShape(mesh, dynamic_cast<NiTriShape*>(node), flags, bounds);
|
||||
}
|
||||
|
||||
void NIFLoader::loadResource(Resource *resource)
|
||||
|
@ -604,6 +687,9 @@ void NIFLoader::loadResource(Resource *resource)
|
|||
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
|
||||
// of the early stages of development. Right now we WANT to catch
|
||||
// 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);
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
mesh->_setBounds(AxisAlignedBox(-10,-10,-10,10,10,10));
|
||||
mesh->_setBoundingSphereRadius(10);
|
||||
// Finally, set the bounding value.
|
||||
if(bounds.isValid())
|
||||
{
|
||||
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,
|
||||
|
|
Loading…
Reference in a new issue