Coverage Report

Created: 2026-07-30 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/Common/SceneCombiner.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
42
// TODO: refactor entire file to get rid of the "flat-copy" first approach
43
// to copying structures. This easily breaks in the most unintuitive way
44
// possible as new fields are added to assimp structures.
45
46
// ----------------------------------------------------------------------------
47
/**
48
  * @file Implements Assimp::SceneCombiner. This is a smart utility
49
  *       class that combines multiple scenes, meshes, ... into one. Currently
50
  *       these utilities are used by the IRR and LWS loaders and the
51
  *       OptimizeGraph step.
52
  */
53
// ----------------------------------------------------------------------------
54
#include "ScenePrivate.h"
55
#include <assimp/Hash.h>
56
#include <assimp/SceneCombiner.h>
57
#include <assimp/StringUtils.h>
58
#include <assimp/fast_atof.h>
59
#include <assimp/mesh.h>
60
#include <assimp/metadata.h>
61
#include <assimp/scene.h>
62
#include <assimp/DefaultLogger.hpp>
63
64
#include <unordered_set>
65
#include <ctime>
66
#include <cstdio>
67
68
namespace Assimp {
69
70
#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0)
71
#  pragma GCC diagnostic push
72
#  pragma GCC diagnostic ignored "-Wclass-memaccess"
73
#endif
74
75
// ------------------------------------------------------------------------------------------------
76
// Add a prefix to a string
77
0
inline void PrefixString(aiString &string, const char *prefix, unsigned int len) {
78
    // If the string is already prefixed, we won't prefix it a second time
79
0
    if (string.length >= 1 && string.data[0] == '$')
80
0
        return;
81
82
0
    if (len + string.length >= AI_MAXLEN - 1) {
83
0
        ASSIMP_LOG_ERROR("Can't add an unique prefix because the string is too long");
84
0
        return;
85
0
    }
86
87
    // Add the prefix
88
0
    ::memmove(string.data + len, string.data, string.length + 1);
89
0
    ::memcpy(string.data, prefix, len);
90
91
    // And update the string's length
92
0
    string.length += len;
93
0
}
94
95
// ------------------------------------------------------------------------------------------------
96
// Add node identifiers to a hashing set
97
0
void SceneCombiner::AddNodeHashes(aiNode *node, std::set<unsigned int> &hashes) {
98
0
    if (node == nullptr) {
99
0
        ASSIMP_LOG_ERROR("Pointer to aiNode is nullptr.");
100
0
        return;
101
0
    }
102
103
    // Add node name to hashing set if it is non-empty - empty nodes are allowed
104
    // and they can't have any anims assigned so its absolutely safe to duplicate them.
105
0
    if (node->mName.length) {
106
0
        hashes.insert(SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)));
107
0
    }
108
109
    // Process all children recursively
110
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
111
0
        AddNodeHashes(node->mChildren[i], hashes);
112
0
    }
113
0
}
114
115
// ------------------------------------------------------------------------------------------------
116
// Add a name prefix to all nodes in a hierarchy
117
0
void SceneCombiner::AddNodePrefixes(aiNode *node, const char *prefix, unsigned int len) {
118
0
    if (prefix == nullptr) {
119
0
        ASSIMP_LOG_ERROR("Pointer to prefix is nullptr.");
120
0
        return;
121
0
    }
122
123
0
    PrefixString(node->mName, prefix, len);
124
125
    // Process all children recursively
126
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
127
0
        AddNodePrefixes(node->mChildren[i], prefix, len);
128
0
    }
129
0
}
130
131
// ------------------------------------------------------------------------------------------------
132
// Search for matching names
133
0
bool SceneCombiner::FindNameMatch(const aiString &name, std::vector<SceneHelper> &input, unsigned int cur) {
134
0
    const unsigned int hash = SuperFastHash(name.data, static_cast<uint32_t>(name.length));
135
136
    // Check whether we find a positive match in one of the given sets
137
0
    for (unsigned int i = 0; i < input.size(); ++i) {
138
0
        if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
139
0
            return true;
140
0
        }
141
0
    }
142
0
    return false;
143
0
}
144
145
// ------------------------------------------------------------------------------------------------
146
// Add a name prefix to all nodes in a hierarchy if a hash match is found
147
void SceneCombiner::AddNodePrefixesChecked(aiNode *node, const char *prefix, unsigned int len,
148
0
        std::vector<SceneHelper> &input, unsigned int cur) {
149
0
    if (prefix == nullptr) {
150
0
        ASSIMP_LOG_ERROR("Pointer to prefix is nullptr.");
151
0
        return;
152
0
    }
153
154
0
    const unsigned int hash = SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length));
155
156
    // Check whether we find a positive match in one of the given sets
157
0
    for (unsigned int i = 0; i < input.size(); ++i) {
158
0
        if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
159
0
            PrefixString(node->mName, prefix, len);
160
0
            break;
161
0
        }
162
0
    }
163
164
    // Process all children recursively
165
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
166
0
        AddNodePrefixesChecked(node->mChildren[i], prefix, len, input, cur);
167
0
    }
168
0
}
169
170
// ------------------------------------------------------------------------------------------------
171
// Add an offset to all mesh indices in a node graph
172
0
void SceneCombiner::OffsetNodeMeshIndices(aiNode *node, unsigned int offset) {
173
0
    for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
174
0
        node->mMeshes[i] += offset;
175
0
    }
176
177
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
178
0
        OffsetNodeMeshIndices(node->mChildren[i], offset);
179
0
    }
180
0
}
181
182
// ------------------------------------------------------------------------------------------------
183
// Merges two scenes. Currently only used by the LWS loader.
184
0
void SceneCombiner::MergeScenes(aiScene **_dest, std::vector<aiScene *> &src, unsigned int flags) {
185
0
    if (nullptr == _dest) {
186
0
        ASSIMP_LOG_ERROR("Pointer to destination scene is nullptr.");
187
0
        return;
188
0
    }
189
190
    // if _dest points to nullptr allocate a new scene. Otherwise clear the old and reuse it
191
0
    if (src.empty()) {
192
0
        if (*_dest) {
193
0
            (*_dest)->~aiScene();
194
0
            SceneCombiner::CopySceneFlat(_dest, src[0]);
195
0
        } else
196
0
            *_dest = src[0];
197
0
        return;
198
0
    }
199
0
    if (*_dest) {
200
0
        (*_dest)->~aiScene();
201
0
        new (*_dest) aiScene();
202
0
    } else
203
0
        *_dest = new aiScene();
204
205
    // Create a dummy scene to serve as master for the others
206
0
    aiScene *master = new aiScene();
207
0
    master->mRootNode = new aiNode();
208
0
    master->mRootNode->mName.Set("<MergeRoot>");
209
210
0
    std::vector<AttachmentInfo> srcList(src.size());
211
0
    for (unsigned int i = 0; i < srcList.size(); ++i) {
212
0
        srcList[i] = AttachmentInfo(src[i], master->mRootNode);
213
0
    }
214
215
    // 'master' will be deleted afterwards
216
0
    MergeScenes(_dest, master, srcList, flags);
217
0
}
218
219
// ------------------------------------------------------------------------------------------------
220
0
void SceneCombiner::AttachToGraph(aiNode *attach, std::vector<NodeAttachmentInfo> &srcList) {
221
0
    unsigned int cnt{0};
222
0
    for (cnt = 0; cnt < attach->mNumChildren; ++cnt) {
223
0
        AttachToGraph(attach->mChildren[cnt], srcList);
224
0
    }
225
226
0
    cnt = 0;
227
0
    for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin();
228
0
            it != srcList.end(); ++it) {
229
0
        if ((*it).attachToNode == attach && !(*it).resolved) {
230
0
            ++cnt;
231
0
        }
232
0
    }
