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