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