Optimized LimitBoneWeightsProcess. Added SmallVector to reduce heap allocations. Simplified algorithm and removed unnecessary copying.
parent
0c3933ca7c
commit
d5d30c898b
|
@ -121,6 +121,7 @@ SET( PUBLIC_HEADERS
|
||||||
${HEADER_PATH}/GenericProperty.h
|
${HEADER_PATH}/GenericProperty.h
|
||||||
${HEADER_PATH}/SpatialSort.h
|
${HEADER_PATH}/SpatialSort.h
|
||||||
${HEADER_PATH}/SkeletonMeshBuilder.h
|
${HEADER_PATH}/SkeletonMeshBuilder.h
|
||||||
|
${HEADER_PATH}/SmallVector.h
|
||||||
${HEADER_PATH}/SmoothingGroups.h
|
${HEADER_PATH}/SmoothingGroups.h
|
||||||
${HEADER_PATH}/SmoothingGroups.inl
|
${HEADER_PATH}/SmoothingGroups.inl
|
||||||
${HEADER_PATH}/StandardShapes.h
|
${HEADER_PATH}/StandardShapes.h
|
||||||
|
|
|
@ -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,7 +53,6 @@ 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()
|
||||||
|
@ -76,10 +76,12 @@ bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
// 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");
|
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess begin");
|
||||||
for (unsigned int a = 0; a < pScene->mNumMeshes; ++a ) {
|
|
||||||
ProcessMesh(pScene->mMeshes[a]);
|
for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
|
||||||
|
ProcessMesh(pScene->mMeshes[m]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
|
ASSIMP_LOG_DEBUG("LimitBoneWeightsProcess end");
|
||||||
|
@ -101,101 +103,90 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// collect all bone weights per vertex
|
// collect all bone weights per vertex
|
||||||
typedef std::vector< std::vector< Weight > > WeightsPerVertex;
|
typedef SmallVector<Weight,AI_LMW_MAX_WEIGHTS*2> VertexWeightArray;
|
||||||
|
typedef std::vector<VertexWeightArray> WeightsPerVertex;
|
||||||
WeightsPerVertex vertexWeights(pMesh->mNumVertices);
|
WeightsPerVertex vertexWeights(pMesh->mNumVertices);
|
||||||
|
unsigned int 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[a];
|
const aiBone* bone = pMesh->mBones[b];
|
||||||
for( unsigned int b = 0; b < bone->mNumWeights; b++)
|
for (unsigned int w = 0; w < bone->mNumWeights; ++w)
|
||||||
{
|
{
|
||||||
const aiVertexWeight& w = bone->mWeights[b];
|
const aiVertexWeight& vw = bone->mWeights[w];
|
||||||
vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
|
vertexWeights[vw.mVertexId].push_back(Weight(b, vw.mWeight));
|
||||||
|
maxVertexWeights = std::max(maxVertexWeights, vertexWeights[vw.mVertexId].size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (maxVertexWeights <= mMaxWeights)
|
||||||
|
return;
|
||||||
|
|
||||||
unsigned int removed = 0, old_bones = pMesh->mNumBones;
|
unsigned int removed = 0, old_bones = pMesh->mNumBones;
|
||||||
|
|
||||||
// now cut the weight count if it exceeds the maximum
|
// now cut the weight count if it exceeds the maximum
|
||||||
bool bChanged = false;
|
|
||||||
for (WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
|
for (WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
|
||||||
{
|
{
|
||||||
if (vit->size() <= mMaxWeights)
|
if (vit->size() <= mMaxWeights)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bChanged = true;
|
|
||||||
|
|
||||||
// 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
|
||||||
|
for (unsigned int a = 0; a < pMesh->mNumBones; ++a)
|
||||||
|
{
|
||||||
|
pMesh->mBones[a]->mNumWeights = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// rebuild the vertex weight array for all bones
|
// rebuild the vertex weight array for all bones
|
||||||
typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
|
for (unsigned int a = 0; a < vertexWeights.size(); ++a)
|
||||||
WeightsPerBone boneWeights( pMesh->mNumBones);
|
|
||||||
for( unsigned int a = 0; a < vertexWeights.size(); a++)
|
|
||||||
{
|
{
|
||||||
const std::vector<Weight>& vw = vertexWeights[a];
|
const VertexWeightArray& vw = vertexWeights[a];
|
||||||
for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
|
for (const Weight* 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
|
|
||||||
std::vector<bool> abNoNeed(pMesh->mNumBones,false);
|
|
||||||
bChanged = false;
|
|
||||||
|
|
||||||
for( unsigned int a = 0; a < pMesh->mNumBones; a++)
|
|
||||||
{
|
{
|
||||||
const std::vector<aiVertexWeight>& bw = boneWeights[a];
|
aiBone* bone = pMesh->mBones[it->mBone];
|
||||||
aiBone* bone = pMesh->mBones[a];
|
bone->mWeights[bone->mNumWeights++] = aiVertexWeight(a, it->mWeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( bw.empty() )
|
// remove empty bones
|
||||||
|
unsigned int writeBone = 0;
|
||||||
|
|
||||||
|
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
|
||||||
// copy the weight list. should always be less weights than before, so we don't need a new allocation
|
{
|
||||||
ai_assert( bw.size() <= bone->mNumWeights);
|
delete bone;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pMesh->mNumBones = writeBone;
|
||||||
|
|
||||||
if (!DefaultLogger::isNullLogger()) {
|
if (!DefaultLogger::isNullLogger()) {
|
||||||
ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
|
ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
Open Asset Import Library (assimp)
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006-2020, assimp team
|
||||||
|
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use of this software in source and binary forms,
|
||||||
|
with or without modification, are permitted provided that the
|
||||||
|
following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the
|
||||||
|
following disclaimer in the documentation and/or other
|
||||||
|
materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the assimp team, nor the names of its
|
||||||
|
contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior
|
||||||
|
written permission of the assimp team.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file Defines small vector with inplace storage.
|
||||||
|
Based on CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data Structures" */
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#ifndef AI_SMALLVECTOR_H_INC
|
||||||
|
#define AI_SMALLVECTOR_H_INC
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
# pragma GCC system_header
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Assimp {
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
/** \brief Small vector with inplace storage. Reduces heap allocations when list is shorter
|
||||||
|
than initial capasity
|
||||||
|
*/
|
||||||
|
template<typename T, unsigned int Capasity>
|
||||||
|
class SmallVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SmallVector()
|
||||||
|
: mStorage(mInplaceStorage)
|
||||||
|
, mSize(0)
|
||||||
|
, mCapasity(Capasity)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~SmallVector()
|
||||||
|
{
|
||||||
|
if (mStorage != mInplaceStorage) {
|
||||||
|
delete [] mStorage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back(const T& item)
|
||||||
|
{
|
||||||
|
if (mSize < mCapasity) {
|
||||||
|
mStorage[mSize++] = item;
|
||||||
|
}
|
||||||
|
else push_back_and_grow(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(unsigned int newSize)
|
||||||
|
{
|
||||||
|
if (newSize > mCapasity)
|
||||||
|
grow(newSize);
|
||||||
|
mSize = newSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int size() const
|
||||||
|
{
|
||||||
|
return mSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* begin()
|
||||||
|
{
|
||||||
|
return mStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* end()
|
||||||
|
{
|
||||||
|
return &mStorage[mSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
T* begin() const
|
||||||
|
{
|
||||||
|
return mStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* end() const
|
||||||
|
{
|
||||||
|
return &mStorage[mSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void grow(unsigned int newCapasity)
|
||||||
|
{
|
||||||
|
T* pOldStorage = mStorage;
|
||||||
|
T* pNewStorage = new T[newCapasity];
|
||||||
|
|
||||||
|
std::memcpy(pNewStorage, pOldStorage, mSize * sizeof(T));
|
||||||
|
|
||||||
|
mStorage = pNewStorage;
|
||||||
|
mCapasity = newCapasity;
|
||||||
|
|
||||||
|
if (pOldStorage != mInplaceStorage)
|
||||||
|
delete [] pOldStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_back_and_grow(const T& item)
|
||||||
|
{
|
||||||
|
grow(mCapasity + Capasity);
|
||||||
|
|
||||||
|
mStorage[mSize++] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* mStorage;
|
||||||
|
unsigned int mSize;
|
||||||
|
unsigned int mCapasity;
|
||||||
|
T mInplaceStorage[Capasity];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end namespace Assimp
|
||||||
|
|
||||||
|
#endif // !! AI_SMALLVECTOR_H_INC
|
Loading…
Reference in New Issue