Browse Source

- added a post processing step to limit the bone count per vertex

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@56 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
ulfjorensen 17 năm trước cách đây
mục cha
commit
492aa8358b

+ 2 - 0
code/Importer.cpp

@@ -89,6 +89,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "KillNormalsProcess.h"
 #include "KillNormalsProcess.h"
 #include "SplitLargeMeshes.h"
 #include "SplitLargeMeshes.h"
 #include "PretransformVertices.h"
 #include "PretransformVertices.h"
+#include "LimitBoneWeightsProcess.h"
 #include "../include/DefaultLogger.h"
 #include "../include/DefaultLogger.h"
 
 
 using namespace Assimp;
 using namespace Assimp;
@@ -143,6 +144,7 @@ Importer::Importer() :
 	mPostProcessingSteps.push_back( new JoinVerticesProcess());
 	mPostProcessingSteps.push_back( new JoinVerticesProcess());
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
 	mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
 	mPostProcessingSteps.push_back( new ConvertToLHProcess());
 	mPostProcessingSteps.push_back( new ConvertToLHProcess());
+	mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 109 - 0
code/LimitBoneWeightsProcess.cpp

@@ -0,0 +1,109 @@
+/** Implementation of the LimitBoneWeightsProcess post processing step */
+
+#include <vector>
+#include <assert.h>
+#include "LimitBoneWeightsProcess.h"
+#include "../include/aiPostProcess.h"
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LimitBoneWeightsProcess::LimitBoneWeightsProcess()
+{
+	// TODO: (thom) make this configurable from somewhere?
+	mMaxWeights = 4;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LimitBoneWeightsProcess::~LimitBoneWeightsProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_LimitBoneWeights) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void LimitBoneWeightsProcess::Execute( aiScene* pScene)
+{
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+		ProcessMesh( pScene->mMeshes[a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
+{
+	if( !pMesh->HasBones())
+		return;
+
+	// collect all bone weights per vertex
+	typedef std::vector< std::vector< Weight > > WeightsPerVertex;
+	WeightsPerVertex vertexWeights( pMesh->mNumVertices);
+
+	// collect all weights per vertex
+	for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+	{
+		const aiBone* bone = pMesh->mBones[a];
+		for( unsigned int b = 0; b < bone->mNumWeights; b++)
+		{
+			const aiVertexWeight& w = bone->mWeights[b];
+			vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
+		}
+	}
+
+	// now cut the weight count if it exceeds the maximum
+	for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
+	{
+		if( vit->size() <= mMaxWeights)
+			continue;
+
+		// more than the defined maximum -> first sort by weight in descending order. That's 
+		// why we defined the < operator in such a weird way.
+		std::sort( vit->begin(), vit->end());
+
+		// now kill everything beyond the maximum count
+		vit->erase( vit->begin() + mMaxWeights, vit->end());
+
+		// and renormalize the weights
+		float sum = 0.0f;
+		for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it)
+			sum += it->mWeight;
+		for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it)
+			it->mWeight /= sum;
+	}
+
+	// rebuild the vertex weight array for all bones 
+	typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
+	WeightsPerBone boneWeights( pMesh->mNumBones);
+	for( unsigned int a = 0; a < vertexWeights.size(); a++)
+	{
+		const std::vector<Weight>& vw = vertexWeights[a];
+		for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
+			boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight));
+	}
+
+	// and finally copy the vertex weight list over to the mesh's bones
+	for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+	{
+		const std::vector<aiVertexWeight>& bw = boneWeights[a];
+		aiBone* bone = pMesh->mBones[a];
+		// ignore the bone if no vertex weights were removed there
+		if( bw.size() == bone->mNumWeights)
+			continue;
+
+		// copy the weight list. should always be less weights than before, so we don't need a new allocation
+		assert( bw.size() < bone->mNumWeights);
+		bone->mNumWeights = (unsigned int) bw.size();
+		memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
+	}
+}

+ 71 - 0
code/LimitBoneWeightsProcess.h

