From 76ebdecd7a6bc0fbe8b48d76c91b1d66f6f99435 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Tue, 29 Jul 2008 20:36:27 +0000 Subject: [PATCH] The "SplitLargeMeshes"-Process handles bones correctly now. Added Unittest for it and fixed some minor details. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@73 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/SplitLargeMeshes.cpp | 130 ++++++++++++++++++++++++ code/SplitLargeMeshes.h | 3 + test/unit/Main.cpp | 6 ++ test/unit/utSplitLargeMeshes.cpp | 103 +++++++++++++++++++ test/unit/utSplitLargeMeshes.h | 41 ++++++++ test/unit/utVertexTriangleAdjacency.cpp | 7 +- workspaces/vc8/UnitTest.vcproj | 4 + 7 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 test/unit/utSplitLargeMeshes.h diff --git a/code/SplitLargeMeshes.cpp b/code/SplitLargeMeshes.cpp index b8c2d8519..9f8bfb14a 100644 --- a/code/SplitLargeMeshes.cpp +++ b/code/SplitLargeMeshes.cpp @@ -234,6 +234,56 @@ void SplitLargeMeshesProcess_Triangle::SplitMesh( } } + if (pMesh->HasBones()) + { + // assume the number of bones won't change in most cases + pcMesh->mBones = new aiBone*[pMesh->mNumBones]; + + // iterate through all bones of the mesh and find those which + // need to be copied to the splitted mesh + std::vector avTempWeights; + for (unsigned int p = 0; p < pcMesh->mNumBones;++p) + { + aiBone* const bone = pcMesh->mBones[p]; + avTempWeights.clear(); + avTempWeights.reserve(bone->mNumWeights / iSubMeshes); + + for (unsigned int q = 0; q < bone->mNumWeights;++q) + { + aiVertexWeight& weight = bone->mWeights[q]; + if(weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum) + { + avTempWeights.push_back(weight); + weight = avTempWeights.back(); + weight.mVertexId -= iBase; + } + } + + if (!avTempWeights.empty()) + { + // we'll need this bone. Copy it ... + aiBone* pc = new aiBone(); + pcMesh->mBones[pcMesh->mNumBones++] = pc; + pc->mName = aiString(bone->mName); + pc->mNumWeights = avTempWeights.size(); + pc->mOffsetMatrix = bone->mOffsetMatrix; + + // no need to reallocate the array for the last submesh. + // Here we can reuse the (large) source array, although + // we'll waste some memory + if (iSubMeshes-1 == i) + { + pc->mWeights = bone->mWeights; + bone->mWeights = NULL; + } + else pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + // copy the weights + ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights); + } + } + } + // (we will also need to copy the array of indices) unsigned int iCurrent = 0; for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) @@ -356,6 +406,25 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh( { if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT) { + typedef std::vector< std::pair > VertexWeightTable; + VertexWeightTable* avPerVertexWeights = NULL; + + // build a per-vertex weight list if necessary + if (pMesh->HasBones()) + { + avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices]; + for (unsigned int i = 0; i < pMesh->mNumBones;++i) + { + aiBone* bone = pMesh->mBones[i]; + for (unsigned int a = 0; a < bone->mNumWeights;++a) + { + aiVertexWeight& weight = bone->mWeights[a]; + avPerVertexWeights[weight.mVertexId].push_back( + std::pair(a,weight.mWeight)); + } + } + } + // we need to split this mesh into sub meshes // determine the estimated size of a submesh // (this could be too large. Max waste is a single digit percentage) @@ -382,6 +451,13 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh( pcMesh->mNumVertices = 0; pcMesh->mMaterialIndex = pMesh->mMaterialIndex; + typedef std::vector BoneWeightList; + if (pMesh->HasBones()) + { + pcMesh->mBones = new aiBone*[pMesh->mNumBones]; + ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones); + } + // clear the temporary helper array if (0 != iBase) { @@ -491,7 +567,28 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh( pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex]; } } + // check whether we have bone weights assigned to this vertex rFace.mIndices[v] = pcMesh->mNumVertices; + if (avPerVertexWeights) + { + VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ]; + if( !table.empty() ) + { + for (VertexWeightTable::const_iterator + iter = table.begin(); + iter != table.end();++iter) + { + // allocate the bone weight array if necessary + BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first]; + if (!pcWeightList) + { + pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList()); + } + pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second)); + } + } + } + avWasCopied[iIndex] = pcMesh->mNumVertices; pcMesh->mNumVertices++; } @@ -502,6 +599,36 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh( break; } } + + // check which bones we'll need to create for this submesh + if (pMesh->HasBones()) + { + aiBone** ppCurrent = pcMesh->mBones; + for (unsigned int k = 0; k < pMesh->mNumBones;++k) + { + // check whether the bone is existing + BoneWeightList* pcWeightList; + if (pcWeightList = (BoneWeightList*)pcMesh->mBones[k]) + { + aiBone* pcOldBone = pMesh->mBones[k]; + aiBone* pcOut; + *ppCurrent++ = pcOut = new aiBone(); + pcOut->mName = aiString(pcOldBone->mName); + pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix; + pcOut->mNumWeights = pcWeightList->size(); + pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights]; + + // copy the vertex weights + ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0), + pcOut->mNumWeights * sizeof(aiVertexWeight)); + + // delete the temporary bone weight list + delete pcWeightList; + pcMesh->mNumBones++; + } + } + } + // copy the face list to the mesh pcMesh->mFaces = new aiFace[vFaces.size()]; pcMesh->mNumFaces = (unsigned int)vFaces.size(); @@ -519,6 +646,9 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh( } } + // delete the per-vertex weight list again + delete[] avPerVertexWeights; + // now delete the old mesh data delete pMesh; return; diff --git a/code/SplitLargeMeshes.h b/code/SplitLargeMeshes.h index c6e709cbb..195cdb6dc 100644 --- a/code/SplitLargeMeshes.h +++ b/code/SplitLargeMeshes.h @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/aiMesh.h" #include "../include/aiScene.h" +class SplitLargeMeshesTest; namespace Assimp { @@ -84,6 +85,7 @@ class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess { friend class Importer; friend class SplitLargeMeshesProcess_Vertex; + friend class ::SplitLargeMeshesTest; protected: /** Constructor to be privately used by Importer */ @@ -134,6 +136,7 @@ public: class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess { friend class Importer; + friend class ::SplitLargeMeshesTest; protected: /** Constructor to be privately used by Importer */ diff --git a/test/unit/Main.cpp b/test/unit/Main.cpp index 20b0e6f18..37099c8ef 100644 --- a/test/unit/Main.cpp +++ b/test/unit/Main.cpp @@ -7,9 +7,15 @@ #include #include +#include +#include int main (int argc, char* argv[]) { + // seed the randomizer with the current system time + time_t t;time(&t); + srand((unsigned int)t); + // Informiert Test-Listener ueber Testresultate CPPUNIT_NS :: TestResult testresult; diff --git a/test/unit/utSplitLargeMeshes.cpp b/test/unit/utSplitLargeMeshes.cpp index e69de29bb..9f1a73067 100644 --- a/test/unit/utSplitLargeMeshes.cpp +++ b/test/unit/utSplitLargeMeshes.cpp @@ -0,0 +1,103 @@ +#include "utSplitLargeMeshes.h" +#include "aiPostProcess.h" +#include + +CPPUNIT_TEST_SUITE_REGISTRATION (SplitLargeMeshesTest); + +void SplitLargeMeshesTest :: setUp (void) +{ + aiSetVertexSplitLimit(1000); + aiSetTriangleSplitLimit(1000); + + // construct the processes + this->piProcessTriangle = new SplitLargeMeshesProcess_Triangle(); + this->piProcessVertex = new SplitLargeMeshesProcess_Vertex(); + + this->pcMesh1 = new aiMesh(); + pcMesh1->mNumVertices = 2100; // quersumme: 3 + pcMesh1->mVertices = new aiVector3D[pcMesh1->mNumVertices]; + pcMesh1->mNormals = new aiVector3D[pcMesh1->mNumVertices]; + + pcMesh1->mNumFaces = pcMesh1->mNumVertices / 3; + pcMesh1->mFaces = new aiFace[pcMesh1->mNumFaces]; + + unsigned int qq = 0; + for (unsigned int i = 0; i < pcMesh1->mNumFaces;++i) + { + aiFace& face = pcMesh1->mFaces[i]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + face.mIndices[0] = qq++; + face.mIndices[1] = qq++; + face.mIndices[2] = qq++; + } + + // generate many, many faces with randomized indices for + // the second mesh + this->pcMesh2 = new aiMesh(); + pcMesh2->mNumVertices = 3000; + pcMesh2->mVertices = new aiVector3D[pcMesh2->mNumVertices]; + pcMesh2->mNormals = new aiVector3D[pcMesh2->mNumVertices]; + + pcMesh2->mNumFaces = 10000; + pcMesh2->mFaces = new aiFace[pcMesh2->mNumFaces]; + + for (unsigned int i = 0; i < pcMesh2->mNumFaces;++i) + { + aiFace& face = pcMesh2->mFaces[i]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + face.mIndices[0] = unsigned int((rand() / (float)RAND_MAX) * pcMesh2->mNumVertices); + face.mIndices[1] = unsigned int((rand() / (float)RAND_MAX) * pcMesh2->mNumVertices); + face.mIndices[2] = unsigned int((rand() / (float)RAND_MAX) * pcMesh2->mNumVertices); + } +} + +void SplitLargeMeshesTest :: tearDown (void) +{ + delete this->piProcessTriangle; + delete this->piProcessVertex; +} + +void SplitLargeMeshesTest :: testVertexSplit() +{ + std::vector< std::pair > avOut; + + int iOldFaceNum = (int)pcMesh1->mNumFaces; + piProcessVertex->SplitMesh(0,pcMesh1,avOut); + + for (std::vector< std::pair >::const_iterator + iter = avOut.begin(), end = avOut.end(); + iter != end; ++iter) + { + aiMesh* mesh = (*iter).first; + CPPUNIT_ASSERT(mesh->mNumVertices < 1000); + CPPUNIT_ASSERT(0 != mesh->mNormals && 0 != mesh->mVertices); + + iOldFaceNum -= mesh->mNumFaces; + delete mesh; + } + CPPUNIT_ASSERT(0 == iOldFaceNum); +} + +void SplitLargeMeshesTest :: testTriangleSplit() +{ + std::vector< std::pair > avOut; + + // the number of faces shouldn't change + int iOldFaceNum = (int)pcMesh2->mNumFaces; + piProcessTriangle->SplitMesh(0,pcMesh2,avOut); + + for (std::vector< std::pair >::const_iterator + iter = avOut.begin(), end = avOut.end(); + iter != end; ++iter) + { + aiMesh* mesh = (*iter).first; + CPPUNIT_ASSERT(mesh->mNumFaces < 1000); + CPPUNIT_ASSERT(0 != mesh->mNormals && 0 != mesh->mVertices); + + iOldFaceNum -= mesh->mNumFaces; + delete mesh; + } + CPPUNIT_ASSERT(0 == iOldFaceNum); +} \ No newline at end of file diff --git a/test/unit/utSplitLargeMeshes.h b/test/unit/utSplitLargeMeshes.h new file mode 100644 index 000000000..c63d3c2ba --- /dev/null +++ b/test/unit/utSplitLargeMeshes.h @@ -0,0 +1,41 @@ +#ifndef TESTLM_H +#define TESTLM_H + +#include +#include + +#include +#include +#include +#include + + +using namespace std; +using namespace Assimp; + +class SplitLargeMeshesTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (SplitLargeMeshesTest); + CPPUNIT_TEST (testVertexSplit); + CPPUNIT_TEST (testTriangleSplit); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp (void); + void tearDown (void); + + protected: + + void testVertexSplit (void); + void testTriangleSplit (void); + + + private: + + SplitLargeMeshesProcess_Triangle* piProcessTriangle; + SplitLargeMeshesProcess_Vertex* piProcessVertex; + aiMesh* pcMesh1; + aiMesh* pcMesh2; +}; + +#endif diff --git a/test/unit/utVertexTriangleAdjacency.cpp b/test/unit/utVertexTriangleAdjacency.cpp index c2428688d..7e8e0fba6 100644 --- a/test/unit/utVertexTriangleAdjacency.cpp +++ b/test/unit/utVertexTriangleAdjacency.cpp @@ -2,17 +2,12 @@ #include "utVertexTriangleAdjacency.h" -#include -#include + CPPUNIT_TEST_SUITE_REGISTRATION (VTAdjacency); void VTAdjacency :: setUp (void) { - // seed the randomizer - time_t t;time(&t); - srand((unsigned int)t); - // build a test mesh with randomized input data // ******************************************************************************* pMesh = new aiMesh(); diff --git a/workspaces/vc8/UnitTest.vcproj b/workspaces/vc8/UnitTest.vcproj index a4120bdc7..7330b71ac 100644 --- a/workspaces/vc8/UnitTest.vcproj +++ b/workspaces/vc8/UnitTest.vcproj @@ -753,6 +753,10 @@ RelativePath="..\..\test\unit\utRemoveComments.h" > + +