/src/assimp/code/PostProcessing/SplitLargeMeshes.cpp
Line | Count | Source |
1 | | /* |
2 | | Open Asset Import Library (assimp) |
3 | | ---------------------------------------------------------------------- |
4 | | |
5 | | Copyright (c) 2006-2026, assimp team |
6 | | |
7 | | All rights reserved. |
8 | | |
9 | | Redistribution and use of this software in source and binary forms, |
10 | | with or without modification, are permitted provided that the |
11 | | following 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 | | |
27 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
38 | | |
39 | | ---------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /// @file Implementation of the SplitLargeMeshes postprocessing step |
43 | | |
44 | | // internal headers of the post-processing framework |
45 | | #include "SplitLargeMeshes.h" |
46 | | #include "ProcessHelper.h" |
47 | | |
48 | | using namespace Assimp; |
49 | | |
50 | | // ------------------------------------------------------------------------------------------------ |
51 | 38.0k | SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle() { |
52 | 38.0k | LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES; |
53 | 38.0k | } |
54 | | |
55 | | // ------------------------------------------------------------------------------------------------ |
56 | | // Returns whether the processing step is present in the given flag field. |
57 | 10.1k | bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const { |
58 | 10.1k | return (pFlags & aiProcess_SplitLargeMeshes) != 0; |
59 | 10.1k | } |
60 | | |
61 | | // ------------------------------------------------------------------------------------------------ |
62 | | // Executes the post processing step on the given imported data. |
63 | 10.1k | void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) { |
64 | 10.1k | if (0xffffffff == this->LIMIT || nullptr == pScene ) { |
65 | 0 | return; |
66 | 0 | } |
67 | | |
68 | 10.1k | ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle begin"); |
69 | 10.1k | std::vector<std::pair<aiMesh*, unsigned int> > avList; |
70 | | |
71 | 265k | for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) { |
72 | 254k | this->SplitMesh(a, pScene->mMeshes[a],avList); |
73 | 254k | } |
74 | | |
75 | 10.1k | if (avList.size() == pScene->mNumMeshes) { |
76 | 10.1k | ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do"); |
77 | 10.1k | } |
78 | | |
79 | | // it seems something has been split. rebuild the mesh list |
80 | 10.1k | delete[] pScene->mMeshes; |
81 | 10.1k | pScene->mNumMeshes = (unsigned int)avList.size(); |
82 | 10.1k | pScene->mMeshes = new aiMesh*[avList.size()]; |
83 | | |
84 | 265k | for (unsigned int i = 0; i < avList.size();++i) { |
85 | 254k | pScene->mMeshes[i] = avList[i].first; |
86 | 254k | } |
87 | | |
88 | | // now we need to update all nodes |
89 | 10.1k | this->UpdateNode(pScene->mRootNode,avList); |
90 | 10.1k | ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split"); |
91 | 10.1k | } |
92 | | |
93 | | // ------------------------------------------------------------------------------------------------ |
94 | | // Setup properties |
95 | 10.1k | void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) { |
96 | | // get the current value of the split property |
97 | 10.1k | this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); |
98 | 10.1k | } |
99 | | |
100 | | // ------------------------------------------------------------------------------------------------ |
101 | | // Update a node after some meshes have been split |
102 | 474k | void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& avList) { |
103 | 474k | if (pcNode == nullptr) { |
104 | 0 | ASSIMP_LOG_WARN("UpdateNode skipped, nullptr detected."); |
105 | 0 | return; |
106 | 0 | } |
107 | | |
108 | | // for every index in out list build a new entry |
109 | 474k | std::vector<unsigned int> aiEntries; |
110 | 474k | aiEntries.reserve(pcNode->mNumMeshes + 1); |
111 | 733k | for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) { |
112 | 420M | for (unsigned int a = 0; a < avList.size();++a) { |
113 | 419M | if (avList[a].second == pcNode->mMeshes[i]) { |
114 | 258k | aiEntries.push_back(a); |
115 | 258k | } |
116 | 419M | } |
117 | 258k | } |
118 | | |
119 | | // now build the new list |
120 | 474k | delete[] pcNode->mMeshes; |
121 | 474k | pcNode->mNumMeshes = (unsigned int)aiEntries.size(); |
122 | 474k | pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; |
123 | | |
124 | 733k | for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) { |
125 | 258k | pcNode->mMeshes[b] = aiEntries[b]; |
126 | 258k | } |
127 | | |
128 | | // recursively update all other nodes |
129 | 939k | for (unsigned int i = 0; i < pcNode->mNumChildren;++i) { |
130 | 464k | UpdateNode ( pcNode->mChildren[i], avList ); |
131 | 464k | } |
132 | 474k | } |
133 | | |
134 | | // ------------------------------------------------------------------------------------------------ |
135 | | // Executes the post processing step on the given imported data. |
136 | | void SplitLargeMeshesProcess_Triangle::SplitMesh( |
137 | | unsigned int a, |
138 | | aiMesh* pMesh, |
139 | 254k | std::vector<std::pair<aiMesh*, unsigned int> >& avList) { |
140 | 254k | if (pMesh->mNumFaces > SplitLargeMeshesProcess_Triangle::LIMIT) { |
141 | 0 | ASSIMP_LOG_INFO("Mesh exceeds the triangle limit. It will be split ..."); |
142 | | |
143 | | // we need to split this mesh into sub meshes |
144 | | // determine the size of a submesh |
145 | 0 | const unsigned int iSubMeshes = (pMesh->mNumFaces / LIMIT) + 1; |
146 | |
|
147 | 0 | const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes; |
148 | 0 | const unsigned int iOutVertexNum = iOutFaceNum * 3; |
149 | | |
150 | | // now generate all submeshes |
151 | 0 | for (unsigned int i = 0; i < iSubMeshes;++i) { |
152 | 0 | aiMesh* pcMesh = new aiMesh; |
153 | 0 | pcMesh->mNumFaces = iOutFaceNum; |
154 | 0 | pcMesh->mMaterialIndex = pMesh->mMaterialIndex; |
155 | | |
156 | | // the name carries the adjacency information between the meshes |
157 | 0 | pcMesh->mName = pMesh->mName; |
158 | |
|
159 | 0 | if (i == iSubMeshes-1) { |
160 | 0 | pcMesh->mNumFaces = iOutFaceNum + ( |
161 | 0 | pMesh->mNumFaces - iOutFaceNum * iSubMeshes); |
162 | 0 | } |
163 | | // copy the list of faces |
164 | 0 | pcMesh->mFaces = new aiFace[pcMesh->mNumFaces]; |
165 | |
|
166 | 0 | const unsigned int iBase = iOutFaceNum * i; |
167 | | |
168 | | // get the total number of indices |
169 | 0 | unsigned int iCnt = 0; |
170 | 0 | for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p) { |
171 | 0 | iCnt += pMesh->mFaces[p].mNumIndices; |
172 | 0 | } |
173 | 0 | pcMesh->mNumVertices = iCnt; |
174 | | |
175 | | // allocate storage |
176 | 0 | if (pMesh->mVertices != nullptr) { |
177 | 0 | pcMesh->mVertices = new aiVector3D[iCnt]; |
178 | 0 | } |
179 | |
|
180 | 0 | if (pMesh->HasNormals()) { |
181 | 0 | pcMesh->mNormals = new aiVector3D[iCnt]; |
182 | 0 | } |
183 | |
|
184 | 0 | if (pMesh->HasTangentsAndBitangents()) { |
185 | 0 | pcMesh->mTangents = new aiVector3D[iCnt]; |
186 | 0 | pcMesh->mBitangents = new aiVector3D[iCnt]; |
187 | 0 | } |
188 | | |
189 | | // texture coordinates |
190 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
191 | 0 | pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; |
192 | 0 | if (pMesh->HasTextureCoords( c)) { |
193 | 0 | pcMesh->mTextureCoords[c] = new aiVector3D[iCnt]; |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | // vertex colors |
198 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { |
199 | 0 | if (pMesh->HasVertexColors( c)) { |
200 | 0 | pcMesh->mColors[c] = new aiColor4D[iCnt]; |
201 | 0 | } |
202 | 0 | } |
203 | |
|
204 | 0 | if (pMesh->HasBones()) { |
205 | | // assume the number of bones won't change in most cases |
206 | 0 | pcMesh->mBones = new aiBone*[pMesh->mNumBones]; |
207 | | |
208 | | // iterate through all bones of the mesh and find those which |
209 | | // need to be copied to the split mesh |
210 | 0 | std::vector<aiVertexWeight> avTempWeights; |
211 | 0 | for (unsigned int p = 0; p < pcMesh->mNumBones;++p) { |
212 | 0 | aiBone* const bone = pcMesh->mBones[p]; |
213 | 0 | avTempWeights.clear(); |
214 | 0 | avTempWeights.reserve(bone->mNumWeights / iSubMeshes); |
215 | |
|
216 | 0 | for (unsigned int q = 0; q < bone->mNumWeights;++q) { |
217 | 0 | aiVertexWeight& weight = bone->mWeights[q]; |
218 | 0 | if(weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum) { |
219 | 0 | avTempWeights.push_back(weight); |
220 | 0 | weight = avTempWeights.back(); |
221 | 0 | weight.mVertexId -= iBase; |
222 | 0 | } |
223 | 0 | } |
224 | |
|
225 | 0 | if (!avTempWeights.empty()) { |
226 | | // we'll need this bone. Copy it ... |
227 | 0 | aiBone* pc = new aiBone(); |
228 | 0 | pcMesh->mBones[pcMesh->mNumBones++] = pc; |
229 | 0 | pc->mName = aiString(bone->mName); |
230 | 0 | pc->mNumWeights = (unsigned int)avTempWeights.size(); |
231 | 0 | pc->mOffsetMatrix = bone->mOffsetMatrix; |
232 | | |
233 | | // no need to reallocate the array for the last submesh. |
234 | | // Here we can reuse the (large) source array, although |
235 | | // we'll waste some memory |
236 | 0 | if (iSubMeshes-1 == i) { |
237 | 0 | pc->mWeights = bone->mWeights; |
238 | 0 | bone->mWeights = nullptr; |
239 | 0 | } else { |
240 | 0 | pc->mWeights = new aiVertexWeight[pc->mNumWeights]; |
241 | 0 | } |
242 | | |
243 | | // copy the weights |
244 | 0 | ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights); |
245 | 0 | } |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | | // (we will also need to copy the array of indices) |
250 | 0 | unsigned int iCurrent = 0; |
251 | 0 | for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { |
252 | 0 | pcMesh->mFaces[p].mNumIndices = 3; |
253 | | // allocate a new array |
254 | 0 | const unsigned int iTemp = p + iBase; |
255 | 0 | const unsigned int iNumIndices = pMesh->mFaces[iTemp].mNumIndices; |
256 | | |
257 | | // setup face type and number of indices |
258 | 0 | pcMesh->mFaces[p].mNumIndices = iNumIndices; |
259 | 0 | unsigned int* pi = pMesh->mFaces[iTemp].mIndices; |
260 | 0 | unsigned int* piOut = pcMesh->mFaces[p].mIndices = new unsigned int[iNumIndices]; |
261 | | |
262 | | // need to update the output primitive types |
263 | 0 | switch (iNumIndices) { |
264 | 0 | case 1: |
265 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; |
266 | 0 | break; |
267 | 0 | case 2: |
268 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; |
269 | 0 | break; |
270 | 0 | case 3: |
271 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; |
272 | 0 | break; |
273 | 0 | default: |
274 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; |
275 | 0 | } |
276 | | |
277 | | // and copy the contents of the old array, offset by current base |
278 | 0 | for (unsigned int v = 0; v < iNumIndices;++v) { |
279 | 0 | unsigned int iIndex = pi[v]; |
280 | 0 | unsigned int iIndexOut = iCurrent++; |
281 | 0 | piOut[v] = iIndexOut; |
282 | | |
283 | | // copy positions |
284 | 0 | if (pMesh->mVertices != nullptr) { |
285 | 0 | pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex]; |
286 | 0 | } |
287 | | |
288 | | // copy normals |
289 | 0 | if (pMesh->HasNormals()) { |
290 | 0 | pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex]; |
291 | 0 | } |
292 | | |
293 | | // copy tangents/bitangents |
294 | 0 | if (pMesh->HasTangentsAndBitangents()) { |
295 | 0 | pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex]; |
296 | 0 | pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex]; |
297 | 0 | } |
298 | | |
299 | | // texture coordinates |
300 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
301 | 0 | if (pMesh->HasTextureCoords( c ) ) { |
302 | 0 | pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex]; |
303 | 0 | } |
304 | 0 | } |
305 | | // vertex colors |
306 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { |
307 | 0 | if (pMesh->HasVertexColors( c)) { |
308 | 0 | pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex]; |
309 | 0 | } |
310 | 0 | } |
311 | 0 | } |
312 | 0 | } |
313 | | |
314 | | // add the newly created mesh to the list |
315 | 0 | avList.emplace_back(pcMesh,a); |
316 | 0 | } |
317 | | |
318 | | // now delete the old mesh data |
319 | 0 | delete pMesh; |
320 | 254k | } else { |
321 | 254k | avList.emplace_back(pMesh,a); |
322 | 254k | } |
323 | 254k | } |
324 | | |
325 | | // ------------------------------------------------------------------------------------------------ |
326 | 38.0k | SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex() { |
327 | 38.0k | LIMIT = AI_SLM_DEFAULT_MAX_VERTICES; |
328 | 38.0k | } |
329 | | |
330 | | // ------------------------------------------------------------------------------------------------ |
331 | | // Returns whether the processing step is present in the given flag field. |
332 | 10.1k | bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const { |
333 | 10.1k | return (pFlags & aiProcess_SplitLargeMeshes) != 0; |
334 | 10.1k | } |
335 | | |
336 | | // ------------------------------------------------------------------------------------------------ |
337 | | // Executes the post processing step on the given imported data. |
338 | 10.1k | void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { |
339 | 10.1k | if (0xffffffff == this->LIMIT || nullptr == pScene ) { |
340 | 0 | return; |
341 | 0 | } |
342 | | |
343 | 10.1k | ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex begin"); |
344 | | |
345 | 10.1k | std::vector<std::pair<aiMesh*, unsigned int> > avList; |
346 | | |
347 | | //Check for point cloud first, |
348 | | //Do not process point cloud, splitMesh works only with faces data |
349 | 105k | for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { |
350 | 96.8k | if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { |
351 | 1.94k | return; |
352 | 1.94k | } |
353 | 96.8k | } |
354 | | |
355 | 96.0k | for( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { |
356 | 87.8k | this->SplitMesh(a, pScene->mMeshes[a], avList); |
357 | 87.8k | } |
358 | | |
359 | 8.17k | if (avList.size() != pScene->mNumMeshes) { |
360 | | // it seems something has been split. rebuild the mesh list |
361 | 0 | delete[] pScene->mMeshes; |
362 | 0 | pScene->mNumMeshes = (unsigned int)avList.size(); |
363 | 0 | pScene->mMeshes = new aiMesh*[avList.size()]; |
364 | |
|
365 | 0 | for (unsigned int i = 0; i < avList.size();++i) { |
366 | 0 | pScene->mMeshes[i] = avList[i].first; |
367 | 0 | } |
368 | | |
369 | | // now we need to update all nodes |
370 | 0 | SplitLargeMeshesProcess_Triangle::UpdateNode(pScene->mRootNode,avList); |
371 | 0 | ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Vertex finished. Meshes have been split"); |
372 | 8.17k | } else { |
373 | 8.17k | ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Vertex finished. There was nothing to do"); |
374 | 8.17k | } |
375 | 8.17k | } |
376 | | |
377 | | // ------------------------------------------------------------------------------------------------ |
378 | | // Setup properties |
379 | 10.1k | void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) { |
380 | 10.1k | this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); |
381 | 10.1k | } |
382 | | |
383 | | // ------------------------------------------------------------------------------------------------ |
384 | | // Executes the post processing step on the given imported data. |
385 | | void SplitLargeMeshesProcess_Vertex::SplitMesh( |
386 | | unsigned int a, |
387 | | aiMesh* pMesh, |
388 | 87.8k | std::vector<std::pair<aiMesh*, unsigned int> >& avList) { |
389 | 87.8k | if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT) { |
390 | 0 | typedef std::vector< std::pair<unsigned int,float> > VertexWeightTable; |
391 | | |
392 | | // build a per-vertex weight list if necessary |
393 | 0 | VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(pMesh); |
394 | | |
395 | | // we need to split this mesh into sub meshes |
396 | | // determine the estimated size of a submesh |
397 | | // (this could be too large. Max waste is a single digit percentage) |
398 | 0 | const unsigned int iSubMeshes = (pMesh->mNumVertices / SplitLargeMeshesProcess_Vertex::LIMIT) + 1; |
399 | | |
400 | | // create a std::vector<unsigned int> to indicate which vertices |
401 | | // have already been copied |
402 | 0 | std::vector<unsigned int> avWasCopied; |
403 | 0 | avWasCopied.resize(pMesh->mNumVertices,0xFFFFFFFF); |
404 | | |
405 | | // try to find a good estimate for the number of output faces |
406 | | // per mesh. Add 12.5% as buffer |
407 | 0 | unsigned int iEstimatedSize = pMesh->mNumFaces / iSubMeshes; |
408 | 0 | iEstimatedSize += iEstimatedSize >> 3; |
409 | | |
410 | | // now generate all submeshes |
411 | 0 | unsigned int iBase( 0 ); |
412 | 0 | while (true) { |
413 | 0 | const unsigned int iOutVertexNum = SplitLargeMeshesProcess_Vertex::LIMIT; |
414 | 0 | aiMesh* pcMesh = new aiMesh; |
415 | 0 | pcMesh->mNumVertices = 0; |
416 | 0 | pcMesh->mMaterialIndex = pMesh->mMaterialIndex; |
417 | | |
418 | | // the name carries the adjacency information between the meshes |
419 | 0 | pcMesh->mName = pMesh->mName; |
420 | |
|
421 | 0 | typedef std::vector<aiVertexWeight> BoneWeightList; |
422 | 0 | if (pMesh->HasBones()) { |
423 | 0 | pcMesh->mBones = new aiBone*[pMesh->mNumBones]; |
424 | 0 | ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones); |
425 | 0 | } |
426 | | |
427 | | // clear the temporary helper array |
428 | 0 | if (iBase) { |
429 | | // we can't use memset here we unsigned int needn' be 32 bits |
430 | 0 | for (auto &elem : avWasCopied) { |
431 | 0 | elem = 0xffffffff; |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | // output vectors |
436 | 0 | std::vector<aiFace> vFaces; |
437 | | |
438 | | // reserve enough storage for most cases |
439 | 0 | if (pMesh->HasPositions()) { |
440 | 0 | pcMesh->mVertices = new aiVector3D[iOutVertexNum]; |
441 | 0 | } |
442 | 0 | if (pMesh->HasNormals()) { |
443 | 0 | pcMesh->mNormals = new aiVector3D[iOutVertexNum]; |
444 | 0 | } |
445 | 0 | if (pMesh->HasTangentsAndBitangents()) { |
446 | 0 | pcMesh->mTangents = new aiVector3D[iOutVertexNum]; |
447 | 0 | pcMesh->mBitangents = new aiVector3D[iOutVertexNum]; |
448 | 0 | } |
449 | 0 | for (unsigned int c = 0; pMesh->HasVertexColors(c);++c) { |
450 | 0 | pcMesh->mColors[c] = new aiColor4D[iOutVertexNum]; |
451 | 0 | } |
452 | 0 | for (unsigned int c = 0; pMesh->HasTextureCoords(c);++c) { |
453 | 0 | pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c]; |
454 | 0 | pcMesh->mTextureCoords[c] = new aiVector3D[iOutVertexNum]; |
455 | 0 | } |
456 | 0 | vFaces.reserve(iEstimatedSize); |
457 | | |
458 | | // (we will also need to copy the array of indices) |
459 | 0 | while (iBase < pMesh->mNumFaces) { |
460 | | // allocate a new array |
461 | 0 | const unsigned int iNumIndices = pMesh->mFaces[iBase].mNumIndices; |
462 | | |
463 | | // doesn't catch degenerates but is quite fast |
464 | 0 | unsigned int iNeed = 0; |
465 | 0 | for (unsigned int v = 0; v < iNumIndices;++v) { |
466 | 0 | unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; |
467 | | |
468 | | // check whether we do already have this vertex |
469 | 0 | if (0xFFFFFFFF == avWasCopied[iIndex]) { |
470 | 0 | iNeed++; |
471 | 0 | } |
472 | 0 | } |
473 | 0 | if (pcMesh->mNumVertices + iNeed > iOutVertexNum) { |
474 | | // don't use this face |
475 | 0 | break; |
476 | 0 | } |
477 | | |
478 | 0 | vFaces.emplace_back(); |
479 | 0 | aiFace& rFace = vFaces.back(); |
480 | | |
481 | | // setup face type and number of indices |
482 | 0 | rFace.mNumIndices = iNumIndices; |
483 | 0 | rFace.mIndices = new unsigned int[iNumIndices]; |
484 | | |
485 | | // need to update the output primitive types |
486 | 0 | switch (rFace.mNumIndices) { |
487 | 0 | case 1: |
488 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT; |
489 | 0 | break; |
490 | 0 | case 2: |
491 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE; |
492 | 0 | break; |
493 | 0 | case 3: |
494 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; |
495 | 0 | break; |
496 | 0 | default: |
497 | 0 | pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; |
498 | 0 | } |
499 | | |
500 | | // and copy the contents of the old array, offset by current base |
501 | 0 | for (unsigned int v = 0; v < iNumIndices;++v) { |
502 | 0 | unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v]; |
503 | | |
504 | | // check whether we do already have this vertex |
505 | 0 | if (0xFFFFFFFF != avWasCopied[iIndex]) { |
506 | 0 | rFace.mIndices[v] = avWasCopied[iIndex]; |
507 | 0 | continue; |
508 | 0 | } |
509 | | |
510 | | // copy positions |
511 | 0 | pcMesh->mVertices[pcMesh->mNumVertices] = (pMesh->mVertices[iIndex]); |
512 | | |
513 | | // copy normals |
514 | 0 | if (pMesh->HasNormals()) { |
515 | 0 | pcMesh->mNormals[pcMesh->mNumVertices] = (pMesh->mNormals[iIndex]); |
516 | 0 | } |
517 | | |
518 | | // copy tangents/bitangents |
519 | 0 | if (pMesh->HasTangentsAndBitangents()) { |
520 | 0 | pcMesh->mTangents[pcMesh->mNumVertices] = (pMesh->mTangents[iIndex]); |
521 | 0 | pcMesh->mBitangents[pcMesh->mNumVertices] = (pMesh->mBitangents[iIndex]); |
522 | 0 | } |
523 | | |
524 | | // texture coordinates |
525 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { |
526 | 0 | if (pMesh->HasTextureCoords( c)) { |
527 | 0 | pcMesh->mTextureCoords[c][pcMesh->mNumVertices] = pMesh->mTextureCoords[c][iIndex]; |
528 | 0 | } |
529 | 0 | } |
530 | | // vertex colors |
531 | 0 | for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { |
532 | 0 | if (pMesh->HasVertexColors( c)) { |
533 | 0 | pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex]; |
534 | 0 | } |
535 | 0 | } |
536 | | // check whether we have bone weights assigned to this vertex |
537 | 0 | rFace.mIndices[v] = pcMesh->mNumVertices; |
538 | 0 | if (avPerVertexWeights) { |
539 | 0 | VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ]; |
540 | 0 | if( !table.empty() ) { |
541 | 0 | for (VertexWeightTable::const_iterator iter = table.begin(); |
542 | 0 | iter != table.end();++iter) { |
543 | | // allocate the bone weight array if necessary |
544 | 0 | BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first]; |
545 | 0 | if (nullptr == pcWeightList) { |
546 | 0 | pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList()); |
547 | 0 | } |
548 | 0 | pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second)); |
549 | 0 | } |
550 | 0 | } |
551 | 0 | } |
552 | |
|
553 | 0 | avWasCopied[iIndex] = pcMesh->mNumVertices; |
554 | 0 | pcMesh->mNumVertices++; |
555 | 0 | } |
556 | 0 | ++iBase; |
557 | 0 | if(pcMesh->mNumVertices == iOutVertexNum) { |
558 | | // break here. The face is only added if it was complete |
559 | 0 | break; |
560 | 0 | } |
561 | 0 | } |
562 | | |
563 | | // check which bones we'll need to create for this submesh |
564 | 0 | if (pMesh->HasBones()) { |
565 | 0 | aiBone** ppCurrent = pcMesh->mBones; |
566 | 0 | for (unsigned int k = 0; k < pMesh->mNumBones;++k) { |
567 | | // check whether the bone is existing |
568 | 0 | BoneWeightList* pcWeightList; |
569 | 0 | pcWeightList = (BoneWeightList *)pcMesh->mBones[k]; |
570 | 0 | if (nullptr != pcWeightList) { |
571 | 0 | aiBone *pcOldBone = pMesh->mBones[k]; |
572 | 0 | aiBone* pcOut( nullptr ); |
573 | 0 | *ppCurrent++ = pcOut = new aiBone(); |
574 | 0 | pcOut->mName = aiString(pcOldBone->mName); |
575 | 0 | pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix; |
576 | 0 | pcOut->mNumWeights = (unsigned int)pcWeightList->size(); |
577 | 0 | pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights]; |
578 | | |
579 | | // copy the vertex weights |
580 | 0 | ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0), |
581 | 0 | pcOut->mNumWeights * sizeof(aiVertexWeight)); |
582 | | |
583 | | // delete the temporary bone weight list |
584 | 0 | delete pcWeightList; |
585 | 0 | pcMesh->mNumBones++; |
586 | 0 | } |
587 | 0 | } |
588 | 0 | } |
589 | | |
590 | | // copy the face list to the mesh |
591 | 0 | pcMesh->mFaces = new aiFace[vFaces.size()]; |
592 | 0 | pcMesh->mNumFaces = (unsigned int)vFaces.size(); |
593 | |
|
594 | 0 | for (unsigned int p = 0; p < pcMesh->mNumFaces;++p) { |
595 | 0 | pcMesh->mFaces[p] = vFaces[p]; |
596 | 0 | } |
597 | | |
598 | | // add the newly created mesh to the list |
599 | 0 | avList.emplace_back(pcMesh,a); |
600 | |
|
601 | 0 | if (iBase == pMesh->mNumFaces) { |
602 | | // have all faces ... finish the outer loop, too |
603 | 0 | break; |
604 | 0 | } |
605 | 0 | } |
606 | | |
607 | | // delete the per-vertex weight list again |
608 | 0 | delete[] avPerVertexWeights; |
609 | | |
610 | | // now delete the old mesh data |
611 | 0 | delete pMesh; |
612 | 0 | return; |
613 | 0 | } |
614 | 87.8k | avList.emplace_back(pMesh,a); |
615 | 87.8k | } |