233
234
0
    if (cnt) {
235
0
        aiNode **n = new aiNode *[cnt + attach->mNumChildren];
236
0
        if (attach->mNumChildren) {
237
0
            ::memcpy(n, attach->mChildren, sizeof(void *) * attach->mNumChildren);
238
0
            delete[] attach->mChildren;
239
0
        }
240
0
        attach->mChildren = n;
241
242
0
        n += attach->mNumChildren;
243
0
        attach->mNumChildren += cnt;
244
245
0
        for (unsigned int i = 0; i < srcList.size(); ++i) {
246
0
            NodeAttachmentInfo &att = srcList[i];
247
0
            if (att.attachToNode == attach && !att.resolved) {
248
0
                *n = att.node;
249
0
                (**n).mParent = attach;
250
0
                ++n;
251
252
                // mark this attachment as resolved
253
0
                att.resolved = true;
254
0
            }
255
0
        }
256
0
    }
257
0
}
258
259
// ------------------------------------------------------------------------------------------------
260
0
void SceneCombiner::AttachToGraph(aiScene *master, std::vector<NodeAttachmentInfo> &src) {
261
0
    ai_assert(nullptr != master);
262
263
0
    AttachToGraph(master->mRootNode, src);
264
0
}
265
266
// ------------------------------------------------------------------------------------------------
267
1.33k
void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector<AttachmentInfo> &srcList, unsigned int flags) {
268
1.33k
    if (nullptr == _dest) {
269
0
        std::unordered_set<aiScene *> uniqueScenes;
270
0
        uniqueScenes.insert(master);
271
0
        for (const auto &item : srcList) {
272
0
            uniqueScenes.insert(item.scene);
273
0
        }
274
0
        for (const auto &item : uniqueScenes) {
275
0
            delete item;
276
0
        }
277
0
        return;
278
0
    }
279
280
    // if _dest points to nullptr allocate a new scene. Otherwise clear the old and reuse it
281
1.33k
    if (srcList.empty()) {
282
1.33k
        if (*_dest) {
283
1.33k
            SceneCombiner::CopySceneFlat(_dest, master);
284
1.33k
            delete master;
285
1.33k
        } else
286
0
            *_dest = master;
287
1.33k
        return;
288
1.33k
    }
289
0
    if (*_dest) {
290
0
        (*_dest)->~aiScene();
291
0
        new (*_dest) aiScene();
292
0
    } else
293
0
        *_dest = new aiScene();
294
295
0
    aiScene *dest = *_dest;
296
297
0
    std::vector<SceneHelper> src(srcList.size() + 1);
298
0
    src[0].scene = master;
299
0
    for (unsigned int i = 0; i < srcList.size(); ++i) {
300
0
        src[i + 1] = SceneHelper(srcList[i].scene);
301
0
    }
302
303
    // this helper array specifies which scenes are duplicates of others
304
0
    std::vector<unsigned int> duplicates(src.size(), UINT_MAX);
305
306
    // this helper array is used as lookup table several times
307
0
    std::vector<unsigned int> offset(src.size());
308
309
    // Find duplicate scenes
310
0
    for (unsigned int i = 0; i < src.size(); ++i) {
311
0
        if (duplicates[i] != i && duplicates[i] != UINT_MAX) {
312
0
            continue;
313
0
        }
314
315
0
        duplicates[i] = i;
316
0
        for (unsigned int a = i + 1; a < src.size(); ++a) {
317
0
            if (src[i].scene == src[a].scene) {
318
0
                duplicates[a] = i;
319
0
            }
320
0
        }
321
0
    }
322
323
    // Generate unique names for all named stuff?
324
0
    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
325
0
        for (unsigned int i = 1; i < src.size(); ++i) {
326
0
            src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_", i);
327
328
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
329
330
                // Compute hashes for all identifiers in this scene and store them
331
                // in a sorted table (for convenience I'm using std::set). We hash
332
                // just the node and animation channel names, all identifiers except
333
                // the material names should be caught by doing this.
334
0
                AddNodeHashes(src[i]->mRootNode, src[i].hashes);
335
336
0
                for (unsigned int a = 0; a < src[i]->mNumAnimations; ++a) {
337
0
                    aiAnimation *anim = src[i]->mAnimations[a];
338
0
                    src[i].hashes.insert(SuperFastHash(anim->mName.data, static_cast<uint32_t>(anim->mName.length)));
339
0
                }
340
0
            }
341
0
        }
342
0
    }
343
344
0
    unsigned int cnt;
345
346
    // First find out how large the respective output arrays must be
347
0
    for (unsigned int n = 0; n < src.size(); ++n) {
348
0
        SceneHelper *cur = &src[n];
349
350
0
        if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
351
0
            dest->mNumTextures += (*cur)->mNumTextures;
352
0
            dest->mNumMaterials += (*cur)->mNumMaterials;
353
0
            dest->mNumMeshes += (*cur)->mNumMeshes;
354
0
        }
355
356
0
        dest->mNumLights += (*cur)->mNumLights;
357
0
        dest->mNumCameras += (*cur)->mNumCameras;
358
0
        dest->mNumAnimations += (*cur)->mNumAnimations;
359
360
        // Combine the flags of all scenes
361
        // We need to process them flag-by-flag here to get correct results
362
        // dest->mFlags ; //|= (*cur)->mFlags;
363
0
        if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
364
0
            dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
365
0
        }
366
0
    }
367
368
    // generate the output texture list + an offset table for all texture indices
369
0
    if (dest->mNumTextures) {
370
0
        aiTexture **pip = dest->mTextures = new aiTexture *[dest->mNumTextures];
371
0
        cnt = 0;
372
0
        for (unsigned int n = 0; n < src.size(); ++n) {
373
0
            SceneHelper *cur = &src[n];
374
0
            for (unsigned int i = 0; i < (*cur)->mNumTextures; ++i) {
375
0
                if (n != duplicates[n]) {
376
0
                    if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
377
0
                        Copy(pip, (*cur)->mTextures[i]);
378
0
                    } else {
379
0
                        continue;
380
0
                    }
381
0
                } else {
382
0
                    *pip = (*cur)->mTextures[i];
383
0
                }
384
0
                ++pip;
385
0
            }
386
387
0
            offset[n] = cnt;
388
0
            cnt = (unsigned int)(pip - dest->mTextures);
389
0
        }
390
0
    }
391
392
    // generate the output material list + an offset table for all material indices
393
0
    if (dest->mNumMaterials) {
394
0
        aiMaterial **pip = dest->mMaterials = new aiMaterial *[dest->mNumMaterials];
395
0
        cnt = 0;
396
0
        for (unsigned int n = 0; n < src.size(); ++n) {
397
0
            SceneHelper *cur = &src[n];
398
0
            for (unsigned int i = 0; i < (*cur)->mNumMaterials; ++i) {
399
0
                if (n != duplicates[n]) {
400
0
                    if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
401
0
                        Copy(pip, (*cur)->mMaterials[i]);
402
0
                    } else {
403
0
                        continue;
404
0
                    }
405
0
                } else {
406
0
                    *pip = (*cur)->mMaterials[i];
407
0
                }
408
409
0
                if ((*cur)->mNumTextures != dest->mNumTextures) {
410
                    // We need to update all texture indices of the mesh. So we need to search for
411
                    // a material property called '$tex.file'
412
413
0
                    for (unsigned int a = 0; a < (*pip)->mNumProperties; ++a) {
414
0
                        aiMaterialProperty *prop = (*pip)->mProperties[a];
415
0
                        if (!strncmp(prop->mKey.data, "$tex.file", 9)) {
416
                            // Check whether this texture is an embedded texture.
417
                            // In this case the property looks like this: *<n>,
418
                            // where n is the index of the texture.
419
                            // Copy here because we overwrite the string data in-place and the buffer inside of aiString
420
                            // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be
421
                            // AI_MAXLEN in size.
422
0
                            aiString s(*(aiString *)prop->mData);
423
0
                            if ('*' == s.data[0]) {
424
                                // Offset the index and write it back ..
425
0
                                const unsigned int idx = strtoul10(&s.data[1]) + offset[n];
426
0
                                const unsigned int oldLen = s.length;
427
428
0
                                s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx);
429
430
                                // The string changed in size so we need to reallocate the buffer for the property.
431
0
                                if (oldLen < s.length) {
432
0
                                    prop->mDataLength += s.length - oldLen;
433
0
                                    delete[] prop->mData;
434
0
                                    prop->mData = new char[prop->mDataLength];
435
0
                                }
436
437
0
                                memcpy(prop->mData, static_cast<void*>(&s), prop->mDataLength);
438
0
                            }
439
0
                        }
440
441
                        // Need to generate new, unique material names?
442
0
                        else if (!::strcmp(prop->mKey.data, "$mat.name") && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) {
443
0
                            aiString *pcSrc = (aiString *)prop->mData;
444
0
                            PrefixString(*pcSrc, (*cur).id, (*cur).idlen);
445
0
                        }
446
0
                    }
447
0
                }
448
0
                ++pip;
449
0
            }
