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

source code of qtquick3d/src/3rdparty/assimp/src/code/Common/SceneCombiner.cpp