vrshoot

view libs/assimp/SplitByBoneCountProcess.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
5 Copyright (c) 2006-2012, assimp team
6 All rights reserved.
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 ----------------------------------------------------------------------
39 */
42 /// @file SplitByBoneCountProcess.cpp
43 /// Implementation of the SplitByBoneCount postprocessing step
45 #include "AssimpPCH.h"
47 // internal headers of the post-processing framework
48 #include "SplitByBoneCountProcess.h"
50 #include <limits>
52 using namespace Assimp;
54 // ------------------------------------------------------------------------------------------------
55 // Constructor
56 SplitByBoneCountProcess::SplitByBoneCountProcess()
57 {
58 // set default, might be overriden by importer config
59 mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
60 }
62 // ------------------------------------------------------------------------------------------------
63 // Destructor
64 SplitByBoneCountProcess::~SplitByBoneCountProcess()
65 {
66 // nothing to do here
67 }
69 // ------------------------------------------------------------------------------------------------
70 // Returns whether the processing step is present in the given flag.
71 bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
72 {
73 return !!(pFlags & aiProcess_SplitByBoneCount);
74 }
76 // ------------------------------------------------------------------------------------------------
77 // Updates internal properties
78 void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
79 {
80 mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
81 }
83 // ------------------------------------------------------------------------------------------------
84 // Executes the post processing step on the given imported data.
85 void SplitByBoneCountProcess::Execute( aiScene* pScene)
86 {
87 DefaultLogger::get()->debug("SplitByBoneCountProcess begin");
89 // early out
90 bool isNecessary = false;
91 for( size_t a = 0; a < pScene->mNumMeshes; ++a)
92 if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
93 isNecessary = true;
95 if( !isNecessary )
96 {
97 DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess early-out: no meshes with more than %d bones.") % mMaxBoneCount));
98 return;
99 }
101 // we need to do something. Let's go.
102 mSubMeshIndices.clear();
103 mSubMeshIndices.resize( pScene->mNumMeshes);
105 // build a new array of meshes for the scene
106 std::vector<aiMesh*> meshes;
108 for( size_t a = 0; a < pScene->mNumMeshes; ++a)
109 {
110 aiMesh* srcMesh = pScene->mMeshes[a];
112 std::vector<aiMesh*> newMeshes;
113 SplitMesh( pScene->mMeshes[a], newMeshes);
115 // mesh was split
116 if( !newMeshes.empty() )
117 {
118 // store new meshes and indices of the new meshes
119 for( size_t b = 0; b < newMeshes.size(); ++b)
120 {
121 mSubMeshIndices[a].push_back( meshes.size());
122 meshes.push_back( newMeshes[b]);
123 }
125 // and destroy the source mesh. It should be completely contained inside the new submeshes
126 delete srcMesh;
127 }
128 else
129 {
130 // Mesh is kept unchanged - store it's new place in the mesh array
131 mSubMeshIndices[a].push_back( meshes.size());
132 meshes.push_back( srcMesh);
133 }
134 }
136 // rebuild the scene's mesh array
137 pScene->mNumMeshes = meshes.size();
138 delete [] pScene->mMeshes;
139 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
140 std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
142 // recurse through all nodes and translate the node's mesh indices to fit the new mesh array
143 UpdateNode( pScene->mRootNode);
145 DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess end: split %d meshes into %d submeshes.") % mSubMeshIndices.size() % meshes.size()));
146 }
148 // ------------------------------------------------------------------------------------------------
149 // Splits the given mesh by bone count.
150 void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
151 {
152 // skip if not necessary
153 if( pMesh->mNumBones <= mMaxBoneCount )
154 return;
156 // necessary optimisation: build a list of all affecting bones for each vertex
157 // TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
158 typedef std::pair<size_t, float> BoneWeight;
159 std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
160 for( size_t a = 0; a < pMesh->mNumBones; ++a)
161 {
162 const aiBone* bone = pMesh->mBones[a];
163 for( size_t b = 0; b < bone->mNumWeights; ++b)
164 vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
165 }
167 size_t numFacesHandled = 0;
168 std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
169 while( numFacesHandled < pMesh->mNumFaces )
170 {
171 // which bones are used in the current submesh
172 size_t numBones = 0;
173 std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
174 // indices of the faces which are going to go into this submesh
175 std::vector<size_t> subMeshFaces;
176 subMeshFaces.reserve( pMesh->mNumFaces);
177 // accumulated vertex count of all the faces in this submesh
178 size_t numSubMeshVertices = 0;
179 // a small local array of new bones for the current face. State of all used bones for that face
180 // can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
181 std::vector<size_t> newBonesAtCurrentFace;
183 // add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
184 for( size_t a = 0; a < pMesh->mNumFaces; ++a)
185 {
186 // skip if the face is already stored in a submesh
187 if( isFaceHandled[a] )
188 continue;
190 const aiFace& face = pMesh->mFaces[a];
191 // check every vertex if its bones would still fit into the current submesh
192 for( size_t b = 0; b < face.mNumIndices; ++b )
193 {
194 const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
195 for( size_t c = 0; c < vb.size(); ++c)
196 {
197 size_t boneIndex = vb[c].first;
198 // if the bone is already used in this submesh, it's ok
199 if( isBoneUsed[boneIndex] )
200 continue;
202 // if it's not used, yet, we would need to add it. Store its bone index
203 if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() )
204 newBonesAtCurrentFace.push_back( boneIndex);
205 }
206 }
208 // leave out the face if the new bones required for this face don't fit the bone count limit anymore
209 if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
210 continue;
212 // mark all new bones as necessary
213 while( !newBonesAtCurrentFace.empty() )
214 {
215 size_t newIndex = newBonesAtCurrentFace.back();
216 newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear()
217 if( isBoneUsed[newIndex] )
218 continue;
220 isBoneUsed[newIndex] = true;
221 numBones++;
222 }
224 // store the face index and the vertex count
225 subMeshFaces.push_back( a);
226 numSubMeshVertices += face.mNumIndices;
228 // remember that this face is handled
229 isFaceHandled[a] = true;
230 numFacesHandled++;
231 }
233 // create a new mesh to hold this subset of the source mesh
234 aiMesh* newMesh = new aiMesh;
235 if( pMesh->mName.length > 0 )
236 newMesh->mName.Set( boost::str( boost::format( "%s_sub%d") % pMesh->mName.data % poNewMeshes.size()));
237 newMesh->mMaterialIndex = pMesh->mMaterialIndex;
238 newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
239 poNewMeshes.push_back( newMesh);
241 // create all the arrays for this mesh if the old mesh contained them
242 newMesh->mNumVertices = numSubMeshVertices;
243 newMesh->mNumFaces = subMeshFaces.size();
244 newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
245 if( pMesh->HasNormals() )
246 newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
247 if( pMesh->HasTangentsAndBitangents() )
248 {
249 newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
250 newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
251 }
252 for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
253 {
254 if( pMesh->HasTextureCoords( a) )
255 newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
256 newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
257 }
258 for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
259 {
260 if( pMesh->HasVertexColors( a) )
261 newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
262 }
264 // and copy over the data, generating faces with linear indices along the way
265 newMesh->mFaces = new aiFace[subMeshFaces.size()];
266 size_t nvi = 0; // next vertex index
267 std::vector<size_t> previousVertexIndices( numSubMeshVertices, std::numeric_limits<size_t>::max()); // per new vertex: its index in the source mesh
268 for( size_t a = 0; a < subMeshFaces.size(); ++a )
269 {
270 const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
271 aiFace& dstFace = newMesh->mFaces[a];
272 dstFace.mNumIndices = srcFace.mNumIndices;
273 dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
275 // accumulate linearly all the vertices of the source face
276 for( size_t b = 0; b < dstFace.mNumIndices; ++b )
277 {
278 size_t srcIndex = srcFace.mIndices[b];
279 dstFace.mIndices[b] = nvi;
280 previousVertexIndices[nvi] = srcIndex;
282 newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
283 if( pMesh->HasNormals() )
284 newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
285 if( pMesh->HasTangentsAndBitangents() )
286 {
287 newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
288 newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
289 }
290 for( size_t c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
291 {
292 if( pMesh->HasTextureCoords( c) )
293 newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
294 }
295 for( size_t c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
296 {
297 if( pMesh->HasVertexColors( c) )
298 newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
299 }
301 nvi++;
302 }
303 }
305 ai_assert( nvi == numSubMeshVertices );
307 // Create the bones for the new submesh: first create the bone array
308 newMesh->mNumBones = 0;
309 newMesh->mBones = new aiBone*[numBones];
311 std::vector<size_t> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<size_t>::max());
312 for( size_t a = 0; a < pMesh->mNumBones; ++a )
313 {
314 if( !isBoneUsed[a] )
315 continue;
317 // create the new bone
318 const aiBone* srcBone = pMesh->mBones[a];
319 aiBone* dstBone = new aiBone;
320 mappedBoneIndex[a] = newMesh->mNumBones;
321 newMesh->mBones[newMesh->mNumBones++] = dstBone;
322 dstBone->mName = srcBone->mName;
323 dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
324 dstBone->mNumWeights = 0;
325 }
327 ai_assert( newMesh->mNumBones == numBones );
329 // iterate over all new vertices and count which bones affected its old vertex in the source mesh
330 for( size_t a = 0; a < numSubMeshVertices; ++a )
331 {
332 size_t oldIndex = previousVertexIndices[a];
333 const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
335 for( size_t b = 0; b < bonesOnThisVertex.size(); ++b )
336 {
337 size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
338 if( newBoneIndex != std::numeric_limits<size_t>::max() )
339 newMesh->mBones[newBoneIndex]->mNumWeights++;
340 }
341 }
343 // allocate all bone weight arrays accordingly
344 for( size_t a = 0; a < newMesh->mNumBones; ++a )
345 {
346 aiBone* bone = newMesh->mBones[a];
347 ai_assert( bone->mNumWeights > 0 );
348 bone->mWeights = new aiVertexWeight[bone->mNumWeights];
349 bone->mNumWeights = 0; // for counting up in the next step
350 }
352 // now copy all the bone vertex weights for all the vertices which made it into the new submesh
353 for( size_t a = 0; a < numSubMeshVertices; ++a)
354 {
355 // find the source vertex for it in the source mesh
356 size_t previousIndex = previousVertexIndices[a];
357 // these bones were affecting it
358 const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
359 // all of the bones affecting it should be present in the new submesh, or else
360 // the face it comprises shouldn't be present
361 for( size_t b = 0; b < bonesOnThisVertex.size(); ++b)
362 {
363 size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
364 ai_assert( newBoneIndex != std::numeric_limits<size_t>::max() );
365 aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
366 newMesh->mBones[newBoneIndex]->mNumWeights++;
368 dstWeight->mVertexId = a;
369 dstWeight->mWeight = bonesOnThisVertex[b].second;
370 }
371 }
373 // I have the strange feeling that this will break apart at some point in time...
374 }
375 }
377 // ------------------------------------------------------------------------------------------------
378 // Recursively updates the node's mesh list to account for the changed mesh list
379 void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
380 {
381 // rebuild the node's mesh index list
382 if( pNode->mNumMeshes > 0 )
383 {
384 std::vector<size_t> newMeshList;
385 for( size_t a = 0; a < pNode->mNumMeshes; ++a)
386 {
387 size_t srcIndex = pNode->mMeshes[a];
388 const std::vector<size_t>& replaceMeshes = mSubMeshIndices[srcIndex];
389 newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
390 }
392 delete pNode->mMeshes;
393 pNode->mNumMeshes = newMeshList.size();
394 pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
395 std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
396 }
398 // do that also recursively for all children
399 for( size_t a = 0; a < pNode->mNumChildren; ++a )
400 {
401 UpdateNode( pNode->mChildren[a]);
402 }
403 }