- 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-9d2fd5bffc1fpull/1/head
parent
a40ac4eace
commit
492aa8358b
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -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"
|
||||||
>
|
>
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue