Преглед изворни кода

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
aramis_acg пре 17 година
родитељ
комит
76ebdecd7a

+ 130 - 0
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<aiVertexWeight> 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)
 			// (we will also need to copy the array of indices)
 			unsigned int iCurrent = 0;
 			unsigned int iCurrent = 0;
 			for (unsigned int p = 0; p < pcMesh->mNumFaces;++p)
 			for (unsigned int p = 0; p < pcMesh->mNumFaces;++p)
@@ -356,6 +406,25 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh(
 {
 {
 	if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT)
 	if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT)
 	{
 	{
+		typedef std::vector< std::pair<unsigned int,float> > 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<unsigned int,float>(a,weight.mWeight));
+				}
+			}
+		}
+
 		// we need to split this mesh into sub meshes
 		// we need to split this mesh into sub meshes
 		// determine the estimated size of a submesh
 		// determine the estimated size of a submesh
 		// (this could be too large. Max waste is a single digit percentage)
 		// (this could be too large. Max waste is a single digit percentage)
@@ -382,6 +451,13 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh(
 			pcMesh->mNumVertices	= 0;
 			pcMesh->mNumVertices	= 0;
 			pcMesh->mMaterialIndex	= pMesh->mMaterialIndex;
 			pcMesh->mMaterialIndex	= pMesh->mMaterialIndex;
 
 
+			typedef std::vector<aiVertexWeight> BoneWeightList;
+			if (pMesh->HasBones())
+			{
+				pcMesh->mBones = new aiBone*[pMesh->mNumBones];
+				::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones);
+			}
+
 			// clear the temporary helper array
 			// clear the temporary helper array
 			if (0 != iBase)
 			if (0 != iBase)
 			{
 			{
@@ -491,7 +567,28 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh(
 							pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex];
 							pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex];
 						}
 						}
 					}
 					}
+					// check whether we have bone weights assigned to this vertex
 					rFace.mIndices[v] = pcMesh->mNumVertices;
 					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;
 					avWasCopied[iIndex] = pcMesh->mNumVertices;
 					pcMesh->mNumVertices++;
 					pcMesh->mNumVertices++;
 				}
 				}
@@ -502,6 +599,36 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh(
 					break;
 					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
 			// copy the face list to the mesh
 			pcMesh->mFaces = new aiFace[vFaces.size()];
 			pcMesh->mFaces = new aiFace[vFaces.size()];
 			pcMesh->mNumFaces = (unsigned int)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
 		// now delete the old mesh data
 		delete pMesh;
 		delete pMesh;
 		return;
 		return;

+ 3 - 0
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/aiMesh.h"
 #include "../include/aiScene.h"
 #include "../include/aiScene.h"
 
 
+class SplitLargeMeshesTest;
 namespace Assimp
 namespace Assimp
 {
 {
 
 
@@ -84,6 +85,7 @@ class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess
 {
 {
 	friend class Importer;
 	friend class Importer;
 	friend class SplitLargeMeshesProcess_Vertex;
 	friend class SplitLargeMeshesProcess_Vertex;
+	friend class ::SplitLargeMeshesTest;
 
 
 protected:
 protected:
 	/** Constructor to be privately used by Importer */
 	/** Constructor to be privately used by Importer */
@@ -134,6 +136,7 @@ public:
 class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess
 class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess
 {
 {
 	friend class Importer;
 	friend class Importer;
+	friend class ::SplitLargeMeshesTest;
 
 
 protected:
 protected:
 	/** Constructor to be privately used by Importer */
 	/** Constructor to be privately used by Importer */

+ 6 - 0
test/unit/Main.cpp

@@ -7,9 +7,15 @@
 #include <cppunit/TestRunner.h>
 #include <cppunit/TestRunner.h>
 #include <cppunit/BriefTestProgressListener.h>
 #include <cppunit/BriefTestProgressListener.h>
 
 
+#include <math.h>
+#include <time.h>
 
 
 int main (int argc, char* argv[])
 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
     // Informiert Test-Listener ueber Testresultate
     CPPUNIT_NS :: TestResult testresult;
     CPPUNIT_NS :: TestResult testresult;
 
 

+ 103 - 0
test/unit/utSplitLargeMeshes.cpp

@@ -0,0 +1,103 @@
+#include "utSplitLargeMeshes.h"
+#include "aiPostProcess.h"
+#include <math.h>
+
+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<aiMesh*, unsigned int> > avOut;
+
+	int iOldFaceNum = (int)pcMesh1->mNumFaces;
+	piProcessVertex->SplitMesh(0,pcMesh1,avOut);
+
+	for (std::vector< std::pair<aiMesh*, unsigned int> >::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<aiMesh*, unsigned int> > avOut;
+
+	// the number of faces shouldn't change
+	int iOldFaceNum = (int)pcMesh2->mNumFaces;
+	piProcessTriangle->SplitMesh(0,pcMesh2,avOut);
+
+	for (std::vector< std::pair<aiMesh*, unsigned int> >::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);
+}

+ 41 - 0
test/unit/utSplitLargeMeshes.h

@@ -0,0 +1,41 @@
+#ifndef TESTLM_H
+#define TESTLM_H
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+#include <aiTypes.h>
+#include <aiMesh.h>
+#include <aiScene.h>
+#include <SplitLargeMeshes.h>
+
+
+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 

+ 1 - 6
test/unit/utVertexTriangleAdjacency.cpp

@@ -2,17 +2,12 @@
 
 
 
 
 #include "utVertexTriangleAdjacency.h"
 #include "utVertexTriangleAdjacency.h"
-#include <math.h>
-#include <time.h>
+
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION (VTAdjacency);
 CPPUNIT_TEST_SUITE_REGISTRATION (VTAdjacency);
 
 
 void VTAdjacency :: setUp (void)
 void VTAdjacency :: setUp (void)
 {
 {
-	// seed the randomizer
-	time_t t;time(&t);
-	srand((unsigned int)t);
-
 	// build a test mesh with randomized input data
 	// build a test mesh with randomized input data
 	// *******************************************************************************
 	// *******************************************************************************
 	pMesh = new aiMesh();
 	pMesh = new aiMesh();

+ 4 - 0
workspaces/vc8/UnitTest.vcproj

@@ -753,6 +753,10 @@
 				RelativePath="..\..\test\unit\utRemoveComments.h"
 				RelativePath="..\..\test\unit\utRemoveComments.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\test\unit\utSplitLargeMeshes.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\test\unit\utVertexTriangleAdjacency.h"
 				RelativePath="..\..\test\unit\utVertexTriangleAdjacency.h"
 				>
 				>