1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42/** Implementation of the LimitBoneWeightsProcess post processing step */
43
44
45#include "LimitBoneWeightsProcess.h"
46#include "StringUtils.h"
47#include <assimp/postprocess.h>
48#include <assimp/DefaultLogger.hpp>
49#include <assimp/scene.h>
50#include <stdio.h>
51
52using namespace Assimp;
53
54
55// ------------------------------------------------------------------------------------------------
56// Constructor to be privately used by Importer
57LimitBoneWeightsProcess::LimitBoneWeightsProcess()
58{
59 mMaxWeights = AI_LMW_MAX_WEIGHTS;
60}
61
62// ------------------------------------------------------------------------------------------------
63// Destructor, private as well
64LimitBoneWeightsProcess::~LimitBoneWeightsProcess()
65{
66 // nothing to do here
67}
68
69// ------------------------------------------------------------------------------------------------
70// Returns whether the processing step is present in the given flag field.
71bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
72{
73 return (pFlags & aiProcess_LimitBoneWeights) != 0;
74}
75
76// ------------------------------------------------------------------------------------------------
77// Executes the post processing step on the given imported data.
78void LimitBoneWeightsProcess::Execute( aiScene* pScene)
79{
80 DefaultLogger::get()->debug("LimitBoneWeightsProcess begin");
81 for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
82 ProcessMesh( pScene->mMeshes[a]);
83
84 DefaultLogger::get()->debug("LimitBoneWeightsProcess end");
85}
86
87// ------------------------------------------------------------------------------------------------
88// Executes the post processing step on the given imported data.
89void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
90{
91 // get the current value of the property
92 this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
93}
94
95// ------------------------------------------------------------------------------------------------
96// Unites identical vertices in the given mesh
97void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
98{
99 if( !pMesh->HasBones())
100 return;
101
102 // collect all bone weights per vertex
103 typedef std::vector< std::vector< Weight > > WeightsPerVertex;
104 WeightsPerVertex vertexWeights( pMesh->mNumVertices);
105
106 // collect all weights per vertex
107 for( unsigned int a = 0; a < pMesh->mNumBones; a++)
108 {
109 const aiBone* bone = pMesh->mBones[a];
110 for( unsigned int b = 0; b < bone->mNumWeights; b++)
111 {
112 const aiVertexWeight& w = bone->mWeights[b];
113 vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
114 }
115 }
116
117 unsigned int removed = 0, old_bones = pMesh->mNumBones;
118
119 // now cut the weight count if it exceeds the maximum
120 bool bChanged = false;
121 for( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
122 {
123 if( vit->size() <= mMaxWeights)
124 continue;
125
126 bChanged = true;
127
128 // more than the defined maximum -> first sort by weight in descending order. That's
129 // why we defined the < operator in such a weird way.
130 std::sort( vit->begin(), vit->end());
131
132 // now kill everything beyond the maximum count
133 unsigned int m = static_cast<unsigned int>(vit->size());
134 vit->erase( vit->begin() + mMaxWeights, vit->end());
135 removed += static_cast<unsigned int>(m-vit->size());
136
137 // and renormalize the weights
138 float sum = 0.0f;
139 for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) {
140 sum += it->mWeight;
141 }
142 if( 0.0f != sum ) {
143 const float invSum = 1.0f / sum;
144 for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) {
145 it->mWeight *= invSum;
146 }
147 }
148 }
149
150 if (bChanged) {
151 // rebuild the vertex weight array for all bones
152 typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
153 WeightsPerBone boneWeights( pMesh->mNumBones);
154 for( unsigned int a = 0; a < vertexWeights.size(); a++)
155 {
156 const std::vector<Weight>& vw = vertexWeights[a];
157 for( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
158 boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight));
159 }
160
161 // and finally copy the vertex weight list over to the mesh's bones
162 std::vector<bool> abNoNeed(pMesh->mNumBones,false);
163 bChanged = false;
164
165 for( unsigned int a = 0; a < pMesh->mNumBones; a++)
166 {
167 const std::vector<aiVertexWeight>& bw = boneWeights[a];
168 aiBone* bone = pMesh->mBones[a];
169
170 if ( bw.empty() )
171 {
172 abNoNeed[a] = bChanged = true;
173 continue;
174 }
175
176 // copy the weight list. should always be less weights than before, so we don't need a new allocation
177 ai_assert( bw.size() <= bone->mNumWeights);
178 bone->mNumWeights = static_cast<unsigned int>( bw.size() );
179 ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
180 }
181
182 if (bChanged) {
183 // the number of new bones is smaller than before, so we can reuse the old array
184 aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur;
185
186 for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
187 if (*iter) {
188 delete *ppcSrc;
189 --pMesh->mNumBones;
190 }
191 else *ppcCur++ = *ppcSrc;
192 ++ppcSrc;
193 }
194 }
195
196 if (!DefaultLogger::isNullLogger()) {
197 char buffer[1024];
198 ai_snprintf(buffer,1024,"Removed %u weights. Input bones: %u. Output bones: %u",removed,old_bones,pMesh->mNumBones);
199 DefaultLogger::get()->info(buffer);
200 }
201 }
202}
203