From 3a0600b84c6be466e0bcb5a1c69da88fb294ebd7 Mon Sep 17 00:00:00 2001 From: Nicolay Korslund Date: Tue, 22 Jun 2010 09:49:44 +0200 Subject: [PATCH] Implemented correct bounding box computation in NIFLoader --- nifogre/ogre_nif_loader.cpp | 184 +++++++++++++++++++++++++++--------- 1 file changed, 138 insertions(+), 46 deletions(-) diff --git a/nifogre/ogre_nif_loader.cpp b/nifogre/ogre_nif_loader.cpp index ec774e061..d362f4392 100644 --- a/nifogre/ogre_nif_loader.cpp +++ b/nifogre/ogre_nif_loader.cpp @@ -34,6 +34,9 @@ // For warning messages #include +// float infinity +#include + 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::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 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,47 +555,53 @@ 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 + nested levels of sub-meshes with transformations applied to each + level. + */ + NiTriShapeData *data = shape->data.getPtr(); + 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; + const Vector &pos = shape->trafo->pos; + float scale = shape->trafo->scale; + for(int i=0; idata.getPtr(); - int numVerts = data->vertices.length / 3; - - float *ptr = (float*)data->vertices.ptr; - - // Rotate, scale and translate all the vertices - const Matrix &rot = shape->trafo->rotation; - const Vector &pos = shape->trafo->pos; - float scale = shape->trafo->scale; - for(int i=0; inormals.length) - { - ptr = (float*)data->normals.ptr; - for(int i=0; inormals.length) + { + ptr = (float*)data->normals.ptr; + for(int i=0; itrafo); // 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; itrafo); + 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(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(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,