@@ -0,0 +1,71 @@
+/** Defines a post processing step to limit the number of bones affecting a single vertex. */
+#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
+#define AI_LIMITBONEWEIGHTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** This post processing step limits the number of bones affecting a vertex
+* to a certain maximum value. If a vertex is affected by more than that number
+* of bones, the bone weight with the least influence on this vertex are removed.
+* The other weights on this bone are then renormalized to assure the sum weight
+* to be 1.
+*/
+class LimitBoneWeightsProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	LimitBoneWeightsProcess();
+
+	/** Destructor, private as well */
+	~LimitBoneWeightsProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag field.
+	* @param pFlags The processing flags the importer was called with. A bitwise
+	*   combination of #aiPostProcessSteps.
+	* @return true if the process is present in this flag fields, false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+protected:
+	// -------------------------------------------------------------------
+	/** Limits the bone weight count for all vertices in the given mesh.
+	* @param pMesh The mesh to process.
+	*/
+	void ProcessMesh( aiMesh* pMesh);
+
+protected:
+	/** Describes a bone weight on a vertex */
+	struct Weight
+	{
+		unsigned int mBone; ///< Index of the bone
+		float mWeight;      ///< Weight of that bone on this vertex
+		Weight() { }
+		Weight( unsigned int pBone, float pWeight) { mBone = pBone; mWeight = pWeight; }
+		/** Comparision operator to sort bone weights by descending weight */
+		bool operator < (const Weight& pWeight) const { return mWeight > pWeight.mWeight; }
+	};
+
+	/** Maximum number of bones influencing any single vertex. */
+	unsigned int mMaxWeights;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC

+ 1 - 1
code/XFileParser.cpp

@@ -79,7 +79,7 @@ XFileParser::XFileParser( const std::vector<char>& pBuffer)
 	else if( strncmp( P + 8, "bin ", 4) == 0)
 	else if( strncmp( P + 8, "bin ", 4) == 0)
 		mIsBinaryFormat = true;
 		mIsBinaryFormat = true;
 	else
 	else
-		ThrowException( "Unsupported xfile format");
+    ThrowException( boost::str( boost::format( "Unsupported xfile format '%c%c%c%c'") % P[8] % P[9] % P[10] % P[11]));
 
 
 	// float size
 	// float size
 	mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000
 	mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000

+ 11 - 0
include/aiPostProcess.h

@@ -125,6 +125,17 @@ enum aiPostProcessSteps
 	* the normal list will be zeroed.
 	* the normal list will be zeroed.
 	*/
 	*/
 	aiProcess_PreTransformVertices = 0x100,
 	aiProcess_PreTransformVertices = 0x100,
+
+	/** Limits the number of bones simultaneously affecting a single vertex
+	* to a maximum value. If any vertex is affected by more than that number
+	* of bones, the least important vertex weights are removed and the remaining
+	* vertex weights are renormalized so that the weights still sum up to 1.
+	* At the moment the maximum bone count is hardcoded to 4.
+	*
+	* If you intend to perform the skinning in hardware, this post processing step
+	* might be of interest for you.
+	*/
+	aiProcess_LimitBoneWeights = 0x200
 };
 };
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------

+ 9 - 0
workspaces/vc8/assimp.vcproj

@@ -48,6 +48,7 @@
 				RuntimeLibrary="1"
 				RuntimeLibrary="1"
 				EnableFunctionLevelLinking="true"
 				EnableFunctionLevelLinking="true"
 				WarningLevel="3"
 				WarningLevel="3"
+				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="4"
 				DebugInformationFormat="4"
 			/>
 			/>
 			<Tool
 			<Tool
@@ -1012,6 +1013,10 @@
 				RelativePath="..\..\code\KillNormalsProcess.h"
 				RelativePath="..\..\code\KillNormalsProcess.h"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\LimitBoneWeightsProcess.h"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\MaterialSystem.h"
 				RelativePath="..\..\code\MaterialSystem.h"
 				>
 				>
@@ -1300,6 +1305,10 @@
 				RelativePath="..\..\code\KillNormalsProcess.cpp"
 				RelativePath="..\..\code\KillNormalsProcess.cpp"
 				>
 				>
 			</File>
 			</File>
+			<File
+				RelativePath="..\..\code\LimitBoneWeightsProcess.cpp"
+				>
+			</File>
 			<File
 			<File
 				RelativePath="..\..\code\MaterialSystem.cpp"
 				RelativePath="..\..\code\MaterialSystem.cpp"
 				>
 				>

+ 1 - 1
workspaces/vc8/assimp_view.vcproj

@@ -52,7 +52,7 @@
 				EnableFunctionLevelLinking="true"
 				EnableFunctionLevelLinking="true"
 				UsePrecompiledHeader="2"
 				UsePrecompiledHeader="2"
 				WarningLevel="3"
 				WarningLevel="3"
-				Detect64BitPortabilityProblems="false"
+				Detect64BitPortabilityProblems="true"
 				DebugInformationFormat="4"
 				DebugInformationFormat="4"
 			/>
 			/>
 			<Tool
 			<Tool