1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file Implementation of the DeterminePTypeHelperProcess and |
44 | * SortByPTypeProcess post-process steps. |
45 | */ |
46 | |
47 | |
48 | |
49 | // internal headers |
50 | #include "ProcessHelper.h" |
51 | #include "SortByPTypeProcess.h" |
52 | #include "Exceptional.h" |
53 | |
54 | using namespace Assimp; |
55 | |
56 | // ------------------------------------------------------------------------------------------------ |
57 | // Constructor to be privately used by Importer |
58 | SortByPTypeProcess::SortByPTypeProcess() |
59 | { |
60 | configRemoveMeshes = 0; |
61 | } |
62 | |
63 | // ------------------------------------------------------------------------------------------------ |
64 | // Destructor, private as well |
65 | SortByPTypeProcess::~SortByPTypeProcess() |
66 | { |
67 | // nothing to do here |
68 | } |
69 | |
70 | // ------------------------------------------------------------------------------------------------ |
71 | // Returns whether the processing step is present in the given flag field. |
72 | bool SortByPTypeProcess::IsActive( unsigned int pFlags) const |
73 | { |
74 | return (pFlags & aiProcess_SortByPType) != 0; |
75 | } |
76 | |
77 | // ------------------------------------------------------------------------------------------------ |
78 | void SortByPTypeProcess::SetupProperties(const Importer* pImp) |
79 | { |
80 | configRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0); |
81 | } |
82 | |
83 | // ------------------------------------------------------------------------------------------------ |
84 | // Update changed meshes in all nodes |
85 | void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node) |
86 | { |
87 | // std::vector<unsigned int>::const_iterator it; |
88 | |
89 | if (node->mNumMeshes) |
90 | { |
91 | unsigned int newSize = 0; |
92 | for (unsigned int m = 0; m< node->mNumMeshes; ++m) |
93 | { |
94 | unsigned int add = node->mMeshes[m]<<2; |
95 | for (unsigned int i = 0; i < 4;++i) |
96 | { |
97 | if (UINT_MAX != replaceMeshIndex[add+i])++newSize; |
98 | } |
99 | } |
100 | if (!newSize) |
101 | { |
102 | delete[] node->mMeshes; |
103 | node->mNumMeshes = 0; |
104 | node->mMeshes = NULL; |
105 | } |
106 | else |
107 | { |
108 | // Try to reuse the old array if possible |
109 | unsigned int* newMeshes = (newSize > node->mNumMeshes |
110 | ? new unsigned int[newSize] : node->mMeshes); |
111 | |
112 | for (unsigned int m = 0; m< node->mNumMeshes; ++m) |
113 | { |
114 | unsigned int add = node->mMeshes[m]<<2; |
115 | for (unsigned int i = 0; i < 4;++i) |
116 | { |
117 | if (UINT_MAX != replaceMeshIndex[add+i]) |
118 | *newMeshes++ = replaceMeshIndex[add+i]; |
119 | } |
120 | } |
121 | if (newSize > node->mNumMeshes) |
122 | delete[] node->mMeshes; |
123 | |
124 | node->mMeshes = newMeshes-(node->mNumMeshes = newSize); |
125 | } |
126 | } |
127 | |
128 | // call all subnodes recursively |
129 | for (unsigned int m = 0; m < node->mNumChildren; ++m) |
130 | UpdateNodes(replaceMeshIndex,node->mChildren[m]); |
131 | } |
132 | |
133 | // ------------------------------------------------------------------------------------------------ |
134 | // Executes the post processing step on the given imported data. |
135 | void SortByPTypeProcess::Execute( aiScene* pScene) |
136 | { |
137 | if (!pScene->mNumMeshes) |
138 | { |
139 | DefaultLogger::get()->debug("SortByPTypeProcess skipped, there are no meshes" ); |
140 | return; |
141 | } |
142 | |
143 | DefaultLogger::get()->debug("SortByPTypeProcess begin" ); |
144 | |
145 | unsigned int aiNumMeshesPerPType[4] = {0,0,0,0}; |
146 | |
147 | std::vector<aiMesh*> outMeshes; |
148 | outMeshes.reserve(pScene->mNumMeshes<<1u); |
149 | |
150 | bool bAnyChanges = false; |
151 | |
152 | std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,UINT_MAX); |
153 | std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin(); |
154 | for (unsigned int i = 0; i < pScene->mNumMeshes;++i) |
155 | { |
156 | aiMesh* const mesh = pScene->mMeshes[i]; |
157 | ai_assert(0 != mesh->mPrimitiveTypes); |
158 | |
159 | // if there's just one primitive type in the mesh there's nothing to do for us |
160 | unsigned int num = 0; |
161 | if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) |
162 | { |
163 | ++aiNumMeshesPerPType[0]; |
164 | ++num; |
165 | } |
166 | if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) |
167 | { |
168 | ++aiNumMeshesPerPType[1]; |
169 | ++num; |
170 | } |
171 | if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) |
172 | { |
173 | ++aiNumMeshesPerPType[2]; |
174 | ++num; |
175 | } |
176 | if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) |
177 | { |
178 | ++aiNumMeshesPerPType[3]; |
179 | ++num; |
180 | } |
181 | |
182 | if (1 == num) |
183 | { |
184 | if (!(configRemoveMeshes & mesh->mPrimitiveTypes)) |
185 | { |
186 | *meshIdx = (unsigned int) outMeshes.size(); |
187 | outMeshes.push_back(mesh); |
188 | } |
189 | else bAnyChanges = true; |
190 | |
191 | meshIdx += 4; |
192 | continue; |
193 | } |
194 | bAnyChanges = true; |
195 | |
196 | // reuse our current mesh arrays for the submesh |
197 | // with the largest numer of primitives |
198 | unsigned int aiNumPerPType[4] = {0,0,0,0}; |
199 | aiFace* pFirstFace = mesh->mFaces; |
200 | aiFace* const pLastFace = pFirstFace + mesh->mNumFaces; |
201 | |
202 | unsigned int numPolyVerts = 0; |
203 | for (;pFirstFace != pLastFace; ++pFirstFace) |
204 | { |
205 | if (pFirstFace->mNumIndices <= 3) |
206 | ++aiNumPerPType[pFirstFace->mNumIndices-1]; |
207 | else |
208 | { |
209 | ++aiNumPerPType[3]; |
210 | numPolyVerts += pFirstFace-> mNumIndices; |
211 | } |
212 | } |
213 | |
214 | VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh); |
215 | for (unsigned int real = 0; real < 4; ++real,++meshIdx) |
216 | { |
217 | if ( !aiNumPerPType[real] || configRemoveMeshes & (1u << real)) |
218 | { |
219 | continue; |
220 | } |
221 | |
222 | *meshIdx = (unsigned int) outMeshes.size(); |
223 | outMeshes.push_back(new aiMesh()); |
224 | aiMesh* out = outMeshes.back(); |
225 | |
226 | // the name carries the adjacency information between the meshes |
227 | out->mName = mesh->mName; |
228 | |
229 | // copy data members |
230 | out->mPrimitiveTypes = 1u << real; |
231 | out->mMaterialIndex = mesh->mMaterialIndex; |
232 | |
233 | // allocate output storage |
234 | out->mNumFaces = aiNumPerPType[real]; |
235 | aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces]; |
236 | |
237 | out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1)); |
238 | |
239 | aiVector3D *vert(NULL), *nor(NULL), *tan(NULL), *bit(NULL); |
240 | aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS]; |
241 | aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS]; |
242 | |
243 | if (mesh->mVertices) |
244 | vert = out->mVertices = new aiVector3D[out->mNumVertices]; |
245 | |
246 | if (mesh->mNormals) |
247 | nor = out->mNormals = new aiVector3D[out->mNumVertices]; |
248 | |
249 | if (mesh->mTangents) |
250 | { |
251 | tan = out->mTangents = new aiVector3D[out->mNumVertices]; |
252 | bit = out->mBitangents = new aiVector3D[out->mNumVertices]; |
253 | } |
254 | |
255 | for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) |
256 | { |
257 | if (mesh->mTextureCoords[i]) |
258 | uv[i] = out->mTextureCoords[i] = new aiVector3D[out->mNumVertices]; |
259 | else uv[i] = NULL; |
260 | |
261 | out->mNumUVComponents[i] = mesh->mNumUVComponents[i]; |
262 | } |
263 | |
264 | for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) |
265 | { |
266 | if (mesh->mColors[i]) |
267 | cols[i] = out->mColors[i] = new aiColor4D[out->mNumVertices]; |
268 | else cols[i] = NULL; |
269 | } |
270 | |
271 | typedef std::vector< aiVertexWeight > TempBoneInfo; |
272 | std::vector< TempBoneInfo > tempBones(mesh->mNumBones); |
273 | |
274 | // try to guess how much storage we'll need |
275 | for (unsigned int q = 0; q < mesh->mNumBones;++q) |
276 | { |
277 | tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1)); |
278 | } |
279 | |
280 | unsigned int outIdx = 0; |
281 | for (unsigned int m = 0; m < mesh->mNumFaces; ++m) |
282 | { |
283 | aiFace& in = mesh->mFaces[m]; |
284 | if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1)) |
285 | { |
286 | continue; |
287 | } |
288 | |
289 | outFaces->mNumIndices = in.mNumIndices; |
290 | outFaces->mIndices = in.mIndices; |
291 | |
292 | for (unsigned int q = 0; q < in.mNumIndices; ++q) |
293 | { |
294 | unsigned int idx = in.mIndices[q]; |
295 | |
296 | // process all bones of this index |
297 | if (avw) |
298 | { |
299 | VertexWeightTable& tbl = avw[idx]; |
300 | for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end(); |
301 | it != end; ++it) |
302 | { |
303 | tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) ); |
304 | } |
305 | } |
306 | |
307 | if (vert) |
308 | { |
309 | *vert++ = mesh->mVertices[idx]; |
310 | //mesh->mVertices[idx].x = get_qnan(); |
311 | } |
312 | if (nor )*nor++ = mesh->mNormals[idx]; |
313 | if (tan ) |
314 | { |
315 | *tan++ = mesh->mTangents[idx]; |
316 | *bit++ = mesh->mBitangents[idx]; |
317 | } |
318 | |
319 | for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) |
320 | { |
321 | if (!uv[pp])break; |
322 | *uv[pp]++ = mesh->mTextureCoords[pp][idx]; |
323 | } |
324 | |
325 | for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) |
326 | { |
327 | if (!cols[pp])break; |
328 | *cols[pp]++ = mesh->mColors[pp][idx]; |
329 | } |
330 | |
331 | in.mIndices[q] = outIdx++; |
332 | } |
333 | |
334 | in.mIndices = NULL; |
335 | ++outFaces; |
336 | } |
337 | ai_assert(outFaces == out->mFaces + out->mNumFaces); |
338 | |
339 | // now generate output bones |
340 | for (unsigned int q = 0; q < mesh->mNumBones;++q) |
341 | if (!tempBones[q].empty())++out->mNumBones; |
342 | |
343 | if (out->mNumBones) |
344 | { |
345 | out->mBones = new aiBone*[out->mNumBones]; |
346 | for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q) |
347 | { |
348 | TempBoneInfo& in = tempBones[q]; |
349 | if (in.empty())continue; |
350 | |
351 | aiBone* srcBone = mesh->mBones[q]; |
352 | aiBone* bone = out->mBones[real] = new aiBone(); |
353 | |
354 | bone->mName = srcBone->mName; |
355 | bone->mOffsetMatrix = srcBone->mOffsetMatrix; |
356 | |
357 | bone->mNumWeights = (unsigned int)in.size(); |
358 | bone->mWeights = new aiVertexWeight[bone->mNumWeights]; |
359 | |
360 | ::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight)); |
361 | |
362 | ++real; |
363 | } |
364 | } |
365 | } |
366 | |
367 | // delete the per-vertex bone weights table |
368 | delete[] avw; |
369 | |
370 | // delete the input mesh |
371 | delete mesh; |
372 | |
373 | // avoid invalid pointer |
374 | pScene->mMeshes[i] = NULL; |
375 | } |
376 | |
377 | if (outMeshes.empty()) |
378 | { |
379 | // This should not occur |
380 | throw DeadlyImportError("No meshes remaining" ); |
381 | } |
382 | |
383 | // If we added at least one mesh process all nodes in the node |
384 | // graph and update their respective mesh indices. |
385 | if (bAnyChanges) |
386 | { |
387 | UpdateNodes(replaceMeshIndex,pScene->mRootNode); |
388 | } |
389 | |
390 | if (outMeshes.size() != pScene->mNumMeshes) |
391 | { |
392 | delete[] pScene->mMeshes; |
393 | pScene->mNumMeshes = (unsigned int)outMeshes.size(); |
394 | pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; |
395 | } |
396 | ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*)); |
397 | |
398 | if (!DefaultLogger::isNullLogger()) |
399 | { |
400 | char buffer[1024]; |
401 | ::ai_snprintf(buffer,1024,"Points: %u%s, Lines: %u%s, Triangles: %u%s, Polygons: %u%s (Meshes, X = removed)" , |
402 | aiNumMeshesPerPType[0], ((configRemoveMeshes & aiPrimitiveType_POINT) ? "X" : "" ), |
403 | aiNumMeshesPerPType[1], ((configRemoveMeshes & aiPrimitiveType_LINE) ? "X" : "" ), |
404 | aiNumMeshesPerPType[2], ((configRemoveMeshes & aiPrimitiveType_TRIANGLE) ? "X" : "" ), |
405 | aiNumMeshesPerPType[3], ((configRemoveMeshes & aiPrimitiveType_POLYGON) ? "X" : "" )); |
406 | DefaultLogger::get()->info(buffer); |
407 | DefaultLogger::get()->debug("SortByPTypeProcess finished" ); |
408 | } |
409 | } |
410 | |
411 | |