nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** Implementation of the LimitBoneWeightsProcess post processing step */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #include "LimitBoneWeightsProcess.h" nuclear@0: nuclear@0: nuclear@0: using namespace Assimp; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Constructor to be privately used by Importer nuclear@0: LimitBoneWeightsProcess::LimitBoneWeightsProcess() nuclear@0: { nuclear@0: mMaxWeights = AI_LMW_MAX_WEIGHTS; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Destructor, private as well nuclear@0: LimitBoneWeightsProcess::~LimitBoneWeightsProcess() nuclear@0: { nuclear@0: // nothing to do here nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Returns whether the processing step is present in the given flag field. nuclear@0: bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const nuclear@0: { nuclear@0: return (pFlags & aiProcess_LimitBoneWeights) != 0; nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: void LimitBoneWeightsProcess::Execute( aiScene* pScene) nuclear@0: { nuclear@0: DefaultLogger::get()->debug("LimitBoneWeightsProcess begin"); nuclear@0: for( unsigned int a = 0; a < pScene->mNumMeshes; a++) nuclear@0: ProcessMesh( pScene->mMeshes[a]); nuclear@0: nuclear@0: DefaultLogger::get()->debug("LimitBoneWeightsProcess end"); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Executes the post processing step on the given imported data. nuclear@0: void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) nuclear@0: { nuclear@0: // get the current value of the property nuclear@0: this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Unites identical vertices in the given mesh nuclear@0: void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh) nuclear@0: { nuclear@0: if( !pMesh->HasBones()) nuclear@0: return; nuclear@0: nuclear@0: // collect all bone weights per vertex nuclear@0: typedef std::vector< std::vector< Weight > > WeightsPerVertex; nuclear@0: WeightsPerVertex vertexWeights( pMesh->mNumVertices); nuclear@0: nuclear@0: // collect all weights per vertex nuclear@0: for( unsigned int a = 0; a < pMesh->mNumBones; a++) nuclear@0: { nuclear@0: const aiBone* bone = pMesh->mBones[a]; nuclear@0: for( unsigned int b = 0; b < bone->mNumWeights; b++) nuclear@0: { nuclear@0: const aiVertexWeight& w = bone->mWeights[b]; nuclear@0: vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: unsigned int removed = 0, old_bones = pMesh->mNumBones; nuclear@0: nuclear@0: // now cut the weight count if it exceeds the maximum nuclear@0: bool bChanged = false; nuclear@0: for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit) nuclear@0: { nuclear@0: if( vit->size() <= mMaxWeights) nuclear@0: continue; nuclear@0: nuclear@0: bChanged = true; nuclear@0: nuclear@0: // more than the defined maximum -> first sort by weight in descending order. That's nuclear@0: // why we defined the < operator in such a weird way. nuclear@0: std::sort( vit->begin(), vit->end()); nuclear@0: nuclear@0: // now kill everything beyond the maximum count nuclear@0: unsigned int m = vit->size(); nuclear@0: vit->erase( vit->begin() + mMaxWeights, vit->end()); nuclear@0: removed += m-vit->size(); nuclear@0: nuclear@0: // and renormalize the weights nuclear@0: float sum = 0.0f; nuclear@0: for( std::vector::const_iterator it = vit->begin(); it != vit->end(); ++it) nuclear@0: sum += it->mWeight; nuclear@0: for( std::vector::iterator it = vit->begin(); it != vit->end(); ++it) nuclear@0: it->mWeight /= sum; nuclear@0: } nuclear@0: nuclear@0: if (bChanged) { nuclear@0: // rebuild the vertex weight array for all bones nuclear@0: typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone; nuclear@0: WeightsPerBone boneWeights( pMesh->mNumBones); nuclear@0: for( unsigned int a = 0; a < vertexWeights.size(); a++) nuclear@0: { nuclear@0: const std::vector& vw = vertexWeights[a]; nuclear@0: for( std::vector::const_iterator it = vw.begin(); it != vw.end(); ++it) nuclear@0: boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight)); nuclear@0: } nuclear@0: nuclear@0: // and finally copy the vertex weight list over to the mesh's bones nuclear@0: std::vector abNoNeed(pMesh->mNumBones,false); nuclear@0: bChanged = false; nuclear@0: nuclear@0: for( unsigned int a = 0; a < pMesh->mNumBones; a++) nuclear@0: { nuclear@0: const std::vector& bw = boneWeights[a]; nuclear@0: aiBone* bone = pMesh->mBones[a]; nuclear@0: nuclear@0: // ignore the bone if no vertex weights were removed there nuclear@0: nuclear@0: // FIX (Aramis, 07|22|08) nuclear@0: // NO! we can't ignore it in this case ... it is possible that nuclear@0: // the number of weights did not change, but the weight values did. nuclear@0: nuclear@0: // if( bw.size() == bone->mNumWeights) nuclear@0: // continue; nuclear@0: nuclear@0: // FIX (Aramis, 07|21|08) nuclear@0: // It is possible that all weights of a bone have been removed. nuclear@0: // This would naturally cause an exception in &bw[0]. nuclear@0: if ( bw.empty() ) nuclear@0: { nuclear@0: abNoNeed[a] = bChanged = true; nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // copy the weight list. should always be less weights than before, so we don't need a new allocation nuclear@0: ai_assert( bw.size() <= bone->mNumWeights); nuclear@0: bone->mNumWeights = (unsigned int) bw.size(); nuclear@0: ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight)); nuclear@0: } nuclear@0: nuclear@0: if (bChanged) { nuclear@0: // the number of new bones is smaller than before, so we can reuse the old array nuclear@0: aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur; nuclear@0: nuclear@0: for (std::vector::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) { nuclear@0: if (*iter) { nuclear@0: delete *ppcSrc; nuclear@0: --pMesh->mNumBones; nuclear@0: } nuclear@0: else *ppcCur++ = *ppcSrc; nuclear@0: ++ppcSrc; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (!DefaultLogger::isNullLogger()) { nuclear@0: char buffer[1024]; nuclear@0: ::sprintf(buffer,"Removed %i weights. Input bones: %i. Output bones: %i",removed,old_bones,pMesh->mNumBones); nuclear@0: DefaultLogger::get()->info(buffer); nuclear@0: } nuclear@0: } nuclear@0: }