Coverage Report

Created: 2026-04-01 06:27

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_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
0
    ::memmove(string.data + len, string.data, string.length + 1);
90
0
    ::memcpy(string.data, prefix, len);
91
92
    // And update the string's length
93
0
    string.length += len;
94
0
}
95
96
// ------------------------------------------------------------------------------------------------
97
// Add node identifiers to a hashing set
98
0
void SceneCombiner::AddNodeHashes(aiNode *node, std::set<unsigned int> &hashes) {
99
0
    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
0
    if (node->mName.length) {
107
0
        hashes.insert(SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)));
108
0
    }
109
110
    // Process all children recursively
111
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
112
0
        AddNodeHashes(node->mChildren[i], hashes);
113
0
    }
114
0
}
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
0
bool SceneCombiner::FindNameMatch(const aiString &name, std::vector<SceneHelper> &input, unsigned int cur) {
132
0
    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
0
    for (unsigned int i = 0; i < input.size(); ++i) {
136
0
        if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
137
0
            return true;
138
0
        }
139
0
    }
140
0
    return false;
141
0
}
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
0
        std::vector<SceneHelper> &input, unsigned int cur) {
147
0
    ai_assert(nullptr != prefix);
148
149
0
    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
0
    for (unsigned int i = 0; i < input.size(); ++i) {
153
0
        if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
154
0
            PrefixString(node->mName, prefix, len);
155
0
            break;
156
0
        }
157
0
    }
158
159
    // Process all children recursively
160
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
161
0
        AddNodePrefixesChecked(node->mChildren[i], prefix, len, input, cur);
162
0
    }
163
0
}
164
165
// ------------------------------------------------------------------------------------------------
166
// Add an offset to all mesh indices in a node graph
167
0
void SceneCombiner::OffsetNodeMeshIndices(aiNode *node, unsigned int offset) {
168
0
    for (unsigned int i = 0; i < node->mNumMeshes; ++i)
169
0
        node->mMeshes[i] += offset;
170
171
0
    for (unsigned int i = 0; i < node->mNumChildren; ++i) {
172
0
        OffsetNodeMeshIndices(node->mChildren[i], offset);
173
0
    }
174
0
}
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
0
void SceneCombiner::AttachToGraph(aiNode *attach, std::vector<NodeAttachmentInfo> &srcList) {
214
0
    unsigned int cnt;
215
0
    for (cnt = 0; cnt < attach->mNumChildren; ++cnt) {
216
0
        AttachToGraph(attach->mChildren[cnt], srcList);
217
0
    }
218
219
0
    cnt = 0;
220
0
    for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin();
221
0
            it != srcList.end(); ++it) {
222
0
        if ((*it).attachToNode == attach && !(*it).resolved)
223
0
            ++cnt;
224
0
    }
225
226
0
    if (cnt) {
227
0
        aiNode **n = new aiNode *[cnt + attach->mNumChildren];
228
0
        if (attach->mNumChildren) {
229
0
            ::memcpy(n, attach->mChildren, sizeof(void *) * attach->mNumChildren);
230
0
            delete[] attach->mChildren;
231
0
        }
232
0
        attach->mChildren = n;
233
234
0
        n += attach->mNumChildren;
235
0
        attach->mNumChildren += cnt;
236
237
0
        for (unsigned int i = 0; i < srcList.size(); ++i) {
238
0
            NodeAttachmentInfo &att = srcList[i];
239
0
            if (att.attachToNode == attach && !att.resolved) {
240
0
                *n = att.node;
241
0
                (**n).mParent = attach;
242
0
                ++n;
243
244
                // mark this attachment as resolved
245
0
                att.resolved = true;
246
0
            }
247
0
        }
248
0
    }
249
0
}
250
251
// ------------------------------------------------------------------------------------------------
252
0
void SceneCombiner::AttachToGraph(aiScene *master, std::vector<NodeAttachmentInfo> &src) {
253
0
    ai_assert(nullptr != master);
254
255
0
    AttachToGraph(master->mRootNode, src);
256
0
}
257
258
// ------------------------------------------------------------------------------------------------
259
0
void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector<AttachmentInfo> &srcList, unsigned int flags) {
260
0
    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
0
    if (srcList.empty()) {
274
0
        if (*_dest) {
275
0
            SceneCombiner::CopySceneFlat(_dest, master);
276
0
            delete master;
277
0
        } else
278
0
            *_dest = master;
279
0
        return;
280
0
    }
281
0
    if (*_dest) {
282
0
        (*_dest)->~aiScene();
283
0
        new (*_dest) aiScene();
284
0
    } else
285
0
        *_dest = new aiScene();
286
287
0
    aiScene *dest = *_dest;
288
289
0
    std::vector<SceneHelper> src(srcList.size() + 1);
290
0
    src[0].scene = master;
291
0
    for (unsigned int i = 0; i < srcList.size(); ++i) {
292
0
        src[i + 1] = SceneHelper(srcList[i].scene);
293
0
    }
294
295
    // this helper array specifies which scenes are duplicates of others
296
0
    std::vector<unsigned int> duplicates(src.size(), UINT_MAX);
297
298
    // this helper array is used as lookup table several times
299
0
    std::vector<unsigned int> offset(src.size());
300
301
    // Find duplicate scenes
302
0
    for (unsigned int i = 0; i < src.size(); ++i) {
303
0
        if (duplicates[i] != i && duplicates[i] != UINT_MAX) {
304
0
            continue;
305
0
        }
306
307
0
        duplicates[i] = i;
308
0
        for (unsigned int a = i + 1; a < src.size(); ++a) {
309
0
            if (src[i].scene == src[a].scene) {
310
0
                duplicates[a] = i;
311
0
            }
312
0
        }
313
0
    }
314
315
    // Generate unique names for all named stuff?
316
0
    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
0
        for (unsigned int i = 1; i < src.size(); ++i) {
324
0
            src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_", i);
325
326
0
            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
0
                AddNodeHashes(src[i]->mRootNode, src[i].hashes);
333
334
0
                for (unsigned int a = 0; a < src[i]->mNumAnimations; ++a) {
335
0
                    aiAnimation *anim = src[i]->mAnimations[a];
336
0
                    src[i].hashes.insert(SuperFastHash(anim->mName.data, static_cast<uint32_t>(anim->mName.length)));
337
0
                }
338
0
            }
339
0
        }