450
451
0
            offset[n] = cnt;
452
0
            cnt = (unsigned int)(pip - dest->mMaterials);
453
0
        }
454
0
    }
455
456
    // generate the output mesh list + again an offset table for all mesh indices
457
0
    if (dest->mNumMeshes) {
458
0
        aiMesh **pip = dest->mMeshes = new aiMesh *[dest->mNumMeshes];
459
0
        cnt = 0;
460
0
        for (unsigned int n = 0; n < src.size(); ++n) {
461
0
            SceneHelper *cur = &src[n];
462
0
            for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) {
463
0
                if (n != duplicates[n]) {
464
0
                    if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
465
0
                        Copy(pip, (*cur)->mMeshes[i]);
466
0
                    } else {
467
0
                        continue;
468
0
                    }
469
0
                } else {
470
0
                    *pip = (*cur)->mMeshes[i];
471
0
                }
472
473
                // update the material index of the mesh
474
0
                if ((*pip) != nullptr) {
475
0
                    (*pip)->mMaterialIndex += offset[n];
476
0
                } else {
477
0
                    ASSIMP_LOG_ERROR("CopyMeshes: Missing mesh instance found, skipped.");
478
0
                }
479
0
                ++pip;
480
0
            }
481
482
            // reuse the offset array - store now the mesh offset in it
483
0
            offset[n] = cnt;
484
0
            cnt = (unsigned int)(pip - dest->mMeshes);
485
0
        }
486
0
    }
487
488
0
    std::vector<NodeAttachmentInfo> nodes;
489
0
    nodes.reserve(srcList.size());
490
491
    // ----------------------------------------------------------------------------
492
    // Now generate the output node graph. We need to make those
493
    // names in the graph that are referenced by anims or lights
494
    // or cameras unique. So we add a prefix to them ... $<rand>_
495
    // We could also use a counter, but using a random value allows us to
496
    // use just one prefix if we are joining multiple scene hierarchies recursively.
497
    // Chances are quite good we don't collide, so we try that ...
498
    // ----------------------------------------------------------------------------
499
500
    // Allocate space for light sources, cameras and animations
501
0
    aiLight **ppLights = dest->mLights = (dest->mNumLights ? new aiLight *[dest->mNumLights] : nullptr);
502
503
0
    aiCamera **ppCameras = dest->mCameras = (dest->mNumCameras ? new aiCamera *[dest->mNumCameras] : nullptr);
504
505
0
    aiAnimation **ppAnims = dest->mAnimations = (dest->mNumAnimations ? new aiAnimation *[dest->mNumAnimations] : nullptr);
506
507
0
    for (int n = static_cast<int>(src.size() - 1); n >= 0; --n) /* !!! important !!! */
508
0
    {
509
0
        SceneHelper *cur = &src[n];
510
0
        aiNode *node;
511
512
        // To offset or not to offset, this is the question
513
0
        if (n != (int)duplicates[n]) {
514
            // Get full scene-graph copy
515
0
            Copy(&node, (*cur)->mRootNode);
516
0
            OffsetNodeMeshIndices(node, offset[duplicates[n]]);
517
518
0
            if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
519
                // (note:) they are already 'offseted' by offset[duplicates[n]]
520
0
                OffsetNodeMeshIndices(node, offset[n] - offset[duplicates[n]]);
521
0
            }
522
0
        } else // if (n == duplicates[n])
523
0
        {
524
0
            node = (*cur)->mRootNode;
525
0
            OffsetNodeMeshIndices(node, offset[n]);
526
0
        }
527
0
        if (n) // src[0] is the master node
528
0
            nodes.emplace_back(node, srcList[n - 1].attachToNode, n);
529
530
        // add name prefixes?
531
0
        if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
532
533
            // or the whole scenegraph
534
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
535
0
                AddNodePrefixesChecked(node, (*cur).id, (*cur).idlen, src, n);
536
0
            } else
537
0
                AddNodePrefixes(node, (*cur).id, (*cur).idlen);
538
539
            // meshes
540
0
            for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) {
541
0
                aiMesh *mesh = (*cur)->mMeshes[i];
542
543
                // rename all bones
544
0
                for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
545
0
                    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
546
0
                        if (!FindNameMatch(mesh->mBones[a]->mName, src, n))
547
0
                            continue;
548
0
                    }
549
0
                    PrefixString(mesh->mBones[a]->mName, (*cur).id, (*cur).idlen);
550
0
                }
551
0
            }
552
0
        }
553
554
        // --------------------------------------------------------------------
555
        // Copy light sources
556
0
        for (unsigned int i = 0; i < (*cur)->mNumLights; ++i, ++ppLights) {
557
0
            if (n != (int)duplicates[n]) // duplicate scene?
558
0
            {
559
0
                Copy(ppLights, (*cur)->mLights[i]);
560
0
            } else
561
0
                *ppLights = (*cur)->mLights[i];
562
563
            // Add name prefixes?
564
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
565
0
                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
566
0
                    if (!FindNameMatch((*ppLights)->mName, src, n))
567
0
                        continue;
568
0
                }
569
570
0
                PrefixString((*ppLights)->mName, (*cur).id, (*cur).idlen);
571
0
            }
572
0
        }
573
574
        // --------------------------------------------------------------------
575
        // Copy cameras
576
0
        for (unsigned int i = 0; i < (*cur)->mNumCameras; ++i, ++ppCameras) {
577
0
            if (n != (int)duplicates[n]) // duplicate scene?
578
0
            {
579
0
                Copy(ppCameras, (*cur)->mCameras[i]);
580
0
            } else
581
0
                *ppCameras = (*cur)->mCameras[i];
582
583
            // Add name prefixes?
584
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
585
0
                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
586
0
                    if (!FindNameMatch((*ppCameras)->mName, src, n))
587
0
                        continue;
588
0
                }
589
590
0
                PrefixString((*ppCameras)->mName, (*cur).id, (*cur).idlen);
591
0
            }
592
0
        }
593
594
        // --------------------------------------------------------------------
595
        // Copy animations
596
0
        for (unsigned int i = 0; i < (*cur)->mNumAnimations; ++i, ++ppAnims) {
597
0
            if (n != (int)duplicates[n]) // duplicate scene?
598
0
            {
599
0
                Copy(ppAnims, (*cur)->mAnimations[i]);
600
0
            } else
601
0
                *ppAnims = (*cur)->mAnimations[i];
602
603
            // Add name prefixes?
604
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
605
0
                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
606
0
                    if (!FindNameMatch((*ppAnims)->mName, src, n))
607
0
                        continue;
608
0
                }
609
610
0
                PrefixString((*ppAnims)->mName, (*cur).id, (*cur).idlen);
611
612
                // don't forget to update all node animation channels
613
0
                for (unsigned int a = 0; a < (*ppAnims)->mNumChannels; ++a) {
614
0
                    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
615
0
                        if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName, src, n))
616
0
                            continue;
617
0
                    }
618
619
0
                    PrefixString((*ppAnims)->mChannels[a]->mNodeName, (*cur).id, (*cur).idlen);
620
0
                }
621
0
            }
622
0
        }
623
0
    }
624
625
    // Now build the output graph
626
0
    AttachToGraph(master, nodes);
627
0
    dest->mRootNode = master->mRootNode;
628
629
    // Check whether we succeeded at building the output graph
630
0
    for (std::vector<NodeAttachmentInfo>::iterator it = nodes.begin();
631
0
            it != nodes.end(); ++it) {
632
0
        if (!(*it).resolved) {
633
0
            if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) {
634
                // search for this attachment point in all other imported scenes, too.
635
0
                for (unsigned int n = 0; n < src.size(); ++n) {
636
0
                    if (n != (*it).src_idx) {
637
0
                        AttachToGraph(src[n].scene, nodes);
638
0
                        if ((*it).resolved)
639
0
                            break;
640
0
                    }
641
0
                }
642
0
            }
643
0
            if (!(*it).resolved) {
644
0
                ASSIMP_LOG_ERROR("SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data,
645
0
                        " ", (*it).attachToNode->mName.data);
646
0
            }
647
0
        }
