| /* |
| Open Asset Import Library (assimp) |
| ---------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the |
| following conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| ---------------------------------------------------------------------- |
| */ |
| |
| // TODO: refactor entire file to get rid of the "flat-copy" first approach |
| // to copying structures. This easily breaks in the most unintuitive way |
| // possible as new fields are added to assimp structures. |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @file Implements Assimp::SceneCombiner. This is a smart utility |
| * class that combines multiple scenes, meshes, ... into one. Currently |
| * these utilities are used by the IRR and LWS loaders and the |
| * OptimizeGraph step. |
| */ |
| // ---------------------------------------------------------------------------- |
| #include <assimp/SceneCombiner.h> |
| #include "StringUtils.h" |
| #include "fast_atof.h" |
| #include "Hash.h" |
| #include "time.h" |
| #include <assimp/DefaultLogger.hpp> |
| #include <assimp/scene.h> |
| #include <assimp/mesh.h> |
| #include <stdio.h> |
| #include "ScenePrivate.h" |
| |
| namespace Assimp { |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Add a prefix to a string |
| inline |
| void PrefixString(aiString& string,const char* prefix, unsigned int len) { |
| // If the string is already prefixed, we won't prefix it a second time |
| if (string.length >= 1 && string.data[0] == '$') |
| return; |
| |
| if (len+string.length>=MAXLEN-1) { |
| DefaultLogger::get()->debug("Can't add an unique prefix because the string is too long"); |
| ai_assert(false); |
| return; |
| } |
| |
| // Add the prefix |
| ::memmove(string.data+len,string.data,string.length+1); |
| ::memcpy (string.data, prefix, len); |
| |
| // And update the string's length |
| string.length += len; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Add node identifiers to a hashing set |
| void SceneCombiner::AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes) { |
| // Add node name to hashing set if it is non-empty - empty nodes are allowed |
| // and they can't have any anims assigned so its absolutely safe to duplicate them. |
| if (node->mName.length) { |
| hashes.insert( SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)) ); |
| } |
| |
| // Process all children recursively |
| for (unsigned int i = 0; i < node->mNumChildren;++i) |
| AddNodeHashes(node->mChildren[i],hashes); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Add a name prefix to all nodes in a hierarchy |
| void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len) { |
| ai_assert(NULL != prefix); |
| PrefixString(node->mName,prefix,len); |
| |
| // Process all children recursively |
| for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { |
| AddNodePrefixes( node->mChildren[ i ], prefix, len ); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Search for matching names |
| bool SceneCombiner::FindNameMatch(const aiString& name, std::vector<SceneHelper>& input, unsigned int cur) { |
| const unsigned int hash = SuperFastHash(name.data, static_cast<uint32_t>(name.length)); |
| |
| // Check whether we find a positive match in one of the given sets |
| for (unsigned int i = 0; i < input.size(); ++i) { |
| if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Add a name prefix to all nodes in a hierarchy if a hash match is found |
| void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, unsigned int len, |
| std::vector<SceneHelper>& input, unsigned int cur) { |
| ai_assert(NULL != prefix); |
| const unsigned int hash = SuperFastHash(node->mName.data, static_cast<uint32_t>(node->mName.length)); |
| |
| // Check whether we find a positive match in one of the given sets |
| for (unsigned int i = 0; i < input.size(); ++i) { |
| if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) { |
| PrefixString(node->mName,prefix,len); |
| break; |
| } |
| } |
| |
| // Process all children recursively |
| for (unsigned int i = 0; i < node->mNumChildren;++i) |
| AddNodePrefixesChecked(node->mChildren[i],prefix,len,input,cur); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Add an offset to all mesh indices in a node graph |
| void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset) { |
| for (unsigned int i = 0; i < node->mNumMeshes;++i) |
| node->mMeshes[i] += offset; |
| |
| for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { |
| OffsetNodeMeshIndices( node->mChildren[ i ], offset ); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Merges two scenes. Currently only used by the LWS loader. |
| void SceneCombiner::MergeScenes(aiScene** _dest,std::vector<aiScene*>& src, unsigned int flags) { |
| if ( nullptr == _dest ) { |
| return; |
| } |
| |
| // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it |
| if (src.empty()) { |
| if (*_dest) { |
| (*_dest)->~aiScene(); |
| SceneCombiner::CopySceneFlat(_dest,src[0]); |
| } |
| else *_dest = src[0]; |
| return; |
| } |
| if (*_dest)(*_dest)->~aiScene(); |
| else *_dest = new aiScene(); |
| |
| // Create a dummy scene to serve as master for the others |
| aiScene* master = new aiScene(); |
| master->mRootNode = new aiNode(); |
| master->mRootNode->mName.Set("<MergeRoot>"); |
| |
| std::vector<AttachmentInfo> srcList (src.size()); |
| for (unsigned int i = 0; i < srcList.size();++i) { |
| srcList[i] = AttachmentInfo(src[i],master->mRootNode); |
| } |
| |
| // 'master' will be deleted afterwards |
| MergeScenes (_dest, master, srcList, flags); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::AttachToGraph (aiNode* attach, std::vector<NodeAttachmentInfo>& srcList) { |
| unsigned int cnt; |
| for ( cnt = 0; cnt < attach->mNumChildren; ++cnt ) { |
| AttachToGraph( attach->mChildren[ cnt ], srcList ); |
| } |
| |
| cnt = 0; |
| for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin(); |
| it != srcList.end(); ++it) |
| { |
| if ((*it).attachToNode == attach && !(*it).resolved) |
| ++cnt; |
| } |
| |
| if (cnt) { |
| aiNode** n = new aiNode*[cnt+attach->mNumChildren]; |
| if (attach->mNumChildren) { |
| ::memcpy(n,attach->mChildren,sizeof(void*)*attach->mNumChildren); |
| delete[] attach->mChildren; |
| } |
| attach->mChildren = n; |
| |
| n += attach->mNumChildren; |
| attach->mNumChildren += cnt; |
| |
| for (unsigned int i = 0; i < srcList.size();++i) { |
| NodeAttachmentInfo& att = srcList[i]; |
| if (att.attachToNode == attach && !att.resolved) { |
| *n = att.node; |
| (**n).mParent = attach; |
| ++n; |
| |
| // mark this attachment as resolved |
| att.resolved = true; |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::AttachToGraph ( aiScene* master, std::vector<NodeAttachmentInfo>& src) { |
| ai_assert(NULL != master); |
| AttachToGraph(master->mRootNode,src); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master, std::vector<AttachmentInfo>& srcList, unsigned int flags) { |
| if ( nullptr == _dest ) { |
| return; |
| } |
| |
| // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it |
| if (srcList.empty()) { |
| if (*_dest) { |
| SceneCombiner::CopySceneFlat(_dest,master); |
| } |
| else *_dest = master; |
| return; |
| } |
| if (*_dest) { |
| (*_dest)->~aiScene(); |
| new (*_dest) aiScene(); |
| } |
| else *_dest = new aiScene(); |
| |
| aiScene* dest = *_dest; |
| |
| std::vector<SceneHelper> src (srcList.size()+1); |
| src[0].scene = master; |
| for (unsigned int i = 0; i < srcList.size();++i) { |
| src[i+1] = SceneHelper( srcList[i].scene ); |
| } |
| |
| // this helper array specifies which scenes are duplicates of others |
| std::vector<unsigned int> duplicates(src.size(),UINT_MAX); |
| |
| // this helper array is used as lookup table several times |
| std::vector<unsigned int> offset(src.size()); |
| |
| // Find duplicate scenes |
| for (unsigned int i = 0; i < src.size();++i) { |
| if (duplicates[i] != i && duplicates[i] != UINT_MAX) { |
| continue; |
| } |
| |
| duplicates[i] = i; |
| for ( unsigned int a = i+1; a < src.size(); ++a) { |
| if (src[i].scene == src[a].scene) { |
| duplicates[a] = i; |
| } |
| } |
| } |
| |
| // Generate unique names for all named stuff? |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) |
| { |
| #if 0 |
| // Construct a proper random number generator |
| boost::mt19937 rng( ); |
| boost::uniform_int<> dist(1u,1 << 24u); |
| boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rndGen(rng, dist); |
| #endif |
| for (unsigned int i = 1; i < src.size();++i) |
| { |
| //if (i != duplicates[i]) |
| //{ |
| // // duplicate scenes share the same UID |
| // ::strcpy( src[i].id, src[duplicates[i]].id ); |
| // src[i].idlen = src[duplicates[i]].idlen; |
| |
| // continue; |
| //} |
| |
| src[i].idlen = ai_snprintf(src[i].id, 32, "$%.6X$_",i); |
| |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| |
| // Compute hashes for all identifiers in this scene and store them |
| // in a sorted table (for convenience I'm using std::set). We hash |
| // just the node and animation channel names, all identifiers except |
| // the material names should be caught by doing this. |
| AddNodeHashes(src[i]->mRootNode,src[i].hashes); |
| |
| for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) { |
| aiAnimation* anim = src[i]->mAnimations[a]; |
| src[i].hashes.insert(SuperFastHash(anim->mName.data,static_cast<uint32_t>(anim->mName.length))); |
| } |
| } |
| } |
| } |
| |
| unsigned int cnt; |
| |
| // First find out how large the respective output arrays must be |
| for ( unsigned int n = 0; n < src.size();++n ) |
| { |
| SceneHelper* cur = &src[n]; |
| |
| if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { |
| dest->mNumTextures += (*cur)->mNumTextures; |
| dest->mNumMaterials += (*cur)->mNumMaterials; |
| dest->mNumMeshes += (*cur)->mNumMeshes; |
| } |
| |
| dest->mNumLights += (*cur)->mNumLights; |
| dest->mNumCameras += (*cur)->mNumCameras; |
| dest->mNumAnimations += (*cur)->mNumAnimations; |
| |
| // Combine the flags of all scenes |
| // We need to process them flag-by-flag here to get correct results |
| // dest->mFlags ; //|= (*cur)->mFlags; |
| if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { |
| dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; |
| } |
| } |
| |
| // generate the output texture list + an offset table for all texture indices |
| if (dest->mNumTextures) |
| { |
| aiTexture** pip = dest->mTextures = new aiTexture*[dest->mNumMaterials]; |
| cnt = 0; |
| for ( unsigned int n = 0; n < src.size();++n ) |
| { |
| SceneHelper* cur = &src[n]; |
| for (unsigned int i = 0; i < (*cur)->mNumTextures;++i) |
| { |
| if (n != duplicates[n]) |
| { |
| if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) |
| Copy(pip,(*cur)->mTextures[i]); |
| |
| else continue; |
| } |
| else *pip = (*cur)->mTextures[i]; |
| ++pip; |
| } |
| |
| offset[n] = cnt; |
| cnt = (unsigned int)(pip - dest->mTextures); |
| } |
| } |
| |
| // generate the output material list + an offset table for all material indices |
| if (dest->mNumMaterials) |
| { |
| aiMaterial** pip = dest->mMaterials = new aiMaterial*[dest->mNumMaterials]; |
| cnt = 0; |
| for ( unsigned int n = 0; n < src.size();++n ) { |
| SceneHelper* cur = &src[n]; |
| for (unsigned int i = 0; i < (*cur)->mNumMaterials;++i) |
| { |
| if (n != duplicates[n]) |
| { |
| if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) |
| Copy(pip,(*cur)->mMaterials[i]); |
| |
| else continue; |
| } |
| else *pip = (*cur)->mMaterials[i]; |
| |
| if ((*cur)->mNumTextures != dest->mNumTextures) { |
| // We need to update all texture indices of the mesh. So we need to search for |
| // a material property called '$tex.file' |
| |
| for (unsigned int a = 0; a < (*pip)->mNumProperties;++a) |
| { |
| aiMaterialProperty* prop = (*pip)->mProperties[a]; |
| if (!strncmp(prop->mKey.data,"$tex.file",9)) |
| { |
| // Check whether this texture is an embedded texture. |
| // In this case the property looks like this: *<n>, |
| // where n is the index of the texture. |
| aiString& s = *((aiString*)prop->mData); |
| if ('*' == s.data[0]) { |
| // Offset the index and write it back .. |
| const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; |
| ASSIMP_itoa10(&s.data[1],sizeof(s.data)-1,idx); |
| } |
| } |
| |
| // Need to generate new, unique material names? |
| else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) |
| { |
| aiString* pcSrc = (aiString*) prop->mData; |
| PrefixString(*pcSrc, (*cur).id, (*cur).idlen); |
| } |
| } |
| } |
| ++pip; |
| } |
| |
| offset[n] = cnt; |
| cnt = (unsigned int)(pip - dest->mMaterials); |
| } |
| } |
| |
| // generate the output mesh list + again an offset table for all mesh indices |
| if (dest->mNumMeshes) |
| { |
| aiMesh** pip = dest->mMeshes = new aiMesh*[dest->mNumMeshes]; |
| cnt = 0; |
| for ( unsigned int n = 0; n < src.size();++n ) |
| { |
| SceneHelper* cur = &src[n]; |
| for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) |
| { |
| if (n != duplicates[n]) { |
| if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) |
| Copy(pip, (*cur)->mMeshes[i]); |
| |
| else continue; |
| } |
| else *pip = (*cur)->mMeshes[i]; |
| |
| // update the material index of the mesh |
| (*pip)->mMaterialIndex += offset[n]; |
| ++pip; |
| } |
| |
| // reuse the offset array - store now the mesh offset in it |
| offset[n] = cnt; |
| cnt = (unsigned int)(pip - dest->mMeshes); |
| } |
| } |
| |
| std::vector <NodeAttachmentInfo> nodes; |
| nodes.reserve(srcList.size()); |
| |
| // ---------------------------------------------------------------------------- |
| // Now generate the output node graph. We need to make those |
| // names in the graph that are referenced by anims or lights |
| // or cameras unique. So we add a prefix to them ... $<rand>_ |
| // We could also use a counter, but using a random value allows us to |
| // use just one prefix if we are joining multiple scene hierarchies recursively. |
| // Chances are quite good we don't collide, so we try that ... |
| // ---------------------------------------------------------------------------- |
| |
| // Allocate space for light sources, cameras and animations |
| aiLight** ppLights = dest->mLights = (dest->mNumLights |
| ? new aiLight*[dest->mNumLights] : NULL); |
| |
| aiCamera** ppCameras = dest->mCameras = (dest->mNumCameras |
| ? new aiCamera*[dest->mNumCameras] : NULL); |
| |
| aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations |
| ? new aiAnimation*[dest->mNumAnimations] : NULL); |
| |
| for ( int n = static_cast<int>(src.size()-1); n >= 0 ;--n ) /* !!! important !!! */ |
| { |
| SceneHelper* cur = &src[n]; |
| aiNode* node; |
| |
| // To offset or not to offset, this is the question |
| if (n != (int)duplicates[n]) |
| { |
| // Get full scene-graph copy |
| Copy( &node, (*cur)->mRootNode ); |
| OffsetNodeMeshIndices(node,offset[duplicates[n]]); |
| |
| if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) { |
| // (note:) they are already 'offseted' by offset[duplicates[n]] |
| OffsetNodeMeshIndices(node,offset[n] - offset[duplicates[n]]); |
| } |
| } |
| else // if (n == duplicates[n]) |
| { |
| node = (*cur)->mRootNode; |
| OffsetNodeMeshIndices(node,offset[n]); |
| } |
| if (n) // src[0] is the master node |
| nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n )); |
| |
| // add name prefixes? |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { |
| |
| // or the whole scenegraph |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n); |
| } |
| else AddNodePrefixes(node,(*cur).id,(*cur).idlen); |
| |
| // meshes |
| for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) { |
| aiMesh* mesh = (*cur)->mMeshes[i]; |
| |
| // rename all bones |
| for (unsigned int a = 0; a < mesh->mNumBones;++a) { |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| if (!FindNameMatch(mesh->mBones[a]->mName,src,n)) |
| continue; |
| } |
| PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen); |
| } |
| } |
| } |
| |
| // -------------------------------------------------------------------- |
| // Copy light sources |
| for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights) |
| { |
| if (n != (int)duplicates[n]) // duplicate scene? |
| { |
| Copy(ppLights, (*cur)->mLights[i]); |
| } |
| else *ppLights = (*cur)->mLights[i]; |
| |
| |
| // Add name prefixes? |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| if (!FindNameMatch((*ppLights)->mName,src,n)) |
| continue; |
| } |
| |
| PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen); |
| } |
| } |
| |
| // -------------------------------------------------------------------- |
| // Copy cameras |
| for (unsigned int i = 0; i < (*cur)->mNumCameras;++i,++ppCameras) { |
| if (n != (int)duplicates[n]) // duplicate scene? |
| { |
| Copy(ppCameras, (*cur)->mCameras[i]); |
| } |
| else *ppCameras = (*cur)->mCameras[i]; |
| |
| // Add name prefixes? |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| if (!FindNameMatch((*ppCameras)->mName,src,n)) |
| continue; |
| } |
| |
| PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen); |
| } |
| } |
| |
| // -------------------------------------------------------------------- |
| // Copy animations |
| for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i,++ppAnims) { |
| if (n != (int)duplicates[n]) // duplicate scene? |
| { |
| Copy(ppAnims, (*cur)->mAnimations[i]); |
| } |
| else *ppAnims = (*cur)->mAnimations[i]; |
| |
| // Add name prefixes? |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) { |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| if (!FindNameMatch((*ppAnims)->mName,src,n)) |
| continue; |
| } |
| |
| PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen); |
| |
| // don't forget to update all node animation channels |
| for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) { |
| if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) { |
| if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n)) |
| continue; |
| } |
| |
| PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen); |
| } |
| } |
| } |
| } |
| |
| // Now build the output graph |
| AttachToGraph ( master, nodes); |
| dest->mRootNode = master->mRootNode; |
| |
| // Check whether we succeeded at building the output graph |
| for (std::vector <NodeAttachmentInfo> ::iterator it = nodes.begin(); |
| it != nodes.end(); ++it) |
| { |
| if (!(*it).resolved) { |
| if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) { |
| // search for this attachment point in all other imported scenes, too. |
| for ( unsigned int n = 0; n < src.size();++n ) { |
| if (n != (*it).src_idx) { |
| AttachToGraph(src[n].scene,nodes); |
| if ((*it).resolved) |
| break; |
| } |
| } |
| } |
| if (!(*it).resolved) { |
| DefaultLogger::get()->error(std::string("SceneCombiner: Failed to resolve attachment ") |
| + (*it).node->mName.data + " " + (*it).attachToNode->mName.data); |
| } |
| } |
| } |
| |
| // now delete all input scenes. Make sure duplicate scenes aren't |
| // deleted more than one time |
| for ( unsigned int n = 0; n < src.size();++n ) { |
| if (n != duplicates[n]) // duplicate scene? |
| continue; |
| |
| aiScene* deleteMe = src[n].scene; |
| |
| // We need to delete the arrays before the destructor is called - |
| // we are reusing the array members |
| delete[] deleteMe->mMeshes; deleteMe->mMeshes = NULL; |
| delete[] deleteMe->mCameras; deleteMe->mCameras = NULL; |
| delete[] deleteMe->mLights; deleteMe->mLights = NULL; |
| delete[] deleteMe->mMaterials; deleteMe->mMaterials = NULL; |
| delete[] deleteMe->mAnimations; deleteMe->mAnimations = NULL; |
| |
| deleteMe->mRootNode = NULL; |
| |
| // Now we can safely delete the scene |
| delete deleteMe; |
| } |
| |
| // Check flags |
| if (!dest->mNumMeshes || !dest->mNumMaterials) { |
| dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; |
| } |
| |
| // We're finished |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Build a list of unique bones |
| void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash>& asBones, |
| std::vector<aiMesh*>::const_iterator it, |
| std::vector<aiMesh*>::const_iterator end) |
| { |
| unsigned int iOffset = 0; |
| for (; it != end;++it) { |
| for (unsigned int l = 0; l < (*it)->mNumBones;++l) { |
| aiBone* p = (*it)->mBones[l]; |
| uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length); |
| |
| std::list<BoneWithHash>::iterator it2 = asBones.begin(); |
| std::list<BoneWithHash>::iterator end2 = asBones.end(); |
| |
| for (;it2 != end2;++it2) { |
| if ((*it2).first == itml) { |
| (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset)); |
| break; |
| } |
| } |
| if (end2 == it2) { |
| // need to begin a new bone entry |
| asBones.push_back(BoneWithHash()); |
| BoneWithHash& btz = asBones.back(); |
| |
| // setup members |
| btz.first = itml; |
| btz.second = &p->mName; |
| btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset)); |
| } |
| } |
| iOffset += (*it)->mNumVertices; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Merge a list of bones |
| void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it, |
| std::vector<aiMesh*>::const_iterator end) |
| { |
| if ( nullptr == out || out->mNumBones == 0 ) { |
| return; |
| } |
| |
| // find we need to build an unique list of all bones. |
| // we work with hashes to make the comparisons MUCH faster, |
| // at least if we have many bones. |
| std::list<BoneWithHash> asBones; |
| BuildUniqueBoneList(asBones, it,end); |
| |
| // now create the output bones |
| out->mNumBones = 0; |
| out->mBones = new aiBone*[asBones.size()]; |
| |
| for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),end = asBones.end(); it != end;++it) { |
| // Allocate a bone and setup it's name |
| aiBone* pc = out->mBones[out->mNumBones++] = new aiBone(); |
| pc->mName = aiString( *((*it).second )); |
| |
| std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end(); |
| |
| // Loop through all bones to be joined for this bone |
| for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) { |
| pc->mNumWeights += (*wmit).first->mNumWeights; |
| |
| // NOTE: different offset matrices for bones with equal names |
| // are - at the moment - not handled correctly. |
| if (wmit != (*it).pSrcBones.begin() && pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix) { |
| DefaultLogger::get()->warn("Bones with equal names but different offset matrices can't be joined at the moment"); |
| continue; |
| } |
| pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix; |
| } |
| |
| // Allocate the vertex weight array |
| aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights]; |
| |
| // And copy the final weights - adjust the vertex IDs by the |
| // face index offset of the corresponding mesh. |
| for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) { |
| aiBone* pip = (*wmit).first; |
| for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) { |
| const aiVertexWeight& vfi = pip->mWeights[mp]; |
| avw->mWeight = vfi.mWeight; |
| avw->mVertexId = vfi.mVertexId + (*wmit).second; |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| // Merge a list of meshes |
| void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, |
| std::vector<aiMesh*>::const_iterator begin, |
| std::vector<aiMesh*>::const_iterator end) |
| { |
| if ( nullptr == _out ) { |
| return; |
| } |
| |
| if (begin == end) { |
| *_out = NULL; // no meshes ... |
| return; |
| } |
| |
| // Allocate the output mesh |
| aiMesh* out = *_out = new aiMesh(); |
| out->mMaterialIndex = (*begin)->mMaterialIndex; |
| |
| std::string name; |
| // Find out how much output storage we'll need |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it) { |
| const char *meshName( (*it)->mName.C_Str() ); |
| name += std::string( meshName ); |
| if ( it != end - 1 ) { |
| name += "."; |
| } |
| out->mNumVertices += (*it)->mNumVertices; |
| out->mNumFaces += (*it)->mNumFaces; |
| out->mNumBones += (*it)->mNumBones; |
| |
| // combine primitive type flags |
| out->mPrimitiveTypes |= (*it)->mPrimitiveTypes; |
| } |
| out->mName.Set( name.c_str() ); |
| |
| if (out->mNumVertices) { |
| aiVector3D* pv2; |
| |
| // copy vertex positions |
| if ((**begin).HasPositions()) { |
| |
| pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end; ++it) { |
| if ((*it)->mVertices) { |
| ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D)); |
| } |
| else DefaultLogger::get()->warn("JoinMeshes: Positions expected but input mesh contains no positions"); |
| pv2 += (*it)->mNumVertices; |
| } |
| } |
| // copy normals |
| if ((**begin).HasNormals()) { |
| |
| pv2 = out->mNormals = new aiVector3D[out->mNumVertices]; |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { |
| if ((*it)->mNormals) { |
| ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); |
| } |
| else DefaultLogger::get()->warn("JoinMeshes: Normals expected but input mesh contains no normals"); |
| pv2 += (*it)->mNumVertices; |
| } |
| } |
| // copy tangents and bi-tangents |
| if ((**begin).HasTangentsAndBitangents()) { |
| |
| pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; |
| aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; |
| |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { |
| if ((*it)->mTangents) { |
| ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); |
| ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); |
| } |
| else DefaultLogger::get()->warn("JoinMeshes: Tangents expected but input mesh contains no tangents"); |
| pv2 += (*it)->mNumVertices; |
| pv2b += (*it)->mNumVertices; |
| } |
| } |
| // copy texture coordinates |
| unsigned int n = 0; |
| while ((**begin).HasTextureCoords(n)) { |
| out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; |
| |
| pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { |
| |
| if ((*it)->mTextureCoords[n]) { |
| ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); |
| } |
| else DefaultLogger::get()->warn("JoinMeshes: UVs expected but input mesh contains no UVs"); |
| pv2 += (*it)->mNumVertices; |
| } |
| ++n; |
| } |
| // copy vertex colors |
| n = 0; |
| while ((**begin).HasVertexColors(n)) { |
| aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { |
| |
| if ((*it)->mColors[n]) { |
| ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D)); |
| } |
| else DefaultLogger::get()->warn("JoinMeshes: VCs expected but input mesh contains no VCs"); |
| pv2 += (*it)->mNumVertices; |
| } |
| ++n; |
| } |
| } |
| |
| if (out->mNumFaces) // just for safety |
| { |
| // copy faces |
| out->mFaces = new aiFace[out->mNumFaces]; |
| aiFace* pf2 = out->mFaces; |
| |
| unsigned int ofs = 0; |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) { |
| for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2) { |
| aiFace& face = (*it)->mFaces[m]; |
| pf2->mNumIndices = face.mNumIndices; |
| pf2->mIndices = face.mIndices; |
| |
| if (ofs) { |
| // add the offset to the vertex |
| for (unsigned int q = 0; q < face.mNumIndices; ++q) |
| face.mIndices[q] += ofs; |
| } |
| face.mIndices = NULL; |
| } |
| ofs += (*it)->mNumVertices; |
| } |
| } |
| |
| // bones - as this is quite lengthy, I moved the code to a separate function |
| if (out->mNumBones) |
| MergeBones(out,begin,end); |
| |
| // delete all source meshes |
| for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) |
| delete *it; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::MergeMaterials(aiMaterial** dest, |
| std::vector<aiMaterial*>::const_iterator begin, |
| std::vector<aiMaterial*>::const_iterator end) |
| { |
| if ( nullptr == dest ) { |
| return; |
| } |
| |
| if (begin == end) { |
| *dest = NULL; // no materials ... |
| return; |
| } |
| |
| // Allocate the output material |
| aiMaterial* out = *dest = new aiMaterial(); |
| |
| // Get the maximal number of properties |
| unsigned int size = 0; |
| for (std::vector<aiMaterial*>::const_iterator it = begin; it != end; ++it) { |
| size += (*it)->mNumProperties; |
| } |
| |
| out->Clear(); |
| delete[] out->mProperties; |
| |
| out->mNumAllocated = size; |
| out->mNumProperties = 0; |
| out->mProperties = new aiMaterialProperty*[out->mNumAllocated]; |
| |
| for (std::vector<aiMaterial*>::const_iterator it = begin; it != end; ++it) { |
| for(unsigned int i = 0; i < (*it)->mNumProperties; ++i) { |
| aiMaterialProperty* sprop = (*it)->mProperties[i]; |
| |
| // Test if we already have a matching property |
| const aiMaterialProperty* prop_exist; |
| if(aiGetMaterialProperty(out, sprop->mKey.C_Str(), sprop->mSemantic, sprop->mIndex, &prop_exist) != AI_SUCCESS) { |
| // If not, we add it to the new material |
| aiMaterialProperty* prop = out->mProperties[out->mNumProperties] = new aiMaterialProperty(); |
| |
| prop->mDataLength = sprop->mDataLength; |
| prop->mData = new char[prop->mDataLength]; |
| ::memcpy(prop->mData, sprop->mData, prop->mDataLength); |
| |
| prop->mIndex = sprop->mIndex; |
| prop->mSemantic = sprop->mSemantic; |
| prop->mKey = sprop->mKey; |
| prop->mType = sprop->mType; |
| |
| out->mNumProperties++; |
| } |
| } |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| template <typename Type> |
| inline |
| void CopyPtrArray (Type**& dest, const Type* const * src, ai_uint num) { |
| if (!num) { |
| dest = NULL; |
| return; |
| } |
| dest = new Type*[num]; |
| for (ai_uint i = 0; i < num;++i) { |
| SceneCombiner::Copy(&dest[i],src[i]); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| template <typename Type> |
| inline |
| void GetArrayCopy(Type*& dest, ai_uint num ) { |
| if ( !dest ) { |
| return; |
| } |
| Type* old = dest; |
| |
| dest = new Type[num]; |
| ::memcpy(dest, old, sizeof(Type) * num); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::CopySceneFlat(aiScene** _dest,const aiScene* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| // reuse the old scene or allocate a new? |
| if (*_dest) { |
| (*_dest)->~aiScene(); |
| new (*_dest) aiScene(); |
| } else { |
| *_dest = new aiScene(); |
| } |
| |
| ::memcpy(*_dest,src,sizeof(aiScene)); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| if (allocate) { |
| *_dest = new aiScene(); |
| } |
| aiScene* dest = *_dest; |
| ai_assert(dest); |
| |
| // copy animations |
| dest->mNumAnimations = src->mNumAnimations; |
| CopyPtrArray(dest->mAnimations,src->mAnimations, |
| dest->mNumAnimations); |
| |
| // copy textures |
| dest->mNumTextures = src->mNumTextures; |
| CopyPtrArray(dest->mTextures,src->mTextures, |
| dest->mNumTextures); |
| |
| // copy materials |
| dest->mNumMaterials = src->mNumMaterials; |
| CopyPtrArray(dest->mMaterials,src->mMaterials, |
| dest->mNumMaterials); |
| |
| // copy lights |
| dest->mNumLights = src->mNumLights; |
| CopyPtrArray(dest->mLights,src->mLights, |
| dest->mNumLights); |
| |
| // copy cameras |
| dest->mNumCameras = src->mNumCameras; |
| CopyPtrArray(dest->mCameras,src->mCameras, |
| dest->mNumCameras); |
| |
| // copy meshes |
| dest->mNumMeshes = src->mNumMeshes; |
| CopyPtrArray(dest->mMeshes,src->mMeshes, |
| dest->mNumMeshes); |
| |
| // now - copy the root node of the scene (deep copy, too) |
| Copy( &dest->mRootNode, src->mRootNode); |
| |
| // and keep the flags ... |
| dest->mFlags = src->mFlags; |
| |
| // source private data might be NULL if the scene is user-allocated (i.e. for use with the export API) |
| if (dest->mPrivate != NULL) { |
| ScenePriv(dest)->mPPStepsApplied = ScenePriv(src) ? ScenePriv(src)->mPPStepsApplied : 0; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiMesh* dest = *_dest = new aiMesh(); |
| |
| // get a flat copy |
| ::memcpy(dest,src,sizeof(aiMesh)); |
| |
| // and reallocate all arrays |
| GetArrayCopy( dest->mVertices, dest->mNumVertices ); |
| GetArrayCopy( dest->mNormals , dest->mNumVertices ); |
| GetArrayCopy( dest->mTangents, dest->mNumVertices ); |
| GetArrayCopy( dest->mBitangents, dest->mNumVertices ); |
| |
| unsigned int n = 0; |
| while (dest->HasTextureCoords(n)) |
| GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices ); |
| |
| n = 0; |
| while (dest->HasVertexColors(n)) |
| GetArrayCopy( dest->mColors[n++], dest->mNumVertices ); |
| |
| // make a deep copy of all bones |
| CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones); |
| |
| // make a deep copy of all faces |
| GetArrayCopy(dest->mFaces,dest->mNumFaces); |
| for (unsigned int i = 0; i < dest->mNumFaces;++i) { |
| aiFace& f = dest->mFaces[i]; |
| GetArrayCopy(f.mIndices,f.mNumIndices); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiMaterial* dest = (aiMaterial*) ( *_dest = new aiMaterial() ); |
| |
| dest->Clear(); |
| delete[] dest->mProperties; |
| |
| dest->mNumAllocated = src->mNumAllocated; |
| dest->mNumProperties = src->mNumProperties; |
| dest->mProperties = new aiMaterialProperty* [dest->mNumAllocated]; |
| |
| for (unsigned int i = 0; i < dest->mNumProperties;++i) |
| { |
| aiMaterialProperty* prop = dest->mProperties[i] = new aiMaterialProperty(); |
| aiMaterialProperty* sprop = src->mProperties[i]; |
| |
| prop->mDataLength = sprop->mDataLength; |
| prop->mData = new char[prop->mDataLength]; |
| ::memcpy(prop->mData,sprop->mData,prop->mDataLength); |
| |
| prop->mIndex = sprop->mIndex; |
| prop->mSemantic = sprop->mSemantic; |
| prop->mKey = sprop->mKey; |
| prop->mType = sprop->mType; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy(aiTexture** _dest, const aiTexture* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiTexture* dest = *_dest = new aiTexture(); |
| |
| // get a flat copy |
| ::memcpy(dest,src,sizeof(aiTexture)); |
| |
| // and reallocate all arrays. We must do it manually here |
| const char* old = (const char*)dest->pcData; |
| if (old) |
| { |
| unsigned int cpy; |
| if (!dest->mHeight)cpy = dest->mWidth; |
| else cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel); |
| |
| if (!cpy) |
| { |
| dest->pcData = NULL; |
| return; |
| } |
| // the cast is legal, the aiTexel c'tor does nothing important |
| dest->pcData = (aiTexel*) new char[cpy]; |
| ::memcpy(dest->pcData, old, cpy); |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiAnimation* dest = *_dest = new aiAnimation(); |
| |
| // get a flat copy |
| ::memcpy(dest,src,sizeof(aiAnimation)); |
| |
| // and reallocate all arrays |
| CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels ); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiNodeAnim* dest = *_dest = new aiNodeAnim(); |
| |
| // get a flat copy |
| ::memcpy(dest,src,sizeof(aiNodeAnim)); |
| |
| // and reallocate all arrays |
| GetArrayCopy( dest->mPositionKeys, dest->mNumPositionKeys ); |
| GetArrayCopy( dest->mScalingKeys, dest->mNumScalingKeys ); |
| GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys ); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiCamera* dest = *_dest = new aiCamera(); |
| |
| // get a flat copy, that's already OK |
| ::memcpy(dest,src,sizeof(aiCamera)); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy(aiLight** _dest, const aiLight* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiLight* dest = *_dest = new aiLight(); |
| |
| // get a flat copy, that's already OK |
| ::memcpy(dest,src,sizeof(aiLight)); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy(aiBone** _dest, const aiBone* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| aiBone* dest = *_dest = new aiBone(); |
| |
| // get a flat copy |
| ::memcpy(dest,src,sizeof(aiBone)); |
| |
| // and reallocate all arrays |
| GetArrayCopy( dest->mWeights, dest->mNumWeights ); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy (aiNode** _dest, const aiNode* src) |
| { |
| ai_assert(NULL != _dest && NULL != src); |
| |
| aiNode* dest = *_dest = new aiNode(); |
| |
| // get a flat copy |
| ::memcpy(dest,src,sizeof(aiNode)); |
| |
| if (src->mMetaData) { |
| Copy(&dest->mMetaData, src->mMetaData); |
| } |
| |
| // and reallocate all arrays |
| GetArrayCopy( dest->mMeshes, dest->mNumMeshes ); |
| CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren); |
| |
| // need to set the mParent fields to the created aiNode. |
| for( unsigned int i = 0; i < dest->mNumChildren; i ++ ) { |
| dest->mChildren[i]->mParent = dest; |
| } |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { |
| if ( nullptr == _dest || nullptr == src ) { |
| return; |
| } |
| |
| if ( 0 == src->mNumProperties ) { |
| return; |
| } |
| |
| aiMetadata* dest = *_dest = aiMetadata::Alloc( src->mNumProperties ); |
| std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys); |
| |
| dest->mValues = new aiMetadataEntry[src->mNumProperties]; |
| for (unsigned int i = 0; i < src->mNumProperties; ++i) { |
| aiMetadataEntry& in = src->mValues[i]; |
| aiMetadataEntry& out = dest->mValues[i]; |
| out.mType = in.mType; |
| switch (dest->mValues[i].mType) { |
| case AI_BOOL: |
| out.mData = new bool(*static_cast<bool*>(in.mData)); |
| break; |
| case AI_INT32: |
| out.mData = new int32_t(*static_cast<int32_t*>(in.mData)); |
| break; |
| case AI_UINT64: |
| out.mData = new uint64_t(*static_cast<uint64_t*>(in.mData)); |
| break; |
| case AI_FLOAT: |
| out.mData = new float(*static_cast<float*>(in.mData)); |
| break; |
| case AI_DOUBLE: |
| out.mData = new double(*static_cast<double*>(in.mData)); |
| break; |
| case AI_AISTRING: |
| out.mData = new aiString(*static_cast<aiString*>(in.mData)); |
| break; |
| case AI_AIVECTOR3D: |
| out.mData = new aiVector3D(*static_cast<aiVector3D*>(in.mData)); |
| break; |
| default: |
| ai_assert(false); |
| } |
| } |
| } |
| |
| } // Namespace Assimp |
| |