340
0
    }
341
342
0
    unsigned int cnt;
343
344
    // First find out how large the respective output arrays must be
345
0
    for (unsigned int n = 0; n < src.size(); ++n) {
346
0
        SceneHelper *cur = &src[n];
347
348
0
        if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
349
0
            dest->mNumTextures += (*cur)->mNumTextures;
350
0
            dest->mNumMaterials += (*cur)->mNumMaterials;
351
0
            dest->mNumMeshes += (*cur)->mNumMeshes;
352
0
        }
353
354
0
        dest->mNumLights += (*cur)->mNumLights;
355
0
        dest->mNumCameras += (*cur)->mNumCameras;
356
0
        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
0
        if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
362
0
            dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
363
0
        }
364
0
    }
365
366
    // generate the output texture list + an offset table for all texture indices
367
0
    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
0
    if (dest->mNumMaterials) {
391
0
        aiMaterial **pip = dest->mMaterials = new aiMaterial *[dest->mNumMaterials];
392
0
        cnt = 0;
393
0
        for (unsigned int n = 0; n < src.size(); ++n) {
394
0
            SceneHelper *cur = &src[n];
395
0
            for (unsigned int i = 0; i < (*cur)->mNumMaterials; ++i) {
396
0
                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
0
                    *pip = (*cur)->mMaterials[i];
404
405
0
                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
0
                ++pip;
445
0
            }
446
447
0
            offset[n] = cnt;
448
0
            cnt = (unsigned int)(pip - dest->mMaterials);
449
0
        }
450
0
    }
451
452
    // generate the output mesh list + again an offset table for all mesh indices
453
0
    if (dest->mNumMeshes) {
454
0
        aiMesh **pip = dest->mMeshes = new aiMesh *[dest->mNumMeshes];
455
0
        cnt = 0;
456
0
        for (unsigned int n = 0; n < src.size(); ++n) {
457
0
            SceneHelper *cur = &src[n];
458
0
            for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) {
459
0
                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
0
                    *pip = (*cur)->mMeshes[i];
467
468
                // update the material index of the mesh
469
0
                (*pip)->mMaterialIndex += offset[n];
470
0
                ++pip;
471
0
            }
472
473
            // reuse the offset array - store now the mesh offset in it
474
0
            offset[n] = cnt;
475
0
            cnt = (unsigned int)(pip - dest->mMeshes);
476
0
        }
477
0
    }
478
479
0
    std::vector<NodeAttachmentInfo> nodes;
480
0
    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
0
    aiLight **ppLights = dest->mLights = (dest->mNumLights ? new aiLight *[dest->mNumLights] : nullptr);
493
494
0
    aiCamera **ppCameras = dest->mCameras = (dest->mNumCameras ? new aiCamera *[dest->mNumCameras] : nullptr);
495
496
0
    aiAnimation **ppAnims = dest->mAnimations = (dest->mNumAnimations ? new aiAnimation *[dest->mNumAnimations] : nullptr);
497
498
0
    for (int n = static_cast<int>(src.size() - 1); n >= 0; --n) /* !!! important !!! */
499
0
    {
500
0
        SceneHelper *cur = &src[n];
501
0
        aiNode *node;
502
503
        // To offset or not to offset, this is the question
504
0
        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
0
        {
515
0
            node = (*cur)->mRootNode;
516
0
            OffsetNodeMeshIndices(node, offset[n]);
517
0
        }
518
0
        if (n) // src[0] is the master node
519
0
            nodes.emplace_back(node, srcList[n - 1].attachToNode, n);
520
521
        // add name prefixes?
522
0
        if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
523
524
            // or the whole scenegraph
525
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
526
0
                AddNodePrefixesChecked(node, (*cur).id, (*cur).idlen, src, n);
527
0
            } else
528
0
                AddNodePrefixes(node, (*cur).id, (*cur).idlen);
529
530
            // meshes
531
0
            for (unsigned int i = 0; i < (*cur)->mNumMeshes; ++i) {
532
0
                aiMesh *mesh = (*cur)->mMeshes[i];
533
534
                // rename all bones
535
0
                for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
536
0
                    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
537
0
                        if (!FindNameMatch(mesh->mBones[a]->mName, src, n))
538
0
                            continue;
539
0
                    }
540
0
                    PrefixString(mesh->mBones[a]->mName, (*cur).id, (*cur).idlen);
541
0
                }
542
0
            }
543
0
        }
544
545
        // --------------------------------------------------------------------
546
        // Copy light sources
547
0
        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
0
        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