648
0
    }
649
650
    // now delete all input scenes. Make sure duplicate scenes aren't
651
    // deleted more than one time
652
0
    for (unsigned int n = 0; n < src.size(); ++n) {
653
0
        if (n != duplicates[n]) // duplicate scene?
654
0
            continue;
655
656
0
        aiScene *deleteMe = src[n].scene;
657
658
        // We need to delete the arrays before the destructor is called -
659
        // we are reusing the array members
660
0
        delete[] deleteMe->mMeshes;
661
0
        deleteMe->mMeshes = nullptr;
662
0
        delete[] deleteMe->mCameras;
663
0
        deleteMe->mCameras = nullptr;
664
0
        delete[] deleteMe->mLights;
665
0
        deleteMe->mLights = nullptr;
666
0
        delete[] deleteMe->mMaterials;
667
0
        deleteMe->mMaterials = nullptr;
668
0
        delete[] deleteMe->mAnimations;
669
0
        deleteMe->mAnimations = nullptr;
670
0
        delete[] deleteMe->mTextures;
671
0
        deleteMe->mTextures = nullptr;
672
673
0
        deleteMe->mRootNode = nullptr;
674
675
        // Now we can safely delete the scene
676
0
        delete deleteMe;
677
0
    }
678
679
    // Check flags
680
0
    if (!dest->mNumMeshes || !dest->mNumMaterials) {
681
0
        dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
682
0
    }
683
684
    // We're finished
685
0
}
686
687
// ------------------------------------------------------------------------------------------------
688
// Build a list of unique bones
689
void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash> &asBones,
690
        std::vector<aiMesh *>::const_iterator it,
691
0
        std::vector<aiMesh *>::const_iterator end) {
692
0
    unsigned int iOffset = 0;
693
0
    for (; it != end; ++it) {
694
0
        for (unsigned int l = 0; l < (*it)->mNumBones; ++l) {
695
0
            aiBone *p = (*it)->mBones[l];
696
0
            uint32_t itml = SuperFastHash(p->mName.data, (unsigned int)p->mName.length);
697
698
0
            std::list<BoneWithHash>::iterator it2 = asBones.begin();
699
0
            std::list<BoneWithHash>::iterator end2 = asBones.end();
700
701
0
            for (; it2 != end2; ++it2) {
702
0
                if ((*it2).first == itml) {
703
0
                    (*it2).pSrcBones.emplace_back(p, iOffset);
704
0
                    break;
705
0
                }
706
0
            }
707
0
            if (end2 == it2) {
708
                // need to begin a new bone entry
709
0
                asBones.emplace_back();
710
0
                BoneWithHash &btz = asBones.back();
711
712
                // setup members
713
0
                btz.first = itml;
714
0
                btz.second = &p->mName;
715
0
                btz.pSrcBones.emplace_back(p, iOffset);
716
0
            }
717
0
        }
718
0
        iOffset += (*it)->mNumVertices;
719
0
    }
720
0
}
721
722
// ------------------------------------------------------------------------------------------------
723
// Merge a list of bones
724
void SceneCombiner::MergeBones(aiMesh *out, std::vector<aiMesh *>::const_iterator it,
725
0
        std::vector<aiMesh *>::const_iterator end) {
726
0
    if (nullptr == out || out->mNumBones == 0) {
727
0
        return;
728
0
    }
729
730
    // find we need to build an unique list of all bones.
731
    // we work with hashes to make the comparisons MUCH faster,
732
    // at least if we have many bones.
733
0
    std::list<BoneWithHash> asBones;
734
0
    BuildUniqueBoneList(asBones, it, end);
735
736
    // now create the output bones
737
0
    out->mNumBones = 0;
738
0
    out->mBones = new aiBone *[asBones.size()];
739
740
0
    for (std::list<BoneWithHash>::const_iterator boneIt = asBones.begin(), boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt) {
741
        // Allocate a bone and setup it's name
742
0
        aiBone *pc = out->mBones[out->mNumBones++] = new aiBone();
743
0
        pc->mName = aiString(*(boneIt->second));
744
745
0
        std::vector<BoneSrcIndex>::const_iterator wend = boneIt->pSrcBones.end();
746
747
        // Loop through all bones to be joined for this bone
748
0
        for (std::vector<BoneSrcIndex>::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit) {
749
0
            pc->mNumWeights += (*wmit).first->mNumWeights;
750
751
            // NOTE: different offset matrices for bones with equal names
752
            // are - at the moment - not handled correctly.
753
0
            if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) {
754
0
                ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment");
755
0
                continue;
756
0
            }
757
0
            pc->mOffsetMatrix = wmit->first->mOffsetMatrix;
758
0
        }
759
760
        // Allocate the vertex weight array
761
0
        aiVertexWeight *avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
762
763
        // And copy the final weights - adjust the vertex IDs by the
764
        // face index offset of the corresponding mesh.
765
0
        for (std::vector<BoneSrcIndex>::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) {
766
0
            if (wmit == wend) {
767
0
                break;
768
0
            }
769
770
0
            aiBone *pip = (*wmit).first;
771
0
            for (unsigned int mp = 0; mp < pip->mNumWeights; ++mp, ++avw) {
772
0
                const aiVertexWeight &vfi = pip->mWeights[mp];
773
0
                avw->mWeight = vfi.mWeight;
774
0
                avw->mVertexId = vfi.mVertexId + (*wmit).second;
775
0
            }
776
0
        }
777
0
    }
778
0
}
779
780
// ------------------------------------------------------------------------------------------------
781
// Merge a list of meshes
782
void SceneCombiner::MergeMeshes(aiMesh **_out, unsigned int /*flags*/,
783
        std::vector<aiMesh *>::const_iterator begin,
784
302
        std::vector<aiMesh *>::const_iterator end) {
785
302
    if (nullptr == _out) {
786
0
        return;
787
0
    }
788
789
302
    if (begin == end) {
790
0
        *_out = nullptr; // no meshes ...
791
0
        return;
792
0
    }
793
794
    // Allocate the output mesh
795
302
    aiMesh *out = *_out = new aiMesh();
796
302
    out->mMaterialIndex = (*begin)->mMaterialIndex;
797
798
302
    std::string name;
799
    // Find out how much output storage we'll need
800
4.44k
    for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
801
4.14k
        const char *meshName((*it)->mName.C_Str());
802
4.14k
        name += std::string(meshName);
803
4.14k
        if (it != end - 1) {
804
3.83k
            name += ".";
805
3.83k
        }
806
4.14k
        out->mNumVertices += (*it)->mNumVertices;
807
4.14k
        out->mNumFaces += (*it)->mNumFaces;
808
4.14k
        out->mNumBones += (*it)->mNumBones;
809
810
        // combine primitive type flags
811
4.14k
        out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
812
4.14k
    }
813
302
    out->mName.Set(name.c_str());
814
815
302
    if (out->mNumVertices) {
816
256
        aiVector3D *pv2;
817
818
        // copy vertex positions
819
256
        if ((**begin).HasPositions()) {
820
821
256
            pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
822
2.07k
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
823
1.82k
                if ((*it)->mVertices) {
824
1.82k
                    ::memcpy(pv2, (*it)->mVertices, (*it)->mNumVertices * sizeof(aiVector3D));
825
1.82k
                } else
826
1.82k
                    ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions");
827
1.82k
                pv2 += (*it)->mNumVertices;
828
1.82k
            }
829
256
        }
830
        // copy normals
831
256
        if ((**begin).HasNormals()) {
832
833
63
            pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
834
262
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
835
199
                if ((*it)->mNormals) {
836
199
                    ::memcpy(pv2, (*it)->mNormals, (*it)->mNumVertices * sizeof(aiVector3D));
837
199
                } else {
838
0
                    ASSIMP_LOG_WARN("JoinMeshes: Normals expected but input mesh contains no normals");
839
0
                }
840
199
                pv2 += (*it)->mNumVertices;
841
199
            }
