1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
5 
6 Copyright (c) 2006-2021, assimp team
7 
8 
9 
10 All rights reserved.
11 
12 Redistribution and use of this software in source and binary forms,
13 with or without modification, are permitted provided that the following
14 conditions are met:
15 
16 * Redistributions of source code must retain the above
17   copyright notice, this list of conditions and the
18   following disclaimer.
19 
20 * Redistributions in binary form must reproduce the above
21   copyright notice, this list of conditions and the
22   following disclaimer in the documentation and/or other
23   materials provided with the distribution.
24 
25 * Neither the name of the assimp team, nor the names of its
26   contributors may be used to endorse or promote products
27   derived from this software without specific prior
28   written permission of the assimp team.
29 
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ---------------------------------------------------------------------------
42 */
43 
44 /** @file PretransformVertices.cpp
45  *  @brief Implementation of the "PretransformVertices" post processing step
46 */
47 
48 #include "PretransformVertices.h"
49 #include "ConvertToLHProcess.h"
50 #include "ProcessHelper.h"
51 #include <assimp/Exceptional.h>
52 #include <assimp/SceneCombiner.h>
53 
54 using namespace Assimp;
55 
56 // some array offsets
57 #define AI_PTVS_VERTEX 0x0
58 #define AI_PTVS_FACE 0x1
59 
60 // ------------------------------------------------------------------------------------------------
61 // Constructor to be privately used by Importer
PretransformVertices()62 PretransformVertices::PretransformVertices() :
63 		configKeepHierarchy(false),
64 		configNormalize(false),
65 		configTransform(false),
66 		configTransformation(),
67 		mConfigPointCloud(false) {
68 	// empty
69 }
70 
71 // ------------------------------------------------------------------------------------------------
72 // Destructor, private as well
~PretransformVertices()73 PretransformVertices::~PretransformVertices() {
74 	// nothing to do here
75 }
76 
77 // ------------------------------------------------------------------------------------------------
78 // Returns whether the processing step is present in the given flag field.
IsActive(unsigned int pFlags) const79 bool PretransformVertices::IsActive(unsigned int pFlags) const {
80 	return (pFlags & aiProcess_PreTransformVertices) != 0;
81 }
82 
83 // ------------------------------------------------------------------------------------------------
84 // Setup import configuration
SetupProperties(const Importer * pImp)85 void PretransformVertices::SetupProperties(const Importer *pImp) {
86 	// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
87 	// AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
88 	configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
89 	configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
90 	configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
91 
92 	configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
93 
94 	mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
95 }
96 
97 // ------------------------------------------------------------------------------------------------
98 // Count the number of nodes
CountNodes(const aiNode * pcNode) const99 unsigned int PretransformVertices::CountNodes(const aiNode *pcNode) const {
100 	unsigned int iRet = 1;
101 	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
102 		iRet += CountNodes(pcNode->mChildren[i]);
103 	}
104 	return iRet;
105 }
106 
107 // ------------------------------------------------------------------------------------------------
108 // Get a bitwise combination identifying the vertex format of a mesh
GetMeshVFormat(aiMesh * pcMesh) const109 unsigned int PretransformVertices::GetMeshVFormat(aiMesh *pcMesh) const {
110 	// the vertex format is stored in aiMesh::mBones for later retrieval.
111 	// there isn't a good reason to compute it a few hundred times
112 	// from scratch. The pointer is unused as animations are lost
113 	// during PretransformVertices.
114 	if (pcMesh->mBones)
115 		return (unsigned int)(uint64_t)pcMesh->mBones;
116 
117 	const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
118 
119 	// store the value for later use
120 	pcMesh->mBones = (aiBone **)(uint64_t)iRet;
121 	return iRet;
122 }
123 
124 // ------------------------------------------------------------------------------------------------
125 // Count the number of vertices in the whole scene and a given
126 // material index
CountVerticesAndFaces(const aiScene * pcScene,const aiNode * pcNode,unsigned int iMat,unsigned int iVFormat,unsigned int * piFaces,unsigned int * piVertices) const127 void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
128 		unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const {
129 	for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
130 		aiMesh *pcMesh = pcScene->mMeshes[pcNode->mMeshes[i]];
131 		if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) {
132 			*piVertices += pcMesh->mNumVertices;
133 			*piFaces += pcMesh->mNumFaces;
134 		}
135 	}
136 	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
137 		CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat,
138 				iVFormat, piFaces, piVertices);
139 	}
140 }
141 
142 // ------------------------------------------------------------------------------------------------
143 // Collect vertex/face data
CollectData(const aiScene * pcScene,const aiNode * pcNode,unsigned int iMat,unsigned int iVFormat,aiMesh * pcMeshOut,unsigned int aiCurrent[2],unsigned int * num_refs) const144 void PretransformVertices::CollectData(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
145 		unsigned int iVFormat, aiMesh *pcMeshOut,
146 		unsigned int aiCurrent[2], unsigned int *num_refs) const {
147 	// No need to multiply if there's no transformation
148 	const bool identity = pcNode->mTransformation.IsIdentity();
149 	for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
150 		aiMesh *pcMesh = pcScene->mMeshes[pcNode->mMeshes[i]];
151 		if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh)) {
152 			// Decrement mesh reference counter
153 			unsigned int &num_ref = num_refs[pcNode->mMeshes[i]];
154 			ai_assert(0 != num_ref);
155 			--num_ref;
156 			// Save the name of the last mesh
157 			if (num_ref == 0) {
158 				pcMeshOut->mName = pcMesh->mName;
159 			}
160 
161 			if (identity) {
162 				// copy positions without modifying them
163 				::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
164 						pcMesh->mVertices,
165 						pcMesh->mNumVertices * sizeof(aiVector3D));
166 
167 				if (iVFormat & 0x2) {
168 					// copy normals without modifying them
169 					::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
170 							pcMesh->mNormals,
171 							pcMesh->mNumVertices * sizeof(aiVector3D));
172 				}
173 				if (iVFormat & 0x4) {
174 					// copy tangents without modifying them
175 					::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
176 							pcMesh->mTangents,
177 							pcMesh->mNumVertices * sizeof(aiVector3D));
178 					// copy bitangents without modifying them
179 					::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
180 							pcMesh->mBitangents,
181 							pcMesh->mNumVertices * sizeof(aiVector3D));
182 				}
183 			} else {
184 				// copy positions, transform them to worldspace
185 				for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
186 					pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX] + n] = pcNode->mTransformation * pcMesh->mVertices[n];
187 				}
188 				aiMatrix4x4 mWorldIT = pcNode->mTransformation;
189 				mWorldIT.Inverse().Transpose();
190 
191 				// TODO: implement Inverse() for aiMatrix3x3
192 				aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
193 
194 				if (iVFormat & 0x2) {
195 					// copy normals, transform them to worldspace
196 					for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
197 						pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX] + n] =
198 								(m * pcMesh->mNormals[n]).Normalize();
199 					}
200 				}
201 				if (iVFormat & 0x4) {
202 					// copy tangents and bitangents, transform them to worldspace
203 					for (unsigned int n = 0; n < pcMesh->mNumVertices; ++n) {
204 						pcMeshOut->mTangents[aiCurrent[AI_PTVS_VERTEX] + n] = (m * pcMesh->mTangents[n]).Normalize();
205 						pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX] + n] = (m * pcMesh->mBitangents[n]).Normalize();
206 					}
207 				}
208 			}
209 			unsigned int p = 0;
210 			while (iVFormat & (0x100 << p)) {
211 				// copy texture coordinates
212 				memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
213 						pcMesh->mTextureCoords[p],
214 						pcMesh->mNumVertices * sizeof(aiVector3D));
215 				++p;
216 			}
217 			p = 0;
218 			while (iVFormat & (0x1000000 << p)) {
219 				// copy vertex colors
220 				memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
221 						pcMesh->mColors[p],
222 						pcMesh->mNumVertices * sizeof(aiColor4D));
223 				++p;
224 			}
225 			// now we need to copy all faces. since we will delete the source mesh afterwards,
226 			// we don't need to reallocate the array of indices except if this mesh is
227 			// referenced multiple times.
228 			for (unsigned int planck = 0; planck < pcMesh->mNumFaces; ++planck) {
229 				aiFace &f_src = pcMesh->mFaces[planck];
230 				aiFace &f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE] + planck];
231 
232 				const unsigned int num_idx = f_src.mNumIndices;
233 
234 				f_dst.mNumIndices = num_idx;
235 
236 				unsigned int *pi;
237 				if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
238 					pi = f_dst.mIndices = f_src.mIndices;
239 
240 					// offset all vertex indices
241 					for (unsigned int hahn = 0; hahn < num_idx; ++hahn) {
242 						pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
243 					}
244 				} else {
245 					pi = f_dst.mIndices = new unsigned int[num_idx];
246 
247 					// copy and offset all vertex indices
248 					for (unsigned int hahn = 0; hahn < num_idx; ++hahn) {
249 						pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
250 					}
251 				}
252 
253 				// Update the mPrimitiveTypes member of the mesh
254 				switch (pcMesh->mFaces[planck].mNumIndices) {
255 					case 0x1:
256 						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
257 						break;
258 					case 0x2:
259 						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE;
260 						break;
261 					case 0x3:
262 						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
263 						break;
264 					default:
265 						pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
266 						break;
267 				};
268 			}
269 			aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices;
270 			aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces;
271 		}
272 	}
273 
274 	// append all children of us
275 	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
276 		CollectData(pcScene, pcNode->mChildren[i], iMat,
277 				iVFormat, pcMeshOut, aiCurrent, num_refs);
278 	}
279 }
280 
281 // ------------------------------------------------------------------------------------------------
282 // Get a list of all vertex formats that occur for a given material index
283 // The output list contains duplicate elements
GetVFormatList(const aiScene * pcScene,unsigned int iMat,std::list<unsigned int> & aiOut) const284 void PretransformVertices::GetVFormatList(const aiScene *pcScene, unsigned int iMat,
285 		std::list<unsigned int> &aiOut) const {
286 	for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
287 		aiMesh *pcMesh = pcScene->mMeshes[i];
288 		if (iMat == pcMesh->mMaterialIndex) {
289 			aiOut.push_back(GetMeshVFormat(pcMesh));
290 		}
291 	}
292 }
293 
294 // ------------------------------------------------------------------------------------------------
295 // Compute the absolute transformation matrices of each node
ComputeAbsoluteTransform(aiNode * pcNode)296 void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
297 	if (pcNode->mParent) {
298 		pcNode->mTransformation = pcNode->mParent->mTransformation * pcNode->mTransformation;
299 	}
300 
301 	for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
302 		ComputeAbsoluteTransform(pcNode->mChildren[i]);
303 	}
304 }
305 
306 // ------------------------------------------------------------------------------------------------
307 // Apply the node transformation to a mesh
ApplyTransform(aiMesh * mesh,const aiMatrix4x4 & mat) const308 void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const {
309 	// Check whether we need to transform the coordinates at all
310 	if (!mat.IsIdentity()) {
311 
312 		// Check for odd negative scale (mirror)
313 		if (mesh->HasFaces() && mat.Determinant() < 0) {
314 			// Reverse the mesh face winding order
315 			FlipWindingOrderProcess::ProcessMesh(mesh);
316 		}
317 
318 		// Update positions
319 		if (mesh->HasPositions()) {
320 			for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
321 				mesh->mVertices[i] = mat * mesh->mVertices[i];
322 			}
323 		}
324 
325 		// Update normals and tangents
326 		if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
327 			const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
328 
329 			if (mesh->HasNormals()) {
330 				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
331 					mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
332 				}
333 			}
334 			if (mesh->HasTangentsAndBitangents()) {
335 				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
336 					mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
337 					mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
338 				}
339 			}
340 		}
341 	}
342 }
343 
344 // ------------------------------------------------------------------------------------------------
345 // Simple routine to build meshes in worldspace, no further optimization
BuildWCSMeshes(std::vector<aiMesh * > & out,aiMesh ** in,unsigned int numIn,aiNode * node) const346 void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **in,
347 		unsigned int numIn, aiNode *node) const {
348 	// NOTE:
349 	//  aiMesh::mNumBones store original source mesh, or UINT_MAX if not a copy
350 	//  aiMesh::mBones store reference to abs. transform we multiplied with
351 
352 	// process meshes
353 	for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
354 		aiMesh *mesh = in[node->mMeshes[i]];
355 
356 		// check whether we can operate on this mesh
357 		if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4 *>(mesh->mBones) == node->mTransformation) {
358 			// yes, we can.
359 			mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
360 			mesh->mNumBones = UINT_MAX;
361 		} else {
362 
363 			// try to find us in the list of newly created meshes
364 			for (unsigned int n = 0; n < out.size(); ++n) {
365 				aiMesh *ctz = out[n];
366 				if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
367 
368 					// ok, use this one. Update node mesh index
369 					node->mMeshes[i] = numIn + n;
370 				}
371 			}
372 			if (node->mMeshes[i] < numIn) {
373 				// Worst case. Need to operate on a full copy of the mesh
374 				ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
375 				aiMesh *ntz;
376 
377 				const unsigned int tmp = mesh->mNumBones; //
378 				mesh->mNumBones = 0;
379 				SceneCombiner::Copy(&ntz, mesh);
380 				mesh->mNumBones = tmp;
381 
382 				ntz->mNumBones = node->mMeshes[i];
383 				ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
384 
385 				out.push_back(ntz);
386 
387 				node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
388 			}
389 		}
390 	}
391 
392 	// call children
393 	for (unsigned int i = 0; i < node->mNumChildren; ++i)
394 		BuildWCSMeshes(out, in, numIn, node->mChildren[i]);
395 }
396 
397 // ------------------------------------------------------------------------------------------------
398 // Reset transformation matrices to identity
MakeIdentityTransform(aiNode * nd) const399 void PretransformVertices::MakeIdentityTransform(aiNode *nd) const {
400 	nd->mTransformation = aiMatrix4x4();
401 
402 	// call children
403 	for (unsigned int i = 0; i < nd->mNumChildren; ++i)
404 		MakeIdentityTransform(nd->mChildren[i]);
405 }
406 
407 // ------------------------------------------------------------------------------------------------
408 // Build reference counters for all meshes
BuildMeshRefCountArray(const aiNode * nd,unsigned int * refs) const409 void PretransformVertices::BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const {
410 	for (unsigned int i = 0; i < nd->mNumMeshes; ++i)
411 		refs[nd->mMeshes[i]]++;
412 
413 	// call children
414 	for (unsigned int i = 0; i < nd->mNumChildren; ++i)
415 		BuildMeshRefCountArray(nd->mChildren[i], refs);
416 }
417 
418 // ------------------------------------------------------------------------------------------------
419 // Executes the post processing step on the given imported data.
Execute(aiScene * pScene)420 void PretransformVertices::Execute(aiScene *pScene) {
421 	ASSIMP_LOG_DEBUG("PretransformVerticesProcess begin");
422 
423 	// Return immediately if we have no meshes
424 	if (!pScene->mNumMeshes)
425 		return;
426 
427 	const unsigned int iOldMeshes = pScene->mNumMeshes;
428 	const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
429 	const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
430 
431 	if (configTransform) {
432 		pScene->mRootNode->mTransformation = configTransformation * pScene->mRootNode->mTransformation;
433 	}
434 
435 	// first compute absolute transformation matrices for all nodes
436 	ComputeAbsoluteTransform(pScene->mRootNode);
437 
438 	// Delete aiMesh::mBones for all meshes. The bones are
439 	// removed during this step and we need the pointer as
440 	// temporary storage
441 	for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
442 		aiMesh *mesh = pScene->mMeshes[i];
443 
444 		for (unsigned int a = 0; a < mesh->mNumBones; ++a)
445 			delete mesh->mBones[a];
446 
447 		delete[] mesh->mBones;
448         mesh->mBones = nullptr;
449 	}
450 
451 	// now build a list of output meshes
452 	std::vector<aiMesh *> apcOutMeshes;
453 
454 	// Keep scene hierarchy? It's an easy job in this case ...
455 	// we go on and transform all meshes, if one is referenced by nodes
456 	// with different absolute transformations a depth copy of the mesh
457 	// is required.
458 	if (configKeepHierarchy) {
459 
460 		// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
461 		BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode);
462 
463 		// ... if new meshes have been generated, append them to the end of the scene
464 		if (apcOutMeshes.size() > 0) {
465 			aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
466 
467 			memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
468 			memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
469 
470 			pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
471 			delete[] pScene->mMeshes;
472 			pScene->mMeshes = npp;
473 		}
474 
475 		// now iterate through all meshes and transform them to world-space
476 		for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
477 			ApplyTransform(pScene->mMeshes[i], *reinterpret_cast<aiMatrix4x4 *>(pScene->mMeshes[i]->mBones));
478 
479 			// prevent improper destruction
480             pScene->mMeshes[i]->mBones = nullptr;
481 			pScene->mMeshes[i]->mNumBones = 0;
482 		}
483 	} else {
484 		apcOutMeshes.reserve(static_cast<size_t>(pScene->mNumMaterials) << 1u);
485 		std::list<unsigned int> aiVFormats;
486 
487 		std::vector<unsigned int> s(pScene->mNumMeshes, 0);
488 		BuildMeshRefCountArray(pScene->mRootNode, &s[0]);
489 
490 		for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
491 			// get the list of all vertex formats for this material
492 			aiVFormats.clear();
493 			GetVFormatList(pScene, i, aiVFormats);
494 			aiVFormats.sort();
495 			aiVFormats.unique();
496 			for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) {
497 				unsigned int iVertices = 0;
498 				unsigned int iFaces = 0;
499 				CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &iFaces, &iVertices);
500 				if (0 != iFaces && 0 != iVertices) {
501 					apcOutMeshes.push_back(new aiMesh());
502 					aiMesh *pcMesh = apcOutMeshes.back();
503 					pcMesh->mNumFaces = iFaces;
504 					pcMesh->mNumVertices = iVertices;
505 					pcMesh->mFaces = new aiFace[iFaces];
506 					pcMesh->mVertices = new aiVector3D[iVertices];
507 					pcMesh->mMaterialIndex = i;
508 					if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[iVertices];
509 					if ((*j) & 0x4) {
510 						pcMesh->mTangents = new aiVector3D[iVertices];
511 						pcMesh->mBitangents = new aiVector3D[iVertices];
512 					}
513 					iFaces = 0;
514 					while ((*j) & (0x100 << iFaces)) {
515 						pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
516 						if ((*j) & (0x10000 << iFaces))
517 							pcMesh->mNumUVComponents[iFaces] = 3;
518 						else
519 							pcMesh->mNumUVComponents[iFaces] = 2;
520 						iFaces++;
521 					}
522 					iFaces = 0;
523 					while ((*j) & (0x1000000 << iFaces))
524 						pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
525 
526 					// fill the mesh ...
527 					unsigned int aiTemp[2] = { 0, 0 };
528 					CollectData(pScene, pScene->mRootNode, i, *j, pcMesh, aiTemp, &s[0]);
529 				}
530 			}
531 		}
532 
533 		// If no meshes are referenced in the node graph it is possible that we get no output meshes.
534 		if (apcOutMeshes.empty()) {
535 
536 			throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by any nodes");
537 		} else {
538 			// now delete all meshes in the scene and build a new mesh list
539 			for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
540 				aiMesh *mesh = pScene->mMeshes[i];
541 				mesh->mNumBones = 0;
542                 mesh->mBones = nullptr;
543 
544 				// we're reusing the face index arrays. avoid destruction
545 				for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
546 					mesh->mFaces[a].mNumIndices = 0;
547                     mesh->mFaces[a].mIndices = nullptr;
548 				}
549 
550 				delete mesh;
551 
552 				// Invalidate the contents of the old mesh array. We will most
553 				// likely have less output meshes now, so the last entries of
554 				// the mesh array are not overridden. We set them to nullptr to
555 				// make sure the developer gets notified when his application
556 				// attempts to access these fields ...
557                 mesh = nullptr;
558 			}
559 
560 			// It is impossible that we have more output meshes than
561 			// input meshes, so we can easily reuse the old mesh array
562 			pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
563 			for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
564 				pScene->mMeshes[i] = apcOutMeshes[i];
565 			}
566 		}
567 	}
568 
569 	// remove all animations from the scene
570 	for (unsigned int i = 0; i < pScene->mNumAnimations; ++i)
571 		delete pScene->mAnimations[i];
572 	delete[] pScene->mAnimations;
573 
574 	pScene->mAnimations = nullptr;
575 	pScene->mNumAnimations = 0;
576 
577 	// --- we need to keep all cameras and lights
578 	for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
579 		aiCamera *cam = pScene->mCameras[i];
580 		const aiNode *nd = pScene->mRootNode->FindNode(cam->mName);
581         ai_assert(nullptr != nd);
582 
583 		// multiply all properties of the camera with the absolute
584 		// transformation of the corresponding node
585 		cam->mPosition = nd->mTransformation * cam->mPosition;
586 		cam->mLookAt = aiMatrix3x3(nd->mTransformation) * cam->mLookAt;
587 		cam->mUp = aiMatrix3x3(nd->mTransformation) * cam->mUp;
588 	}
589 
590 	for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
591 		aiLight *l = pScene->mLights[i];
592 		const aiNode *nd = pScene->mRootNode->FindNode(l->mName);
593         ai_assert(nullptr != nd);
594 
595 		// multiply all properties of the camera with the absolute
596 		// transformation of the corresponding node
597 		l->mPosition = nd->mTransformation * l->mPosition;
598 		l->mDirection = aiMatrix3x3(nd->mTransformation) * l->mDirection;
599 		l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp;
600 	}
601 
602 	if (!configKeepHierarchy) {
603 
604 		// now delete all nodes in the scene and build a new
605 		// flat node graph with a root node and some level 1 children
606 		aiNode *newRoot = new aiNode();
607 		newRoot->mName = pScene->mRootNode->mName;
608 		delete pScene->mRootNode;
609 		pScene->mRootNode = newRoot;
610 
611 		if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) {
612 			pScene->mRootNode->mNumMeshes = 1;
613 			pScene->mRootNode->mMeshes = new unsigned int[1];
614 			pScene->mRootNode->mMeshes[0] = 0;
615 		} else {
616 			pScene->mRootNode->mNumChildren = pScene->mNumMeshes + pScene->mNumLights + pScene->mNumCameras;
617 			aiNode **nodes = pScene->mRootNode->mChildren = new aiNode *[pScene->mRootNode->mNumChildren];
618 
619 			// generate mesh nodes
620 			for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++nodes) {
621 				aiNode *pcNode = new aiNode();
622 				*nodes = pcNode;
623 				pcNode->mParent = pScene->mRootNode;
624 				pcNode->mName = pScene->mMeshes[i]->mName;
625 
626 				// setup mesh indices
627 				pcNode->mNumMeshes = 1;
628 				pcNode->mMeshes = new unsigned int[1];
629 				pcNode->mMeshes[0] = i;
630 			}
631 			// generate light nodes
632 			for (unsigned int i = 0; i < pScene->mNumLights; ++i, ++nodes) {
633 				aiNode *pcNode = new aiNode();
634 				*nodes = pcNode;
635 				pcNode->mParent = pScene->mRootNode;
636 				pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u", i);
637 				pScene->mLights[i]->mName = pcNode->mName;
638 			}
639 			// generate camera nodes
640 			for (unsigned int i = 0; i < pScene->mNumCameras; ++i, ++nodes) {
641 				aiNode *pcNode = new aiNode();
642 				*nodes = pcNode;
643 				pcNode->mParent = pScene->mRootNode;
644 				pcNode->mName.length = ::ai_snprintf(pcNode->mName.data, MAXLEN, "cam_%u", i);
645 				pScene->mCameras[i]->mName = pcNode->mName;
646 			}
647 		}
648 	} else {
649 		// ... and finally set the transformation matrix of all nodes to identity
650 		MakeIdentityTransform(pScene->mRootNode);
651 	}
652 
653 	if (configNormalize) {
654 		// compute the boundary of all meshes
655 		aiVector3D min, max;
656 		MinMaxChooser<aiVector3D>()(min, max);
657 
658 		for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
659 			aiMesh *m = pScene->mMeshes[a];
660 			for (unsigned int i = 0; i < m->mNumVertices; ++i) {
661 				min = std::min(m->mVertices[i], min);
662 				max = std::max(m->mVertices[i], max);
663 			}
664 		}
665 
666 		// find the dominant axis
667 		aiVector3D d = max - min;
668 		const ai_real div = std::max(d.x, std::max(d.y, d.z)) * ai_real(0.5);
669 
670 		d = min + d * (ai_real)0.5;
671 		for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
672 			aiMesh *m = pScene->mMeshes[a];
673 			for (unsigned int i = 0; i < m->mNumVertices; ++i) {
674 				m->mVertices[i] = (m->mVertices[i] - d) / div;
675 			}
676 		}
677 	}
678 
679 	// print statistics
680 	if (!DefaultLogger::isNullLogger()) {
681 		ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
682 
683 		ASSIMP_LOG_INFO("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (",
684 				CountNodes(pScene->mRootNode), " output nodes)");
685 		ASSIMP_LOG_INFO("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras.");
686 		ASSIMP_LOG_INFO("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
687 	}
688 }
689