فهرست منبع

Merge pull request #3085 from napina/optimized_bonelimits

Optimized LimitBoneWeightsProcess.
Kim Kulling 5 سال پیش
والد
کامیت
e3256303f5
3فایلهای تغییر یافته به همراه222 افزوده شده و 66 حذف شده
  1. 1 0
      code/CMakeLists.txt
  2. 57 66
      code/PostProcessing/LimitBoneWeightsProcess.cpp
  3. 164 0
      include/assimp/SmallVector.h

+ 1 - 0
code/CMakeLists.txt

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

+ 57 - 66
code/PostProcessing/LimitBoneWeightsProcess.cpp

@@ -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");
@@ -95,107 +97,96 @@ void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // 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;
-    WeightsPerVertex vertexWeights( pMesh->mNumVertices);
+    typedef SmallVector<Weight,8> VertexWeightArray;
+    typedef std::vector<VertexWeightArray> WeightsPerVertex;
+    WeightsPerVertex vertexWeights(pMesh->mNumVertices);
+    size_t maxVertexWeights = 0;
 
 
-    // collect all weights per vertex
-    for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+    for (unsigned int b = 0; b < pMesh->mNumBones; ++b)
     {
     {
-        const aiBone* bone = pMesh->mBones[a];
-        for( unsigned int b = 0; b < bone->mNumWeights; b++)
+        const aiBone* bone = pMesh->mBones[b];
+        for (unsigned int w = 0; w < bone->mNumWeights; ++w)
         {
         {
-            const aiVertexWeight& w = bone->mWeights[b];
-            vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
+            const aiVertexWeight& vw = bone->mWeights[w];
+            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());
-        removed += static_cast<unsigned int>(m-vit->size());
+        vit->resize(mMaxWeights);
+        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)   {
-        // 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++)
+    // 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
+    for (unsigned int a = 0; a < vertexWeights.size(); ++a)
+    {
+        const VertexWeightArray& vw = vertexWeights[a];
+        for (const Weight* it = vw.begin(); it != vw.end(); ++it)
         {
         {
-            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));
+            aiBone* bone = pMesh->mBones[it->mBone];
+            bone->mWeights[bone->mNumWeights++] = 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;
+    // remove empty bones
+    unsigned int writeBone = 0;
 
 
-        for( unsigned int a = 0; a < pMesh->mNumBones; a++)
+    for (unsigned int readBone = 0; readBone< pMesh->mNumBones; ++readBone)
+    {
+        aiBone* bone = pMesh->mBones[readBone];
+        if (bone->mNumWeights > 0)
         {
         {
-            const std::vector<aiVertexWeight>& bw = boneWeights[a];
-            aiBone* bone = pMesh->mBones[a];
-
-            if ( bw.empty() )
-            {
-                abNoNeed[a] = bChanged = true;
-                continue;
-            }
-
-            // 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);
-            bone->mNumWeights = static_cast<unsigned int>( bw.size() );
-            ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
+            pMesh->mBones[writeBone++] = bone;
         }
         }
-
-        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;
-            }
+        else
+        {
+            delete bone;
         }
         }
+    }
+    pMesh->mNumBones = writeBone;
 
 
-        if (!DefaultLogger::isNullLogger()) {
-            ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones );
-        }
+    if (!DefaultLogger::isNullLogger()) {
+        ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);
     }
     }
 }
 }

+ 164 - 0
include/assimp/SmallVector.h

@@ -0,0 +1,164 @@
+/*
+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. It uses a small array for a dedicated size.
+/// When the growing gets bigger than this small cache a dynamic growing algorithm will be 
+/// used.
+// --------------------------------------------------------------------------------------------
+template<typename T, unsigned int Capacity>
+class SmallVector {
+public:
+    /// @brief  The default class constructor.
+    SmallVector() : 
+            mStorage(mInplaceStorage),
+            mSize(0),
+            mCapacity(Capacity) {
+        // empty
+    }
+
+    /// @brief  The class destructor.
+    ~SmallVector() {
+        if (mStorage != mInplaceStorage) {
+            delete [] mStorage;
+        }
+    }
+
+    /// @brief  Will push a new item. The capacity will grow in case of a too small capacity.
+    /// @param  item    [in] The item to push at the end of the vector.
+    void push_back(const T& item) {
+        if (mSize < mCapacity) {
+            mStorage[mSize++] = item;
+            return;
+        }
+        
+        push_back_and_grow(item);
+    }
+
+    /// @brief  Will resize the vector.
+    /// @param  newSize     [in] The new size.
+    void resize(size_t newSize) {
+        if (newSize > mCapacity) {
+            grow(newSize);
+        }
+        mSize = newSize;
+    }
+
+    /// @brief  Returns the current size of the vector.
+    /// @return The current size.
+    size_t size() const {
+        return mSize;
+    }
+
+    /// @brief  Returns a pointer to the first item.
+    /// @return The first item as a pointer.
+    T* begin() {
+        return mStorage;
+    }
+
+    /// @brief  Returns a pointer to the end.
+    /// @return The end as a pointer.
+    T* end() {
+        return &mStorage[mSize];
+    }
+
+    /// @brief  Returns a const pointer to the first item.
+    /// @return The first item as a const pointer.
+    T* begin() const {
+        return mStorage;
+    }
+
+    /// @brief  Returns a const pointer to the end.
+    /// @return The end as a const pointer.
+    T* end() const {
+        return &mStorage[mSize];
+    }
+
+    SmallVector(const SmallVector &) = delete;
+    SmallVector(SmallVector &&) = delete;
+    SmallVector &operator = (const SmallVector &) = delete;
+    SmallVector &operator = (SmallVector &&) = delete;
+
+private:
+    void grow( size_t newCapacity) {
+        T* oldStorage = mStorage;
+        T* newStorage = new T[newCapacity];
+
+        std::memcpy(newStorage, oldStorage, mSize * sizeof(T));
+
+        mStorage = newStorage;
+        mCapacity = newCapacity;
+
+        if (oldStorage != mInplaceStorage) {
+            delete [] oldStorage;
+        }
+    }
+
+    void push_back_and_grow(const T& item) {
+        grow(mCapacity + Capacity);
+
+        mStorage[mSize++] = item;
+    }
+
+    T* mStorage;
+    size_t mSize;
+    size_t mCapacity;
+    T mInplaceStorage[Capacity];
+};
+
+} // end namespace Assimp
+
+#endif // !! AI_SMALLVECTOR_H_INC