842
63
        }
843
        // copy tangents and bi-tangents
844
256
        if ((**begin).HasTangentsAndBitangents()) {
845
846
0
            pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
847
0
            aiVector3D *pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
848
849
0
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
850
0
                if ((*it)->mTangents) {
851
0
                    ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices * sizeof(aiVector3D));
852
0
                    ::memcpy(pv2b, (*it)->mBitangents, (*it)->mNumVertices * sizeof(aiVector3D));
853
0
                } else {
854
0
                    ASSIMP_LOG_WARN("JoinMeshes: Tangents expected but input mesh contains no tangents");
855
0
                }
856
0
                pv2 += (*it)->mNumVertices;
857
0
                pv2b += (*it)->mNumVertices;
858
0
            }
859
0
        }
860
        // copy texture coordinates
861
256
        unsigned int n = 0;
862
307
        while ((**begin).HasTextureCoords(n)) {
863
51
            out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n];
864
865
51
            pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
866
283
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
867
232
                if ((*it)->mTextureCoords[n]) {
868
232
                    ::memcpy(pv2, (*it)->mTextureCoords[n], (*it)->mNumVertices * sizeof(aiVector3D));
869
232
                } else {
870
0
                    ASSIMP_LOG_WARN("JoinMeshes: UVs expected but input mesh contains no UVs");
871
0
                }
872
232
                pv2 += (*it)->mNumVertices;
873
232
            }
874
51
            ++n;
875
51
        }
876
        // copy vertex colors
877
256
        n = 0;
878
286
        while ((**begin).HasVertexColors(n)) {
879
30
            aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
880
131
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
881
101
                if ((*it)->mColors[n]) {
882
101
                    ::memcpy(pVec2, (*it)->mColors[n], (*it)->mNumVertices * sizeof(aiColor4D));
883
101
                } else {
884
0
                    ASSIMP_LOG_WARN("JoinMeshes: VCs expected but input mesh contains no VCs");
885
0
                }
886
101
                pVec2 += (*it)->mNumVertices;
887
101
            }
888
30
            ++n;
889
30
        }
890
256
    }
891
892
302
    if (out->mNumFaces) // just for safety
893
256
    {
894
        // copy faces
895
256
        out->mFaces = new aiFace[out->mNumFaces];
896
256
        aiFace *pf2 = out->mFaces;
897
898
256
        unsigned int ofs = 0;
899
2.07k
        for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
900
32.1k
            for (unsigned int m = 0; m < (*it)->mNumFaces; ++m, ++pf2) {
901
30.3k
                aiFace &face = (*it)->mFaces[m];
902
30.3k
                pf2->mNumIndices = face.mNumIndices;
903
30.3k
                pf2->mIndices = face.mIndices;
904
905
30.3k
                if (ofs) {
906
                    // add the offset to the vertex
907
97.0k
                    for (unsigned int q = 0; q < face.mNumIndices; ++q) {
908
72.5k
                        face.mIndices[q] += ofs;
909
72.5k
                    }
910
24.5k
                }
911
30.3k
                face.mIndices = nullptr;
912
30.3k
            }
913
1.82k
            ofs += (*it)->mNumVertices;
914
1.82k
        }
915
256
    }
916
917
    // bones - as this is quite lengthy, I moved the code to a separate function
918
302
    if (out->mNumBones)
919
0
        MergeBones(out, begin, end);
920
921
    // delete all source meshes
922
4.44k
    for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it)
923
4.14k
        delete *it;
924
302
}
925
926
// ------------------------------------------------------------------------------------------------
927
void SceneCombiner::MergeMaterials(aiMaterial **dest,
928
        std::vector<aiMaterial *>::const_iterator begin,
929
0
        std::vector<aiMaterial *>::const_iterator end) {
930
0
    if (nullptr == dest) {
931
0
        return;
932
0
    }
933
934
0
    if (begin == end) {
935
0
        *dest = nullptr; // no materials ...
936
0
        return;
937
0
    }
938
939
    // Allocate the output material
940
0
    aiMaterial *out = *dest = new aiMaterial();
941
942
    // Get the maximal number of properties
943
0
    unsigned int size = 0;
944
0
    for (std::vector<aiMaterial *>::const_iterator it = begin; it != end; ++it) {
945
0
        size += (*it)->mNumProperties;
946
0
    }
947
948
0
    out->Clear();
949
0
    delete[] out->mProperties;
950
951
0
    out->mNumAllocated = size;
952
0
    out->mNumProperties = 0;
953
0
    out->mProperties = new aiMaterialProperty *[out->mNumAllocated];
954
955
0
    for (std::vector<aiMaterial *>::const_iterator it = begin; it != end; ++it) {
956
0
        for (unsigned int i = 0; i < (*it)->mNumProperties; ++i) {
957
0
            aiMaterialProperty *sprop = (*it)->mProperties[i];
958
959
            // Test if we already have a matching property
960
0
            const aiMaterialProperty *prop_exist;
961
0
            if (aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) {
962
                // If not, we add it to the new material
963
0
                aiMaterialProperty *prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty();
964
965
0
                prop->mDataLength = sprop->mDataLength;
966
0
                prop->mData = new char[prop->mDataLength];
967
0
                ::memcpy(prop->mData, sprop->mData, prop->mDataLength);
968
969
0
                prop->mIndex = sprop->mIndex;
970
0
                prop->mSemantic = sprop->mSemantic;
971
0
                prop->mKey = sprop->mKey;
972
0
                prop->mType = sprop->mType;
973
974
0
                out->mNumProperties++;
975
0
            }
976
0
        }
977
0
    }
978
0
}
979
980
// ------------------------------------------------------------------------------------------------
981
template <typename Type>
982
901k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
901k
    if (!num) {
984
689k
        dest = nullptr;
985
689k
        return;
986
689k
    }
987
212k
    dest = new Type *[num];
988
748k
    for (ai_uint i = 0; i < num; ++i) {
989
535k
        SceneCombiner::Copy(&dest[i], src[i]);
990
535k
    }
991
212k
}
void Assimp::CopyPtrArray<aiAnimation>(aiAnimation**&, aiAnimation const* const*, unsigned int)
Line
Count
Source
982
41.5k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
41.5k
    if (!num) {
984
37.5k
        dest = nullptr;
985
37.5k
        return;
986
37.5k
    }
987
4.03k
    dest = new Type *[num];
988
9.24k
    for (ai_uint i = 0; i < num; ++i) {
989
5.20k
        SceneCombiner::Copy(&dest[i], src[i]);
990
5.20k
    }
991
4.03k
}
void Assimp::CopyPtrArray<aiTexture>(aiTexture**&, aiTexture const* const*, unsigned int)
Line
Count
Source
982
41.5k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
41.5k
    if (!num) {
984
39.6k
        dest = nullptr;
985
39.6k
        return;
986
39.6k
    }
987
1.89k
    dest = new Type *[num];
988
3.90k
    for (ai_uint i = 0; i < num; ++i) {
989
2.00k
        SceneCombiner::Copy(&dest[i], src[i]);
990
2.00k
    }
991
1.89k
}
void Assimp::CopyPtrArray<aiMaterial>(aiMaterial**&, aiMaterial const* const*, unsigned int)
Line
Count
Source
982
41.5k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
41.5k
    if (!num) {
984
8.13k
        dest = nullptr;
985
8.13k
        return;
986
8.13k
    }
987
33.4k
    dest = new Type *[num];
988
76.4k
    for (ai_uint i = 0; i < num; ++i) {
989
43.0k
        SceneCombiner::Copy(&dest[i], src[i]);
990
43.0k
    }
991
33.4k
}
void Assimp::CopyPtrArray<aiLight>(aiLight**&, aiLight const* const*, unsigned int)
Line
Count
Source
982
41.5k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
41.5k
    if (!num) {
984
41.1k
        dest = nullptr;
985
41.1k
        return;
986
41.1k
    }
987
380
    dest = new Type *[num];
988
2.98k
    for (ai_uint i = 0; i < num; ++i) {
989
2.60k
        SceneCombiner::Copy(&dest[i], src[i]);
990
2.60k
    }
