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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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