0
        for (unsigned int i = 0; i < (*cur)->mNumAnimations; ++i, ++ppAnims) {
588
0
            if (n != (int)duplicates[n]) // duplicate scene?
589
0
            {
590
0
                Copy(ppAnims, (*cur)->mAnimations[i]);
591
0
            } else
592
0
                *ppAnims = (*cur)->mAnimations[i];
593
594
            // Add name prefixes?
595
0
            if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
596
0
                if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
597
0
                    if (!FindNameMatch((*ppAnims)->mName, src, n))
598
0
                        continue;
599
0
                }
600
601
0
                PrefixString((*ppAnims)->mName, (*cur).id, (*cur).idlen);
602
603
                // don't forget to update all node animation channels
604
0
                for (unsigned int a = 0; a < (*ppAnims)->mNumChannels; ++a) {
605
0
                    if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
606
0
                        if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName, src, n))
607
0
                            continue;
608
0
                    }
609
610
0
                    PrefixString((*ppAnims)->mChannels[a]->mNodeName, (*cur).id, (*cur).idlen);
611
0
                }
612
0
            }
613
0
        }
614
0
    }
615
616
    // Now build the output graph
617
0
    AttachToGraph(master, nodes);
618
0
    dest->mRootNode = master->mRootNode;
619
620
    // Check whether we succeeded at building the output graph
621
0
    for (std::vector<NodeAttachmentInfo>::iterator it = nodes.begin();
622
0
            it != nodes.end(); ++it) {
623
0
        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
0
    }
640
641
    // now delete all input scenes. Make sure duplicate scenes aren't
642
    // deleted more than one time
643
0
    for (unsigned int n = 0; n < src.size(); ++n) {
644
0
        if (n != duplicates[n]) // duplicate scene?
645
0
            continue;
646
647
0
        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
0
        delete[] deleteMe->mMeshes;
652
0
        deleteMe->mMeshes = nullptr;
653
0
        delete[] deleteMe->mCameras;
654
0
        deleteMe->mCameras = nullptr;
655
0
        delete[] deleteMe->mLights;
656
0
        deleteMe->mLights = nullptr;
657
0
        delete[] deleteMe->mMaterials;
658
0
        deleteMe->mMaterials = nullptr;
659
0
        delete[] deleteMe->mAnimations;
660
0
        deleteMe->mAnimations = nullptr;
661
0
        delete[] deleteMe->mTextures;
662
0
        deleteMe->mTextures = nullptr;
663
664
0
        deleteMe->mRootNode = nullptr;
665
666
        // Now we can safely delete the scene
667
0
        delete deleteMe;
668
0
    }
669
670
    // Check flags
671
0
    if (!dest->mNumMeshes || !dest->mNumMaterials) {
672
0
        dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
673
0
    }
674
675
    // We're finished
676
0
}
677
678
// ------------------------------------------------------------------------------------------------
679
// Build a list of unique bones
680
void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash> &asBones,
681
        std::vector<aiMesh *>::const_iterator it,
682
0
        std::vector<aiMesh *>::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
void SceneCombiner::MergeBones(aiMesh *out, std::vector<aiMesh *>::const_iterator it,
716
0
        std::vector<aiMesh *>::const_iterator end) {
717
0
    if (nullptr == out || out->mNumBones == 0) {
718
0
        return;
719
0
    }
720
721
    // find we need to build an unique list of all bones.
722
    // we work with hashes to make the comparisons MUCH faster,
723
    // at least if we have many bones.
724
0
    std::list<BoneWithHash> asBones;
725
0
    BuildUniqueBoneList(asBones, it, end);
726
727
    // now create the output bones
728
0
    out->mNumBones = 0;
729
0
    out->mBones = new aiBone *[asBones.size()];
730
731
0
    for (std::list<BoneWithHash>::const_iterator boneIt = asBones.begin(), boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt) {
732
        // Allocate a bone and setup it's name
733
0
        aiBone *pc = out->mBones[out->mNumBones++] = new aiBone();
734
0
        pc->mName = aiString(*(boneIt->second));
735
736
0
        std::vector<BoneSrcIndex>::const_iterator wend = boneIt->pSrcBones.end();
737
738
        // Loop through all bones to be joined for this bone
739
0
        for (std::vector<BoneSrcIndex>::const_iterator wmit = boneIt->pSrcBones.begin(); wmit != wend; ++wmit) {
740
0
            pc->mNumWeights += (*wmit).first->mNumWeights;
741
742
            // NOTE: different offset matrices for bones with equal names
743
            // are - at the moment - not handled correctly.
744
0
            if (wmit != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) {
745
0
                ASSIMP_LOG_WARN("Bones with equal names but different offset matrices can't be joined at the moment");
746
0
                continue;
747
0
            }
748
0
            pc->mOffsetMatrix = wmit->first->mOffsetMatrix;
749
0
        }
750
751
        // Allocate the vertex weight array
752
0
        aiVertexWeight *avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
753
754
        // And copy the final weights - adjust the vertex IDs by the
755
        // face index offset of the corresponding mesh.
756
0
        for (std::vector<BoneSrcIndex>::const_iterator wmit = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) {
757
0
            if (wmit == wend) {
758
0
                break;
759
0
            }
760
761
0
            aiBone *pip = (*wmit).first;
762
0
            for (unsigned int mp = 0; mp < pip->mNumWeights; ++mp, ++avw) {
763
0
                const aiVertexWeight &vfi = pip->mWeights[mp];
764
0
                avw->mWeight = vfi.mWeight;
765
0
                avw->mVertexId = vfi.mVertexId + (*wmit).second;
766
0
            }
767
0
        }
768
0
    }
769
0
}
770
771
// ------------------------------------------------------------------------------------------------
772
// Merge a list of meshes
773
void SceneCombiner::MergeMeshes(aiMesh **_out, unsigned int /*flags*/,
774
        std::vector<aiMesh *>::const_iterator begin,
775
0
        std::vector<aiMesh *>::const_iterator end) {
776
0
    if (nullptr == _out) {
777
0
        return;
778
0
    }
779
780
0
    if (begin == end) {
781
0
        *_out = nullptr; // no meshes ...
782
0
        return;
783
0
    }
784
785
    // Allocate the output mesh
786
0
    aiMesh *out = *_out = new aiMesh();
787
0
    out->mMaterialIndex = (*begin)->mMaterialIndex;
788
789
0
    std::string name;
790
    // Find out how much output storage we'll need
791
0
    for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
792
0
        const char *meshName((*it)->mName.C_Str());
793
0
        name += std::string(meshName);
794
0
        if (it != end - 1) {
795
0
            name += ".";
796
0
        }
797
0
        out->mNumVertices += (*it)->mNumVertices;
798
0
        out->mNumFaces += (*it)->mNumFaces;
799
0
        out->mNumBones += (*it)->mNumBones;
800
801
        // combine primitive type flags
802
0
        out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
803
0
    }