991
380
}
void Assimp::CopyPtrArray<aiCamera>(aiCamera**&, aiCamera const* const*, unsigned int)
Line
Count
Source
982
41.5k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
41.5k
    if (!num) {
984
39.2k
        dest = nullptr;
985
39.2k
        return;
986
39.2k
    }
987
2.27k
    dest = new Type *[num];
988
10.1k
    for (ai_uint i = 0; i < num; ++i) {
989
7.89k
        SceneCombiner::Copy(&dest[i], src[i]);
990
7.89k
    }
991
2.27k
}
void Assimp::CopyPtrArray<aiMesh>(aiMesh**&, aiMesh const* const*, unsigned int)
Line
Count
Source
982
41.5k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
41.5k
    if (!num) {
984
8.13k
        dest = nullptr;
985
8.13k
        return;
986
8.13k
    }
987
33.4k
    dest = new Type *[num];
988
154k
    for (ai_uint i = 0; i < num; ++i) {
989
120k
        SceneCombiner::Copy(&dest[i], src[i]);
990
120k
    }
991
33.4k
}
void Assimp::CopyPtrArray<aiBone>(aiBone**&, aiBone const* const*, unsigned int)
Line
Count
Source
982
120k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
120k
    if (!num) {
984
117k
        dest = nullptr;
985
117k
        return;
986
117k
    }
987
3.06k
    dest = new Type *[num];
988
14.0k
    for (ai_uint i = 0; i < num; ++i) {
989
10.9k
        SceneCombiner::Copy(&dest[i], src[i]);
990
10.9k
    }
991
3.06k
}
void Assimp::CopyPtrArray<aiAnimMesh>(aiAnimMesh**&, aiAnimMesh const* const*, unsigned int)
Line
Count
Source
982
120k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
120k
    if (!num) {
984
120k
        dest = nullptr;
985
120k
        return;
986
120k
    }
987
0
    dest = new Type *[num];
988
0
    for (ai_uint i = 0; i < num; ++i) {
989
0
        SceneCombiner::Copy(&dest[i], src[i]);
990
0
    }
991
0
}
void Assimp::CopyPtrArray<aiNodeAnim>(aiNodeAnim**&, aiNodeAnim const* const*, unsigned int)
Line
Count
Source
982
5.20k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
5.20k
    if (!num) {
984
0
        dest = nullptr;
985
0
        return;
986
0
    }
987
5.20k
    dest = new Type *[num];
988
24.9k
    for (ai_uint i = 0; i < num; ++i) {
989
19.7k
        SceneCombiner::Copy(&dest[i], src[i]);
990
19.7k
    }
991
5.20k
}
void Assimp::CopyPtrArray<aiMeshMorphAnim>(aiMeshMorphAnim**&, aiMeshMorphAnim const* const*, unsigned int)
Line
Count
Source
982
5.20k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
5.20k
    if (!num) {
984
5.20k
        dest = nullptr;
985
5.20k
        return;
986
5.20k
    }
987
0
    dest = new Type *[num];
988
0
    for (ai_uint i = 0; i < num; ++i) {
989
0
        SceneCombiner::Copy(&dest[i], src[i]);
990
0
    }
991
0
}
void Assimp::CopyPtrArray<aiNode>(aiNode**&, aiNode const* const*, unsigned int)
Line
Count
Source
982
400k
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
983
400k
    if (!num) {
984
272k
        dest = nullptr;
985
272k
        return;
986
272k
    }
987
128k
    dest = new Type *[num];
988
452k
    for (ai_uint i = 0; i < num; ++i) {
989
323k
        SceneCombiner::Copy(&dest[i], src[i]);
990
323k
    }
991
128k
}
992
993
// ------------------------------------------------------------------------------------------------
994
template <typename Type>
995
2.99M
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
2.99M
    if (!dest) {
997
2.09M
        return;
998
2.09M
    }
999
898k
    Type *old = dest;
1000
1001
898k
    dest = new Type[num];
1002
898k
    std::copy(old, old+num, dest);
1003
898k
}
void Assimp::GetArrayCopy<aiVector3t<float> >(aiVector3t<float>*&, unsigned int)
Line
Count
Source
995
1.44M
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
1.44M
    if (!dest) {
997
1.10M
        return;
998
1.10M
    }
999
348k
    Type *old = dest;
1000
1001
348k
    dest = new Type[num];
1002
348k
    std::copy(old, old+num, dest);
1003
348k
}
void Assimp::GetArrayCopy<aiColor4t<float> >(aiColor4t<float>*&, unsigned int)
Line
Count
Source
995
965k
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
965k
    if (!dest) {
997
956k
        return;
998
956k
    }
999
8.60k
    Type *old = dest;
1000
1001
8.60k
    dest = new Type[num];
1002
8.60k
    std::copy(old, old+num, dest);
1003
8.60k
}
void Assimp::GetArrayCopy<aiFace>(aiFace*&, unsigned int)
Line
Count
Source
995
120k
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
120k
    if (!dest) {
997
0
        return;
998
0
    }
999
120k
    Type *old = dest;
1000
1001
120k
    dest = new Type[num];
1002
120k
    std::copy(old, old+num, dest);
1003
120k
}
void Assimp::GetArrayCopy<aiVectorKey>(aiVectorKey*&, unsigned int)
Line
Count
Source
995
39.5k
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
39.5k
    if (!dest) {
997
1.78k
        return;
998
1.78k
    }
999
37.7k
    Type *old = dest;
1000
1001
37.7k
    dest = new Type[num];
1002
37.7k
    std::copy(old, old+num, dest);
1003
37.7k
}
void Assimp::GetArrayCopy<aiQuatKey>(aiQuatKey*&, unsigned int)
Line
Count
Source
995
19.7k
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
19.7k
    if (!dest) {
997
1.46k
        return;
998
1.46k
    }
999
18.2k
    Type *old = dest;
1000
1001
18.2k
    dest = new Type[num];
1002
18.2k
    std::copy(old, old+num, dest);
