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