Coverage Report

Created: 2025-11-11 06:59

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