Coverage Report

Created: 2025-08-28 06:38

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