- 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
pull/1/head
ulfjorensen 2008-06-07 23:21:36 +00:00
parent a40ac4eace
commit 492aa8358b
7 changed files with 204 additions and 2 deletions

View File

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

View File

@ -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));
}
}

View File

@ -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

View File

@ -79,7 +79,7 @@ XFileParser::XFileParser( const std::vector<char>& pBuffer)
else if( strncmp( P + 8, "bin ", 4) == 0)
mIsBinaryFormat = true;
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
mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000

View File

@ -125,6 +125,17 @@ enum aiPostProcessSteps
* the normal list will be zeroed.
*/
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
};
// ---------------------------------------------------------------------------

View File

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

View File

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