pull/3042/head
iamAdrianIusca 2020-04-23 22:20:28 +03:00
parent 871f3fd337
commit e5f69d55bb
1 changed files with 92 additions and 101 deletions

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "LimitBoneWeightsProcess.h" #include "LimitBoneWeightsProcess.h"
#include <assimp/SmallVector.h>
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
@ -52,150 +53,140 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
LimitBoneWeightsProcess::LimitBoneWeightsProcess() LimitBoneWeightsProcess::LimitBoneWeightsProcess()
{ {
mMaxWeights = AI_LMW_MAX_WEIGHTS; mMaxWeights = AI_LMW_MAX_WEIGHTS;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
LimitBoneWeightsProcess::~LimitBoneWeightsProcess() LimitBoneWeightsProcess::~LimitBoneWeightsProcess()
{ {
// nothing to do here // nothing to do here
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
{ {
return (pFlags & aiProcess_LimitBoneWeights) != 0; return (pFlags & aiProcess_LimitBoneWeights) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::Execute( aiScene* pScene) { void LimitBoneWeightsProcess::Execute( aiScene* pScene)
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin"); {
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
ProcessMesh(pScene->mMeshes[a]);
}
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end"); for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
ProcessMesh(pScene->mMeshes[m]);
}
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
{ {
// get the current value of the property // get the current value of the property
this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh // Unites identical vertices in the given mesh
void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh) void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh)
{ {
if( !pMesh->HasBones()) if (!pMesh->HasBones())
return; return;
// collect all bone weights per vertex // collect all bone weights per vertex
typedef std::vector< std::vector< Weight > > WeightsPerVertex; typedef SmallVector<Weight,8> VertexWeightArray;
WeightsPerVertex vertexWeights( pMesh->mNumVertices); typedef std::vector<VertexWeightArray> WeightsPerVertex;
WeightsPerVertex vertexWeights(pMesh->mNumVertices);
size_t maxVertexWeights = 0;
// collect all weights per vertex for (unsigned int b = 0; b < pMesh->mNumBones; ++b)
for( unsigned int a = 0; a < pMesh->mNumBones; a++) {
{ const aiBone* bone = pMesh->mBones[b];
const aiBone* bone = pMesh->mBones[a]; for (unsigned int w = 0; w < bone->mNumWeights; ++w)
for( unsigned int b = 0; b < bone->mNumWeights; b++) {
{ const aiVertexWeight& vw = bone->mWeights[w];
const aiVertexWeight& w = bone->mWeights[b]; vertexWeights[vw.mVertexId].push_back(Weight(b, vw.mWeight));
vertexWeights[w.mVertexId].emplace_back( a, w.mWeight); maxVertexWeights = std::max(maxVertexWeights, vertexWeights[vw.mVertexId].size());
} }
} }
unsigned int removed = 0, old_bones = pMesh->mNumBones; if (maxVertexWeights <= mMaxWeights)
return;
// now cut the weight count if it exceeds the maximum unsigned int removed = 0, old_bones = pMesh->mNumBones;
bool bChanged = false;
for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
{
if( vit->size() <= mMaxWeights)
continue;
bChanged = true; // 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 // more than the defined maximum -> first sort by weight in descending order. That's
// why we defined the < operator in such a weird way. // why we defined the < operator in such a weird way.
std::sort( vit->begin(), vit->end()); std::sort(vit->begin(), vit->end());
// now kill everything beyond the maximum count // now kill everything beyond the maximum count
unsigned int m = static_cast<unsigned int>(vit->size()); unsigned int m = static_cast<unsigned int>(vit->size());
vit->erase( vit->begin() + mMaxWeights, vit->end()); vit->resize(mMaxWeights);
removed += static_cast<unsigned int>(m-vit->size()); removed += static_cast<unsigned int>(m - vit->size());
// and renormalize the weights // and renormalize the weights
float sum = 0.0f; float sum = 0.0f;
for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) { for(const Weight* it = vit->begin(); it != vit->end(); ++it) {
sum += it->mWeight; sum += it->mWeight;
} }
if( 0.0f != sum ) { if (0.0f != sum) {
const float invSum = 1.0f / sum; const float invSum = 1.0f / sum;
for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) { for(Weight* it = vit->begin(); it != vit->end(); ++it) {
it->mWeight *= invSum; it->mWeight *= invSum;
} }
} }
} }
if (bChanged) { // clear weight count for all bone
// rebuild the vertex weight array for all bones for (unsigned int a = 0; a < pMesh->mNumBones; ++a)
typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone; {
WeightsPerBone boneWeights( pMesh->mNumBones); pMesh->mBones[a]->mNumWeights = 0;
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].emplace_back( a, it->mWeight);
}
// and finally copy the vertex weight list over to the mesh's bones // rebuild the vertex weight array for all bones
std::vector<bool> abNoNeed(pMesh->mNumBones,false); for (unsigned int a = 0; a < vertexWeights.size(); ++a)
bChanged = false; {
const VertexWeightArray& vw = vertexWeights[a];
for (const Weight* it = vw.begin(); it != vw.end(); ++it)
{
aiBone* bone = pMesh->mBones[it->mBone];
bone->mWeights[bone->mNumWeights++] = aiVertexWeight(a, it->mWeight);
}
}
for( unsigned int a = 0; a < pMesh->mNumBones; a++) // remove empty bones
{ unsigned int writeBone = 0;
const std::vector<aiVertexWeight>& bw = boneWeights[a];
aiBone* bone = pMesh->mBones[a];
if ( bw.empty() ) for (unsigned int readBone = 0; readBone< pMesh->mNumBones; ++readBone)
{ {
abNoNeed[a] = bChanged = true; aiBone* bone = pMesh->mBones[readBone];
continue; if (bone->mNumWeights > 0)
} {
pMesh->mBones[writeBone++] = bone;
}
else
{
delete bone;
}
}
pMesh->mNumBones = writeBone;
// copy the weight list. should always be less weights than before, so we don't need a new allocation if (!DefaultLogger::isNullLogger()) {
ai_assert( bw.size() <= bone->mNumWeights); ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
bone->mNumWeights = static_cast<unsigned int>( bw.size() ); }
::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
}
if (bChanged) {
// the number of new bones is smaller than before, so we can reuse the old array
aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur;
for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
if (*iter) {
delete *ppcSrc;
--pMesh->mNumBones;
}
else *ppcCur++ = *ppcSrc;
++ppcSrc;
}
}
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones );
}
}
} }