1003
18.2k
}
Unexecuted instantiation: void Assimp::GetArrayCopy<aiMeshMorphKey>(aiMeshMorphKey*&, unsigned int)
void Assimp::GetArrayCopy<unsigned int>(unsigned int*&, unsigned int)
Line
Count
Source
995
400k
inline void GetArrayCopy(Type *&dest, ai_uint num) {
996
400k
    if (!dest) {
997
35.3k
        return;
998
35.3k
    }
999
365k
    Type *old = dest;
1000
1001
365k
    dest = new Type[num];
1002
365k
    std::copy(old, old+num, dest);
1003
365k
}
1004
1005
// ------------------------------------------------------------------------------------------------
1006
1.33k
void SceneCombiner::CopySceneFlat(aiScene **_dest, const aiScene *src) {
1007
1.33k
    if (nullptr == _dest || nullptr == src) {
1008
0
        return;
1009
0
    }
1010
1011
    // reuse the old scene or allocate a new?
1012
1.33k
    if (*_dest) {
1013
1.33k
        (*_dest)->~aiScene();
1014
1.33k
        new (*_dest) aiScene();
1015
1.33k
    } else {
1016
0
        *_dest = new aiScene();
1017
0
    }
1018
1.33k
    CopyScene(_dest, src, false);
1019
1.33k
}
1020
1021
// ------------------------------------------------------------------------------------------------
1022
41.5k
void SceneCombiner::CopyScene(aiScene **_dest, const aiScene *src, bool allocate) {
1023
41.5k
    if (nullptr == _dest || nullptr == src) {
1024
0
        return;
1025
0
    }
1026
1027
41.5k
    if (allocate) {
1028
40.2k
        *_dest = new aiScene();
1029
40.2k
    }
1030
41.5k
    aiScene *dest = *_dest;
1031
41.5k
    ai_assert(nullptr != dest);
1032
1033
    // copy metadata
1034
41.5k
    if (nullptr != src->mMetaData) {
1035
40.2k
        dest->mMetaData = new aiMetadata(*src->mMetaData);
1036
40.2k
    }
1037
1038
    // copy animations
1039
41.5k
    dest->mNumAnimations = src->mNumAnimations;
1040
41.5k
    CopyPtrArray(dest->mAnimations, src->mAnimations,
1041
41.5k
            dest->mNumAnimations);
1042
1043
    // copy textures
1044
41.5k
    dest->mNumTextures = src->mNumTextures;
1045
41.5k
    CopyPtrArray(dest->mTextures, src->mTextures,
1046
41.5k
            dest->mNumTextures);
1047
1048
    // copy materials
1049
41.5k
    dest->mNumMaterials = src->mNumMaterials;
1050
41.5k
    CopyPtrArray(dest->mMaterials, src->mMaterials,
1051
41.5k
            dest->mNumMaterials);
1052
1053
    // copy lights
1054
41.5k
    dest->mNumLights = src->mNumLights;
1055
41.5k
    CopyPtrArray(dest->mLights, src->mLights,
1056
41.5k
            dest->mNumLights);
1057
1058
    // copy cameras
1059
41.5k
    dest->mNumCameras = src->mNumCameras;
1060
41.5k
    CopyPtrArray(dest->mCameras, src->mCameras,
1061
41.5k
            dest->mNumCameras);
1062
1063
    // copy meshes
1064
41.5k
    dest->mNumMeshes = src->mNumMeshes;
1065
41.5k
    CopyPtrArray(dest->mMeshes, src->mMeshes,
1066
41.5k
            dest->mNumMeshes);
1067
1068
    // now - copy the root node of the scene (deep copy, too)
1069
41.5k
    Copy(&dest->mRootNode, src->mRootNode);
1070
1071
    // and keep the flags ...
1072
41.5k
    dest->mFlags = src->mFlags;
1073
1074
    // source private data might be nullptr if the scene is user-allocated (i.e. for use with the export API)
1075
41.5k
    if (src->mPrivate != nullptr) {
1076
41.5k
        ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0;
1077
41.5k
    }
1078
41.5k
}
1079
1080
// ------------------------------------------------------------------------------------------------
1081
120k
void SceneCombiner::Copy(aiMesh **_dest, const aiMesh *src) {
1082
120k
    if (nullptr == _dest || nullptr == src) {
1083
0
        return;
1084
0
    }
1085
1086
120k
    aiMesh *dest = *_dest = new aiMesh();
1087
1088
    // get a flat copy
1089
120k
    *dest = *src;
1090
1091
    // and reallocate all arrays
1092
120k
    GetArrayCopy(dest->mVertices, dest->mNumVertices);
1093
120k
    GetArrayCopy(dest->mNormals, dest->mNumVertices);
1094
120k
    GetArrayCopy(dest->mTangents, dest->mNumVertices);
1095
120k
    GetArrayCopy(dest->mBitangents, dest->mNumVertices);
1096
1097
    // Reallocate every populated UV and color channel. The destructor frees
1098
    // all AI_MAX_NUMBER_OF_* channels, so the copy must own an independent
1099
    // buffer for each non-null one. Iterating with HasTextureCoords()/
1100
    // HasVertexColors() would stop at the first empty channel (leaving any
1101
    // later channel aliased with the source) and also skip channels of a mesh
1102
    // with mNumVertices == 0, which then double-frees when both meshes are
1103
    // destroyed.
1104
1.08M
    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
1105
965k
        GetArrayCopy(dest->mTextureCoords[n], dest->mNumVertices);
1106
965k
    }
1107
1108
1.08M
    for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
1109
965k
        GetArrayCopy(dest->mColors[n], dest->mNumVertices);
1110
965k
    }
1111
1112
    // make a deep copy of all bones
1113
120k
    CopyPtrArray(dest->mBones, dest->mBones, dest->mNumBones);
1114
1115
    // make a deep copy of all faces
1116
120k
    GetArrayCopy(dest->mFaces, dest->mNumFaces);
1117
1118
    // make a deep copy of all blend shapes
1119
120k
    CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
1120
1121
    // make a deep copy of all texture coordinate names
1122
120k
    if (src->mTextureCoordsNames != nullptr) {
1123
9.46k
        dest->mTextureCoordsNames = new aiString *[AI_MAX_NUMBER_OF_TEXTURECOORDS] {};
1124
85.1k
        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
1125
75.7k
            Copy(&dest->mTextureCoordsNames[i], src->mTextureCoordsNames[i]);
1126
75.7k
        }
1127
9.46k
    }
1128
120k
}
1129
1130
// ------------------------------------------------------------------------------------------------
1131
0
void SceneCombiner::Copy(aiAnimMesh **_dest, const aiAnimMesh *src) {
1132
0
    if (nullptr == _dest || nullptr == src) {
1133
0
        return;
1134
0
    }
1135
1136
0
    aiAnimMesh *dest = *_dest = new aiAnimMesh();
1137
1138
    // get a flat copy
1139
0
    *dest = *src;
1140
1141
    // and reallocate all arrays
1142
0
    GetArrayCopy(dest->mVertices, dest->mNumVertices);
1143
0
    GetArrayCopy(dest->mNormals, dest->mNumVertices);
1144
0
    GetArrayCopy(dest->mTangents, dest->mNumVertices);
1145
0
    GetArrayCopy(dest->mBitangents, dest->mNumVertices);
1146
1147
0
    unsigned int n = 0;
1148
0
    while (dest->HasTextureCoords(n))
1149
0
        GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
1150
1151
0
    n = 0;
1152
0
    while (dest->HasVertexColors(n))
1153
0
        GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
1154
0
}
1155
1156
// ------------------------------------------------------------------------------------------------
1157
43.0k
void SceneCombiner::Copy(aiMaterial **_dest, const aiMaterial *src) {
1158
43.0k
    if (nullptr == _dest || nullptr == src) {
1159
0
        return;
1160
0
    }
1161
1162
43.0k
    aiMaterial *dest = (aiMaterial *)(*_dest = new aiMaterial());
1163
1164
43.0k
    dest->Clear();
1165
43.0k
    delete[] dest->mProperties;
1166
1167
43.0k
    dest->mNumAllocated = src->mNumAllocated;
1168
43.0k
    dest->mNumProperties = src->mNumProperties;
1169
43.0k
    dest->mProperties = new aiMaterialProperty *[dest->mNumAllocated];
1170
1171
593k
    for (unsigned int i = 0; i < dest->mNumProperties; ++i) {
1172
550k
        aiMaterialProperty *prop = dest->mProperties[i] = new aiMaterialProperty();
1173
550k
        aiMaterialProperty *sprop = src->mProperties[i];
1174
1175
550k
        prop->mDataLength = sprop->mDataLength;
1176
550k
        prop->mData = new char[prop->mDataLength];
1177
550k
        ::memcpy(prop->mData, sprop->mData, prop->mDataLength);
1178
1179
550k
        prop->mIndex = sprop->mIndex;
1180
550k
        prop->mSemantic = sprop->mSemantic;
1181
550k
        prop->mKey = sprop->mKey;
1182
550k
        prop->mType = sprop->mType;
1183
550k
    }
1184
43.0k
}
1185
1186
// ------------------------------------------------------------------------------------------------
1187
2.00k
void SceneCombiner::Copy(aiTexture **_dest, const aiTexture *src) {
1188
2.00k
    if (nullptr == _dest || nullptr == src) {
1189
0
        return;
1190
0
    }
1191
1192
2.00k
    aiTexture *dest = *_dest = new aiTexture();
1193
1194
    // get a flat copy
1195
2.00k
    *dest = *src;
1196
1197
    // and reallocate all arrays. We must do it manually here
1198
2.00k
    const char *old = (const char *)dest->pcData;
1199
2.00k
    if (old) {
1200
2.00k
        unsigned int cpy;
1201
2.00k
        if (!dest->mHeight)
1202
1.53k
            cpy = dest->mWidth;
1203
468
        else
1204
468
            cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel);
1205
1206
2.00k
        if (!cpy) {
1207
0
            dest->pcData = nullptr;
1208
0
            return;
1209
0
        }
1210
        // the cast is legal, the aiTexel c'tor does nothing important
1211
2.00k
        dest->pcData = (aiTexel *)new char[cpy];
1212
2.00k
        ::memcpy(dest->pcData, old, cpy);
1213
2.00k
    }