804
0
    out->mName.Set(name.c_str());
805
806
0
    if (out->mNumVertices) {
807
0
        aiVector3D *pv2;
808
809
        // copy vertex positions
810
0
        if ((**begin).HasPositions()) {
811
812
0
            pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
813
0
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
814
0
                if ((*it)->mVertices) {
815
0
                    ::memcpy(pv2, (*it)->mVertices, (*it)->mNumVertices * sizeof(aiVector3D));
816
0
                } else
817
0
                    ASSIMP_LOG_WARN("JoinMeshes: Positions expected but input mesh contains no positions");
818
0
                pv2 += (*it)->mNumVertices;
819
0
            }
820
0
        }
821
        // copy normals
822
0
        if ((**begin).HasNormals()) {
823
824
0
            pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
825
0
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
826
0
                if ((*it)->mNormals) {
827
0
                    ::memcpy(pv2, (*it)->mNormals, (*it)->mNumVertices * sizeof(aiVector3D));
828
0
                } else {
829
0
                    ASSIMP_LOG_WARN("JoinMeshes: Normals expected but input mesh contains no normals");
830
0
                }
831
0
                pv2 += (*it)->mNumVertices;
832
0
            }
833
0
        }
834
        // copy tangents and bi-tangents
835
0
        if ((**begin).HasTangentsAndBitangents()) {
836
837
0
            pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
838
0
            aiVector3D *pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
839
840
0
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
841
0
                if ((*it)->mTangents) {
842
0
                    ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices * sizeof(aiVector3D));
843
0
                    ::memcpy(pv2b, (*it)->mBitangents, (*it)->mNumVertices * sizeof(aiVector3D));
844
0
                } else {
845
0
                    ASSIMP_LOG_WARN("JoinMeshes: Tangents expected but input mesh contains no tangents");
846
0
                }
847
0
                pv2 += (*it)->mNumVertices;
848
0
                pv2b += (*it)->mNumVertices;
849
0
            }
850
0
        }
851
        // copy texture coordinates
852
0
        unsigned int n = 0;
853
0
        while ((**begin).HasTextureCoords(n)) {
854
0
            out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n];
855
856
0
            pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
857
0
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
858
0
                if ((*it)->mTextureCoords[n]) {
859
0
                    ::memcpy(pv2, (*it)->mTextureCoords[n], (*it)->mNumVertices * sizeof(aiVector3D));
860
0
                } else {
861
0
                    ASSIMP_LOG_WARN("JoinMeshes: UVs expected but input mesh contains no UVs");
862
0
                }
863
0
                pv2 += (*it)->mNumVertices;
864
0
            }
865
0
            ++n;
866
0
        }
867
        // copy vertex colors
868
0
        n = 0;
869
0
        while ((**begin).HasVertexColors(n)) {
870
0
            aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
871
0
            for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
872
0
                if ((*it)->mColors[n]) {
873
0
                    ::memcpy(pVec2, (*it)->mColors[n], (*it)->mNumVertices * sizeof(aiColor4D));
874
0
                } else {
875
0
                    ASSIMP_LOG_WARN("JoinMeshes: VCs expected but input mesh contains no VCs");
876
0
                }
877
0
                pVec2 += (*it)->mNumVertices;
878
0
            }
879
0
            ++n;
880
0
        }
881
0
    }
882
883
0
    if (out->mNumFaces) // just for safety
884
0
    {
885
        // copy faces
886
0
        out->mFaces = new aiFace[out->mNumFaces];
887
0
        aiFace *pf2 = out->mFaces;
888
889
0
        unsigned int ofs = 0;
890
0
        for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
891
0
            for (unsigned int m = 0; m < (*it)->mNumFaces; ++m, ++pf2) {
892
0
                aiFace &face = (*it)->mFaces[m];
893
0
                pf2->mNumIndices = face.mNumIndices;
894
0
                pf2->mIndices = face.mIndices;
895
896
0
                if (ofs) {
897
                    // add the offset to the vertex
898
0
                    for (unsigned int q = 0; q < face.mNumIndices; ++q) {
899
0
                        face.mIndices[q] += ofs;
900
0
                    }
901
0
                }
902
0
                face.mIndices = nullptr;
903
0
            }
904
0
            ofs += (*it)->mNumVertices;
905
0
        }
