1 | /* |
2 | Open Asset Import Library (assimp) |
3 | ---------------------------------------------------------------------- |
4 | |
5 | Copyright (c) 2006-2017, assimp team |
6 | |
7 | All rights reserved. |
8 | |
9 | Redistribution and use of this software in source and binary forms, |
10 | with or without modification, are permitted provided that the |
11 | following 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 | |
27 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
28 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
29 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
30 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
31 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
32 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
33 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
34 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
35 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
36 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
37 | OF 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 | |
65 | namespace Assimp { |
66 | |
67 | // ------------------------------------------------------------------------------------------------ |
68 | // Add a prefix to a string |
69 | inline |
70 | void 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 |
91 | void 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 |
105 | void 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 |
117 | bool 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 |
131 | void 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 |
151 | void 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. |
162 | void 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 | // ------------------------------------------------------------------------------------------------ |
194 | void 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 | // ------------------------------------------------------------------------------------------------ |
234 | void SceneCombiner::AttachToGraph ( aiScene* master, std::vector<NodeAttachmentInfo>& src) { |
235 | ai_assert(NULL != master); |
236 | AttachToGraph(master->mRootNode,src); |
237 | } |
238 | |
239 | // ------------------------------------------------------------------------------------------------ |
240 | void 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 |
661 | void 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 |
697 | void 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 |
752 | void 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 | // ------------------------------------------------------------------------------------------------ |
895 | void 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 | // ------------------------------------------------------------------------------------------------ |
950 | template <typename Type> |
951 | inline |
952 | void 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 | // ------------------------------------------------------------------------------------------------ |
964 | template <typename Type> |
965 | inline |
966 | void 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 | // ------------------------------------------------------------------------------------------------ |
977 | void 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 | // ------------------------------------------------------------------------------------------------ |
994 | void 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 | // ------------------------------------------------------------------------------------------------ |
1048 | void 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 | // ------------------------------------------------------------------------------------------------ |
1084 | void 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 | // ------------------------------------------------------------------------------------------------ |
1115 | void 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 | // ------------------------------------------------------------------------------------------------ |
1145 | void 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 | // ------------------------------------------------------------------------------------------------ |
1160 | void 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 | // ------------------------------------------------------------------------------------------------ |
1177 | void 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 | // ------------------------------------------------------------------------------------------------ |
1189 | void 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 | // ------------------------------------------------------------------------------------------------ |
1201 | void 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 | // ------------------------------------------------------------------------------------------------ |
1216 | void 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 | // ------------------------------------------------------------------------------------------------ |
1240 | void 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 | |