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