1214
2.00k
}
1215
1216
// ------------------------------------------------------------------------------------------------
1217
5.20k
void SceneCombiner::Copy(aiAnimation **_dest, const aiAnimation *src) {
1218
5.20k
    if (nullptr == _dest || nullptr == src) {
1219
0
        return;
1220
0
    }
1221
1222
5.20k
    aiAnimation *dest = *_dest = new aiAnimation();
1223
1224
    // get a flat copy
1225
5.20k
    *dest = *src;
1226
1227
    // and reallocate all arrays
1228
5.20k
    CopyPtrArray(dest->mChannels, src->mChannels, dest->mNumChannels);
1229
5.20k
    CopyPtrArray(dest->mMorphMeshChannels, src->mMorphMeshChannels, dest->mNumMorphMeshChannels);
1230
5.20k
}
1231
1232
// ------------------------------------------------------------------------------------------------
1233
19.7k
void SceneCombiner::Copy(aiNodeAnim **_dest, const aiNodeAnim *src) {
1234
19.7k
    if (nullptr == _dest || nullptr == src) {
1235
0
        return;
1236
0
    }
1237
1238
19.7k
    aiNodeAnim *dest = *_dest = new aiNodeAnim();
1239
1240
    // get a flat copy
1241
19.7k
    *dest = *src;
1242
1243
    // and reallocate all arrays
1244
19.7k
    GetArrayCopy(dest->mPositionKeys, dest->mNumPositionKeys);
1245
19.7k
    GetArrayCopy(dest->mScalingKeys, dest->mNumScalingKeys);
1246
19.7k
    GetArrayCopy(dest->mRotationKeys, dest->mNumRotationKeys);
1247
19.7k
}
1248
1249
0
void SceneCombiner::Copy(aiMeshMorphAnim **_dest, const aiMeshMorphAnim *src) {
1250
0
    if (nullptr == _dest || nullptr == src) {
1251
0
        return;
1252
0
    }
1253
1254
0
    aiMeshMorphAnim *dest = *_dest = new aiMeshMorphAnim();
1255
1256
    // get a flat copy
1257
0
    *dest = *src;
1258
1259
    // and reallocate all arrays
1260
0
    GetArrayCopy(dest->mKeys, dest->mNumKeys);
1261
0
    for (ai_uint i = 0; i < dest->mNumKeys; ++i) {
1262
0
        dest->mKeys[i].mValues = new unsigned int[dest->mKeys[i].mNumValuesAndWeights];
1263
0
        dest->mKeys[i].mWeights = new double[dest->mKeys[i].mNumValuesAndWeights];
1264
0
        ::memcpy(dest->mKeys[i].mValues, src->mKeys[i].mValues, dest->mKeys[i].mNumValuesAndWeights * sizeof(unsigned int));
1265
0
        ::memcpy(dest->mKeys[i].mWeights, src->mKeys[i].mWeights, dest->mKeys[i].mNumValuesAndWeights * sizeof(double));
1266
0
    }
1267
0
}
1268
1269
// ------------------------------------------------------------------------------------------------
1270
7.89k
void SceneCombiner::Copy(aiCamera **_dest, const aiCamera *src) {
1271
7.89k
    if (nullptr == _dest || nullptr == src) {
1272
0
        return;
1273
0
    }
1274
1275
7.89k
    aiCamera *dest = *_dest = new aiCamera();
1276
1277
    // get a flat copy, that's already OK
1278
7.89k
    *dest = *src;
1279
7.89k
}
1280
1281
// ------------------------------------------------------------------------------------------------
1282
2.60k
void SceneCombiner::Copy(aiLight **_dest, const aiLight *src) {
1283
2.60k
    if (nullptr == _dest || nullptr == src) {
1284
0
        return;
1285
0
    }
1286
1287
2.60k
    aiLight *dest = *_dest = new aiLight();
1288
1289
    // get a flat copy, that's already OK
1290
2.60k
    *dest = *src;
1291
2.60k
}
1292
1293
// ------------------------------------------------------------------------------------------------
1294
10.9k
void SceneCombiner::Copy(aiBone **_dest, const aiBone *src) {
1295
10.9k
    if (nullptr == _dest || nullptr == src) {
1296
0
        return;
1297
0
    }
1298
1299
10.9k
    aiBone *dest = *_dest = new aiBone();
1300
1301
    // get a flat copy
1302
10.9k
    *dest = *src;
1303
10.9k
}
1304
1305
// ------------------------------------------------------------------------------------------------
1306
400k
void SceneCombiner::Copy(aiNode **_dest, const aiNode *src) {
1307
400k
    ai_assert(nullptr != _dest);
1308
400k
    ai_assert(nullptr != src);
1309
1310
400k
    aiNode *dest = *_dest = new aiNode();
1311
1312
    // get a flat copy
1313
400k
    *dest = *src;
1314
1315
400k
    if (src->mMetaData) {
1316
27.4k
        Copy(&dest->mMetaData, src->mMetaData);
1317
27.4k
    }
1318
1319
    // and reallocate all arrays
1320
400k
    GetArrayCopy(dest->mMeshes, dest->mNumMeshes);
1321
400k
    CopyPtrArray(dest->mChildren, src->mChildren, dest->mNumChildren);
1322
1323
    // need to set the mParent fields to the created aiNode.
1324
724k
    for (unsigned int i = 0; i < dest->mNumChildren; i++) {
1325
323k
        dest->mChildren[i]->mParent = dest;
1326
323k
    }
1327
400k
}
1328
1329
// ------------------------------------------------------------------------------------------------
1330
27.4k
void SceneCombiner::Copy(aiMetadata **_dest, const aiMetadata *src) {
1331
27.4k
    if (nullptr == _dest || nullptr == src) {
1332
0
        return;
1333
0
    }
1334
1335
27.4k
    if (0 == src->mNumProperties) {
1336
0
        return;
1337
0
    }
1338
1339
27.4k
    aiMetadata *dest = *_dest = aiMetadata::Alloc(src->mNumProperties);
1340
27.4k
    std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys);
1341
1342
219k
    for (unsigned int i = 0; i < src->mNumProperties; ++i) {
1343
191k
        aiMetadataEntry &in = src->mValues[i];
1344
191k
        aiMetadataEntry &out = dest->mValues[i];
1345
191k
        out.mType = in.mType;
1346
191k
        switch (dest->mValues[i].mType) {
1347
48.0k
        case AI_BOOL:
1348
48.0k
            out.mData = new bool(*static_cast<bool *>(in.mData));
1349
48.0k
            break;
1350
62.2k
        case AI_INT32:
1351
62.2k
            out.mData = new int32_t(*static_cast<int32_t *>(in.mData));
1352
62.2k
            break;
1353
13.8k
        case AI_UINT64:
1354
13.8k
            out.mData = new uint64_t(*static_cast<uint64_t *>(in.mData));
1355
13.8k
            break;
1356
7.25k
        case AI_FLOAT:
1357
7.25k
            out.mData = new float(*static_cast<float *>(in.mData));
1358
7.25k
            break;
1359
0
        case AI_DOUBLE:
1360
0
            out.mData = new double(*static_cast<double *>(in.mData));
1361
0
            break;
1362
36.8k
        case AI_AISTRING:
1363
36.8k
            out.mData = new aiString(*static_cast<aiString *>(in.mData));
1364
36.8k
            break;
1365
23.7k
        case AI_AIVECTOR3D:
1366
23.7k
            out.mData = new aiVector3D(*static_cast<aiVector3D *>(in.mData));
1367
23.7k
            break;
1368
0
        case AI_AIMETADATA:
1369
0
            out.mData = new aiMetadata(*static_cast<aiMetadata *>(in.mData));
1370
0
            break;
1371
0
        default:
1372
0
            ai_assert(false);
1373
0
            break;
1374
191k
        }
1375
191k
    }
1376
27.4k
}
1377
1378
// ------------------------------------------------------------------------------------------------
1379
75.7k
void SceneCombiner::Copy(aiString **_dest, const aiString *src) {
1380
75.7k
    if (nullptr == _dest || nullptr == src) {
1381
66.2k
        return;
1382
66.2k
    }
1383
1384
9.49k
    aiString *dest = *_dest = new aiString();
1385
1386
    // get a flat copy
1387
9.49k
    *dest = *src;
1388
9.49k
}
1389
1390
#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0)
1391
#pragma GCC diagnostic pop
1392
#endif
1393
1394
} // Namespace Assimp