1/*
2---------------------------------------------------------------------------
3Open Asset Import Library (assimp)
4---------------------------------------------------------------------------
5
6Copyright (c) 2006-2017, assimp team
7
8
9All rights reserved.
10
11Redistribution and use of this software in source and binary forms,
12with or without modification, are permitted provided that the following
13conditions 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
29THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
30"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
31LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
32A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
33OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
34SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
36DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
39OF 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
54using namespace Assimp;
55
56// ------------------------------------------------------------------------------------------------
57// Constructor to be privately used by Importer
58SortByPTypeProcess::SortByPTypeProcess()
59{
60 configRemoveMeshes = 0;
61}
62
63// ------------------------------------------------------------------------------------------------
64// Destructor, private as well
65SortByPTypeProcess::~SortByPTypeProcess()
66{
67 // nothing to do here
68}
69
70// ------------------------------------------------------------------------------------------------
71// Returns whether the processing step is present in the given flag field.
72bool SortByPTypeProcess::IsActive( unsigned int pFlags) const
73{
74 return (pFlags & aiProcess_SortByPType) != 0;
75}
76
77// ------------------------------------------------------------------------------------------------
78void 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
85void 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.
135void 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