906
0
    }
907
908
    // bones - as this is quite lengthy, I moved the code to a separate function
909
0
    if (out->mNumBones)
910
0
        MergeBones(out, begin, end);
911
912
    // delete all source meshes
913
0
    for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it)
914
0
        delete *it;
915
0
}
916
917
// ------------------------------------------------------------------------------------------------
918
void SceneCombiner::MergeMaterials(aiMaterial **dest,
919
        std::vector<aiMaterial *>::const_iterator begin,
920
0
        std::vector<aiMaterial *>::const_iterator end) {
921
0
    if (nullptr == dest) {
922
0
        return;
923
0
    }
924
925
0
    if (begin == end) {
926
0
        *dest = nullptr; // no materials ...
927
0
        return;
928
0
    }
929
930
    // Allocate the output material
931
0
    aiMaterial *out = *dest = new aiMaterial();
932
933
    // Get the maximal number of properties
934
0
    unsigned int size = 0;
935
0
    for (std::vector<aiMaterial *>::const_iterator it = begin; it != end; ++it) {
936
0
        size += (*it)->mNumProperties;
937
0
    }
938
939
0
    out->Clear();
940
0
    delete[] out->mProperties;
941
942
0
    out->mNumAllocated = size;
943
0
    out->mNumProperties = 0;
944
0
    out->mProperties = new aiMaterialProperty *[out->mNumAllocated];
945
946
0
    for (std::vector<aiMaterial *>::const_iterator it = begin; it != end; ++it) {
947
0
        for (unsigned int i = 0; i < (*it)->mNumProperties; ++i) {
948
0
            aiMaterialProperty *sprop = (*it)->mProperties[i];
949
950
            // Test if we already have a matching property
951
0
            const aiMaterialProperty *prop_exist;
952
0
            if (aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) {
953
                // If not, we add it to the new material
954
0
                aiMaterialProperty *prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty();
955
956
0
                prop->mDataLength = sprop->mDataLength;
957
0
                prop->mData = new char[prop->mDataLength];
958
0
                ::memcpy(prop->mData, sprop->mData, prop->mDataLength);
959
960
0
                prop->mIndex = sprop->mIndex;
961
0
                prop->mSemantic = sprop->mSemantic;
962
0
                prop->mKey = sprop->mKey;
963
0
                prop->mType = sprop->mType;
964
965
0
                out->mNumProperties++;
966
0
            }
967
0
        }
968
0
    }
969
0
}
970
971
// ------------------------------------------------------------------------------------------------
972
template <typename Type>
973
0
inline void CopyPtrArray(Type **&dest, const Type *const *src, ai_uint num) {
974
0
    if (!num) {
975
0
        dest = nullptr;
976
0
        return;
977
0
    }
978
0
    dest = new Type *[num];
979
0
    for (ai_uint i = 0; i < num; ++i) {
980
0
        SceneCombiner::Copy(&dest[i], src[i]);
981
0
    }
982
0
}
Unexecuted instantiation: void Assimp::CopyPtrArray<aiAnimation>(aiAnimation**&, aiAnimation const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiTexture>(aiTexture**&, aiTexture const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiMaterial>(aiMaterial**&, aiMaterial const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiLight>(aiLight**&, aiLight const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiCamera>(aiCamera**&, aiCamera const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiMesh>(aiMesh**&, aiMesh const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiBone>(aiBone**&, aiBone const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiAnimMesh>(aiAnimMesh**&, aiAnimMesh const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiNodeAnim>(aiNodeAnim**&, aiNodeAnim const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiMeshMorphAnim>(aiMeshMorphAnim**&, aiMeshMorphAnim const* const*, unsigned int)
Unexecuted instantiation: void Assimp::CopyPtrArray<aiNode>(aiNode**&, aiNode const* const*, unsigned int)
983
984
// ------------------------------------------------------------------------------------------------
985
template <typename Type>
986
0
inline void GetArrayCopy(Type *&dest, ai_uint num) {
987
0
    if (!dest) {
988
0
        return;
989
0
    }
990
0
    Type *old = dest;
991
992
0
    dest = new Type[num];
993
0
    std::copy(old, old+num, dest);
994
0
}
Unexecuted instantiation: void Assimp::GetArrayCopy<aiVector3t<float> >(aiVector3t<float>*&, unsigned int)
Unexecuted instantiation: void Assimp::GetArrayCopy<aiColor4t<float> >(aiColor4t<float>*&, unsigned int)
Unexecuted instantiation: void Assimp::GetArrayCopy<aiFace>(aiFace*&, unsigned int)
Unexecuted instantiation: void Assimp::GetArrayCopy<aiVectorKey>(aiVectorKey*&, unsigned int)
Unexecuted instantiation: void Assimp::GetArrayCopy<aiQuatKey>(aiQuatKey*&, unsigned int)
Unexecuted instantiation: void Assimp::GetArrayCopy<aiMeshMorphKey>(aiMeshMorphKey*&, unsigned int)
Unexecuted instantiation: void Assimp::GetArrayCopy<unsigned int>(unsigned int*&, unsigned int)
995
996
// ------------------------------------------------------------------------------------------------
997
0
void SceneCombiner::CopySceneFlat(aiScene **_dest, const aiScene *src) {
998
0
    if (nullptr == _dest || nullptr == src) {
999
0
        return;
1000
0
    }
1001
1002
    // reuse the old scene or allocate a new?
1003
0
    if (*_dest) {
1004
0
        (*_dest)->~aiScene();
1005
0
        new (*_dest) aiScene();
1006
0
    } else {
1007
0
        *_dest = new aiScene();
1008
0
    }
1009
0
    CopyScene(_dest, src, false);
1010
0
}
1011
1012
// ------------------------------------------------------------------------------------------------
1013
0
void SceneCombiner::CopyScene(aiScene **_dest, const aiScene *src, bool allocate) {
1014
0
    if (nullptr == _dest || nullptr == src) {
1015
0
        return;
1016
0
    }
1017
1018
0
    if (allocate) {
1019
0
        *_dest = new aiScene();
1020
0
    }
1021
0
    aiScene *dest = *_dest;
1022
0
    ai_assert(nullptr != dest);
1023
1024
    // copy metadata
1025
0
    if (nullptr != src->mMetaData) {
1026
0
        dest->mMetaData = new aiMetadata(*src->mMetaData);
1027
0
    }
1028
1029
    // copy animations
1030
0
    dest->mNumAnimations = src->mNumAnimations;
1031
0
    CopyPtrArray(dest->mAnimations, src->mAnimations,
1032
0
            dest->mNumAnimations);
1033
1034
    // copy textures
1035
0
    dest->mNumTextures = src->mNumTextures;
1036
0
    CopyPtrArray(dest->mTextures, src->mTextures,
1037
0
            dest->mNumTextures);
1038
1039
    // copy materials
1040
0
    dest->mNumMaterials = src->mNumMaterials;
1041
0
    CopyPtrArray(dest->mMaterials, src->mMaterials,
1042
0
            dest->mNumMaterials);
1043
1044
    // copy lights
1045
0
    dest->mNumLights = src->mNumLights;
1046
0
    CopyPtrArray(dest->mLights, src->mLights,
1047
0
            dest->mNumLights);
1048
1049
    // copy cameras
1050
0
    dest->mNumCameras = src->mNumCameras;
1051
0
    CopyPtrArray(dest->mCameras, src->mCameras,
1052
0
            dest->mNumCameras);
1053
1054
    // copy meshes
1055
0
    dest->mNumMeshes = src->mNumMeshes;
1056
0
    CopyPtrArray(dest->mMeshes, src->mMeshes,
1057
0
            dest->mNumMeshes);
1058
1059
    // now - copy the root node of the scene (deep copy, too)
1060
0
    Copy(&dest->mRootNode, src->mRootNode);
1061
1062
    // and keep the flags ...
1063
0
    dest->mFlags = src->mFlags;
1064
1065
    // source private data might be nullptr if the scene is user-allocated (i.e. for use with the export API)
1066
0
    if (src->mPrivate != nullptr) {
1067
0
        ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0;
1068
0
    }
1069
0
}
1070
1071
// ------------------------------------------------------------------------------------------------
1072
0
void SceneCombiner::Copy(aiMesh **_dest, const aiMesh *src) {
1073
0
    if (nullptr == _dest || nullptr == src) {
1074
0
        return;
1075
0
    }
1076
1077
0
    aiMesh *dest = *_dest = new aiMesh();
1078
1079
    // get a flat copy
1080
0
    *dest = *src;
1081
1082
    // and reallocate all arrays
1083
0
    GetArrayCopy(dest->mVertices, dest->mNumVertices);
1084
0
    GetArrayCopy(dest->mNormals, dest->mNumVertices);
1085
0
    GetArrayCopy(dest->mTangents, dest->mNumVertices);
1086
0
    GetArrayCopy(dest->mBitangents, dest->mNumVertices);
1087
1088
0
    unsigned int n = 0;
1089
0
    while (dest->HasTextureCoords(n)) {
1090
0
        GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
1091
0
    }
1092
1093
0
    n = 0;
1094
0
    while (dest->HasVertexColors(n)) {
1095
0
        GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
1096
0
    }
1097
1098
    // make a deep copy of all bones
1099
0
    CopyPtrArray(dest->mBones, dest->mBones, dest->mNumBones);
1100
1101
    // make a deep copy of all faces
1102
0
    GetArrayCopy(dest->mFaces, dest->mNumFaces);
1103
1104
    // make a deep copy of all blend shapes
1105
0
    CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
1106
1107
    // make a deep copy of all texture coordinate names
1108
0
    if (src->mTextureCoordsNames != nullptr) {
1109
0
        dest->mTextureCoordsNames = new aiString *[AI_MAX_NUMBER_OF_TEXTURECOORDS] {};
1110
0
        for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
1111
0
            Copy(&dest->mTextureCoordsNames[i], src->mTextureCoordsNames[i]);
1112
0
        }
1113
0
    }
1114
0
}
1115
1116
// ------------------------------------------------------------------------------------------------
1117
0
void SceneCombiner::Copy(aiAnimMesh **_dest, const aiAnimMesh *src) {
1118
0
    if (nullptr == _dest || nullptr == src) {
1119
0
        return;
1120
0
    }
1121
1122
0
    aiAnimMesh *dest = *_dest = new aiAnimMesh();
1123
1124
    // get a flat copy
1125
0
    *dest = *src;
1126
1127
    // and reallocate all arrays
1128
0
    GetArrayCopy(dest->mVertices, dest->mNumVertices);
1129
0
    GetArrayCopy(dest->mNormals, dest->mNumVertices);
1130
0
    GetArrayCopy(dest->mTangents, dest->mNumVertices);
1131
0
    GetArrayCopy(dest->mBitangents, dest->mNumVertices);
1132
1133
0
    unsigned int n = 0;
1134
0
    while (dest->HasTextureCoords(n))
1135
0
        GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
1136
1137
0
    n = 0;
1138
0
    while (dest->HasVertexColors(n))
1139
0
        GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
1140
0
}
1141
1142
// ------------------------------------------------------------------------------------------------
1143
0
void SceneCombiner::Copy(aiMaterial **_dest, const aiMaterial *src) {
1144
0
    if (nullptr == _dest || nullptr == src) {
1145
0
        return;
1146
0
    }
1147
1148
0
    aiMaterial *dest = (aiMaterial *)(*_dest = new aiMaterial());
1149
1150
0
    dest->Clear();
1151
0
    delete[] dest->mProperties;
1152
1153
0
    dest->mNumAllocated = src->mNumAllocated;
1154
0
    dest->mNumProperties = src->mNumProperties;
1155
0
    dest->mProperties = new aiMaterialProperty *[dest->mNumAllocated];
1156
1157
0
    for (unsigned int i = 0; i < dest->mNumProperties; ++i) {
1158
0
        aiMaterialProperty *prop = dest->mProperties[i] = new aiMaterialProperty();
1159
0
        aiMaterialProperty *sprop = src->mProperties[i];
1160
1161
0
        prop->mDataLength = sprop->mDataLength;
1162
0
        prop->mData = new char[prop->mDataLength];
1163
0
        ::memcpy(prop->mData, sprop->mData, prop->mDataLength);
1164
1165
0
        prop->mIndex = sprop->mIndex;
1166
0
        prop->mSemantic = sprop->mSemantic;
1167
0
        prop->mKey = sprop->mKey;
1168
0
        prop->mType = sprop->mType;
1169
0
    }
1170
0
}
1171
1172
// ------------------------------------------------------------------------------------------------
1173
0
void SceneCombiner::Copy(aiTexture **_dest, const aiTexture *src) {
1174
0
    if (nullptr == _dest || nullptr == src) {
1175
0
        return;
1176
0
    }
1177
1178
0
    aiTexture *dest = *_dest = new aiTexture();
1179
1180
    // get a flat copy
1181
0
    *dest = *src;
1182
1183
    // and reallocate all arrays. We must do it manually here
1184
0
    const char *old = (const char *)dest->pcData;
1185
0
    if (old) {
1186
0
        unsigned int cpy;
1187
0
        if (!dest->mHeight)
1188
0
            cpy = dest->mWidth;
1189
0
        else
1190
0
            cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel);
1191
1192
0
        if (!cpy) {
1193
0
            dest->pcData = nullptr;
1194
0
            return;
1195
0
        }
1196
        // the cast is legal, the aiTexel c'tor does nothing important
1197
0
        dest->pcData = (aiTexel *)new char[cpy];
1198
0
        ::memcpy(dest->pcData, old, cpy);
1199
0
    }
1200
0
}
1201
1202
// ------------------------------------------------------------------------------------------------
1203
0
void SceneCombiner::Copy(aiAnimation **_dest, const aiAnimation *src) {
1204
0
    if (nullptr == _dest || nullptr == src) {
1205
0
        return;
1206
0
    }
1207
1208
0
    aiAnimation *dest = *_dest = new aiAnimation();
1209
1210
    // get a flat copy
1211
0
    *dest = *src;
1212
1213
    // and reallocate all arrays
1214
0
    CopyPtrArray(dest->mChannels, src->mChannels, dest->mNumChannels);
1215
0
    CopyPtrArray(dest->mMorphMeshChannels, src->mMorphMeshChannels, dest->mNumMorphMeshChannels);
1216
0
}
1217
1218
// ------------------------------------------------------------------------------------------------
1219
0
void SceneCombiner::Copy(aiNodeAnim **_dest, const aiNodeAnim *src) {
1220
0
    if (nullptr == _dest || nullptr == src) {
1221
0
        return;
1222
0
    }
1223
1224
0
    aiNodeAnim *dest = *_dest = new aiNodeAnim();
1225
1226
    // get a flat copy
1227
0
    *dest = *src;
1228
1229
    // and reallocate all arrays
1230
0
    GetArrayCopy(dest->mPositionKeys, dest->mNumPositionKeys);
1231
0
    GetArrayCopy(dest->mScalingKeys, dest->mNumScalingKeys);
1232
0
    GetArrayCopy(dest->mRotationKeys, dest->mNumRotationKeys);
1233
0
}
1234
1235
0
void SceneCombiner::Copy(aiMeshMorphAnim **_dest, const aiMeshMorphAnim *src) {
1236
0
    if (nullptr == _dest || nullptr == src) {
1237
0
        return;
1238
0
    }
1239
1240
0
    aiMeshMorphAnim *dest = *_dest = new aiMeshMorphAnim();
1241
1242
    // get a flat copy
1243
0
    *dest = *src;
1244
1245
    // and reallocate all arrays
1246
0
    GetArrayCopy(dest->mKeys, dest->mNumKeys);
1247
0
    for (ai_uint i = 0; i < dest->mNumKeys; ++i) {
1248
0
        dest->mKeys[i].mValues = new unsigned int[dest->mKeys[i].mNumValuesAndWeights];
1249
0
        dest->mKeys[i].mWeights = new double[dest->mKeys[i].mNumValuesAndWeights];
1250
0
        ::memcpy(dest->mKeys[i].mValues, src->mKeys[i].mValues, dest->mKeys[i].mNumValuesAndWeights * sizeof(unsigned int));
1251
0
        ::memcpy(dest->mKeys[i].mWeights, src->mKeys[i].mWeights, dest->mKeys[i].mNumValuesAndWeights * sizeof(double));
1252
0
    }
1253
0
}
1254
1255
// ------------------------------------------------------------------------------------------------
1256
0
void SceneCombiner::Copy(aiCamera **_dest, const aiCamera *src) {
1257
0
    if (nullptr == _dest || nullptr == src) {
1258
0
        return;
1259
0
    }
1260
1261
0
    aiCamera *dest = *_dest = new aiCamera();
1262
1263
    // get a flat copy, that's already OK
1264
0
    *dest = *src;
1265
0
}
1266
1267
// ------------------------------------------------------------------------------------------------
1268
0
void SceneCombiner::Copy(aiLight **_dest, const aiLight *src) {
1269
0
    if (nullptr == _dest || nullptr == src) {
1270
0
        return;
1271
0
    }
1272
1273
0
    aiLight *dest = *_dest = new aiLight();
1274
1275
    // get a flat copy, that's already OK
1276
0
    *dest = *src;
1277
0
}
1278
1279
// ------------------------------------------------------------------------------------------------
1280
0
void SceneCombiner::Copy(aiBone **_dest, const aiBone *src) {
1281
0
    if (nullptr == _dest || nullptr == src) {
1282
0
        return;
1283
0
    }
1284
1285
0
    aiBone *dest = *_dest = new aiBone();
1286
1287
    // get a flat copy
1288
0
    *dest = *src;
1289
0
}
1290
1291
// ------------------------------------------------------------------------------------------------
1292
0
void SceneCombiner::Copy(aiNode **_dest, const aiNode *src) {
1293
0
    ai_assert(nullptr != _dest);
1294
0
    ai_assert(nullptr != src);
1295
1296
0
    aiNode *dest = *_dest = new aiNode();
1297
1298
    // get a flat copy
1299
0
    *dest = *src;
1300
1301
0
    if (src->mMetaData) {
1302
0
        Copy(&dest->mMetaData, src->mMetaData);
1303
0
    }
1304
1305
    // and reallocate all arrays
1306
0
    GetArrayCopy(dest->mMeshes, dest->mNumMeshes);
1307
0
    CopyPtrArray(dest->mChildren, src->mChildren, dest->mNumChildren);
1308
1309
    // need to set the mParent fields to the created aiNode.
1310
0
    for (unsigned int i = 0; i < dest->mNumChildren; i++) {
1311
0
        dest->mChildren[i]->mParent = dest;
1312
0
    }
1313
0
}
1314
1315
// ------------------------------------------------------------------------------------------------
1316
0
void SceneCombiner::Copy(aiMetadata **_dest, const aiMetadata *src) {
1317
0
    if (nullptr == _dest || nullptr == src) {
1318
0
        return;
1319
0
    }
1320
1321
0
    if (0 == src->mNumProperties) {
1322
0
        return;
1323
0
    }
1324
1325
0
    aiMetadata *dest = *_dest = aiMetadata::Alloc(src->mNumProperties);
1326
0
    std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys);
1327
1328
0
    for (unsigned int i = 0; i < src->mNumProperties; ++i) {
1329
0
        aiMetadataEntry &in = src->mValues[i];
1330
0
        aiMetadataEntry &out = dest->mValues[i];
1331
0
        out.mType = in.mType;
1332
0
        switch (dest->mValues[i].mType) {
1333
0
        case AI_BOOL:
1334
0
            out.mData = new bool(*static_cast<bool *>(in.mData));
1335
0
            break;
1336
0
        case AI_INT32:
1337
0
            out.mData = new int32_t(*static_cast<int32_t *>(in.mData));
1338
0
            break;
1339
0
        case AI_UINT64:
1340
0
            out.mData = new uint64_t(*static_cast<uint64_t *>(in.mData));
1341
0
            break;
1342
0
        case AI_FLOAT:
1343
0
            out.mData = new float(*static_cast<float *>(in.mData));
1344
0
            break;
1345
0
        case AI_DOUBLE:
1346
0
            out.mData = new double(*static_cast<double *>(in.mData));
1347
0
            break;
1348
0
        case AI_AISTRING:
1349
0
            out.mData = new aiString(*static_cast<aiString *>(in.mData));
1350
0
            break;
1351
0
        case AI_AIVECTOR3D:
1352
0
            out.mData = new aiVector3D(*static_cast<aiVector3D *>(in.mData));
1353
0
            break;
1354
0
        case AI_AIMETADATA:
1355
0
            out.mData = new aiMetadata(*static_cast<aiMetadata *>(in.mData));
1356
0
            break;
1357
0
        default:
1358
0
            ai_assert(false);
1359
0
            break;
1360
0
        }
1361
0
    }
1362
0
}
1363
1364
// ------------------------------------------------------------------------------------------------
1365
0
void SceneCombiner::Copy(aiString **_dest, const aiString *src) {
1366
0
    if (nullptr == _dest || nullptr == src) {
1367
0
        return;
1368
0
    }
1369
1370
0
    aiString *dest = *_dest = new aiString();
1371
1372
    // get a flat copy
1373
0
    *dest = *src;
1374
0
}
1375
1376
#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0)
1377
#pragma GCC diagnostic pop
1378
#endif
1379
1380
} // Namespace Assimp