summaryrefslogtreecommitdiff
path: root/src/mesh/assimp-master/code/Common/SceneCombiner.cpp
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-03-04 10:47:15 -0600
committersanine <sanine.not@pm.me>2022-03-04 10:47:15 -0600
commit058f98a63658dc1a2579826ba167fd61bed1e21f (patch)
treebcba07a1615a14d943f3af3f815a42f3be86b2f3 /src/mesh/assimp-master/code/Common/SceneCombiner.cpp
parent2f8028ac9e0812cb6f3cbb08f0f419e4e717bd22 (diff)
add assimp submodule
Diffstat (limited to 'src/mesh/assimp-master/code/Common/SceneCombiner.cpp')
-rw-r--r--src/mesh/assimp-master/code/Common/SceneCombiner.cpp1375
1 files changed, 1375 insertions, 0 deletions
diff --git a/src/mesh/assimp-master/code/Common/SceneCombiner.cpp b/src/mesh/assimp-master/code/Common/SceneCombiner.cpp
new file mode 100644
index 0000000..2c2539e
--- /dev/null
+++ b/src/mesh/assimp-master/code/Common/SceneCombiner.cpp
@@ -0,0 +1,1375 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 "ScenePrivate.h"
+#include "time.h"
+#include <assimp/Hash.h>
+#include <assimp/SceneCombiner.h>
+#include <assimp/StringUtils.h>
+#include <assimp/fast_atof.h>
+#include <assimp/mesh.h>
+#include <assimp/metadata.h>
+#include <assimp/scene.h>
+#include <stdio.h>
+#include <assimp/DefaultLogger.hpp>
+
+namespace Assimp {
+
+#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wclass-memaccess"
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// 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) {
+ ASSIMP_LOG_VERBOSE_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(nullptr != 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(nullptr != 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 nullptr 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();
+ new (*_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(nullptr != 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 nullptr 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->mNumTextures];
+ 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.
+ // Copy here because we overwrite the string data in-place and the buffer inside of aiString
+ // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be
+ // MAXLEN in size.
+ 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];
+ const unsigned int oldLen = s.length;
+
+ s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx);
+
+ // The string changed in size so we need to reallocate the buffer for the property.
+ if (oldLen < s.length) {
+ prop->mDataLength += s.length - oldLen;
+ delete[] prop->mData;
+ prop->mData = new char[prop->mDataLength];
+ }
+
+ memcpy(prop->mData, static_cast<void*>(&s), prop->mDataLength);
+ }
+ }
+
+ // 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] : nullptr);
+
+ aiCamera **ppCameras = dest->mCameras = (dest->mNumCameras ? new aiCamera *[dest->mNumCameras] : nullptr);
+
+ aiAnimation **ppAnims = dest->mAnimations = (dest->mNumAnimations ? new aiAnimation *[dest->mNumAnimations] : nullptr);
+
+ 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) {
+ ASSIMP_LOG_ERROR("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 = nullptr;
+ delete[] deleteMe->mCameras;
+ deleteMe->mCameras = nullptr;
+ delete[] deleteMe->mLights;
+ deleteMe->mLights = nullptr;
+ delete[] deleteMe->mMaterials;
+ deleteMe->mMaterials = nullptr;
+ delete[] deleteMe->mAnimations;
+ deleteMe->mAnimations = nullptr;
+ delete[] deleteMe->mTextures;
+ deleteMe->mTextures = nullptr;
+
+ deleteMe->mRootNode = nullptr;
+
+ // 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 boneIt = asBones.begin(), boneEnd = asBones.end(); boneIt != boneEnd; ++boneIt) {
+ // Allocate a bone and setup it's name
+ aiBone *pc = out->mBones[out->mNumBones++] = new aiBone();
+ pc->mName = aiString(*(boneIt->second));
+
+ std::vector<BoneSrcIndex>::const_iterator wend = boneIt->pSrcBones.end();
+
+ // Loop through all bones to be joined for this bone
+ for (std::vector<BoneSrcIndex>::const_iterator wmit = boneIt->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 != boneIt->pSrcBones.begin() && pc->mOffsetMatrix != wmit->first->mOffsetMatrix) {
+ ASSIMP_LOG_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 = (*boneIt).pSrcBones.begin(); wmit != (*boneIt).pSrcBones.end(); ++wmit) {
+ if (wmit == wend) {
+ break;
+ }
+
+ 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 = nullptr; // 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
+ ASSIMP_LOG_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 {
+ ASSIMP_LOG_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 {
+ ASSIMP_LOG_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 {
+ ASSIMP_LOG_WARN("JoinMeshes: UVs expected but input mesh contains no UVs");
+ }
+ pv2 += (*it)->mNumVertices;
+ }
+ ++n;
+ }
+ // copy vertex colors
+ n = 0;
+ while ((**begin).HasVertexColors(n)) {
+ aiColor4D *pVec2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
+ for (std::vector<aiMesh *>::const_iterator it = begin; it != end; ++it) {
+ if ((*it)->mColors[n]) {
+ ::memcpy(pVec2, (*it)->mColors[n], (*it)->mNumVertices * sizeof(aiColor4D));
+ } else {
+ ASSIMP_LOG_WARN("JoinMeshes: VCs expected but input mesh contains no VCs");
+ }
+ pVec2 += (*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 = nullptr;
+ }
+ 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 = nullptr; // 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 = nullptr;
+ 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();
+ }
+ CopyScene(_dest, src, false);
+}
+
+// ------------------------------------------------------------------------------------------------
+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(nullptr != dest);
+
+ // copy metadata
+ if (nullptr != src->mMetaData) {
+ dest->mMetaData = new aiMetadata(*src->mMetaData);
+ }
+
+ // 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 nullptr if the scene is user-allocated (i.e. for use with the export API)
+ if (dest->mPrivate != nullptr) {
+ 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
+ *dest = *src;
+
+ // 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);
+ }
+
+ // make a deep copy of all blend shapes
+ CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
+
+ // make a deep copy of all texture coordinate names
+ if (src->mTextureCoordsNames != nullptr) {
+ dest->mTextureCoordsNames = new aiString *[AI_MAX_NUMBER_OF_TEXTURECOORDS] {};
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ Copy(&dest->mTextureCoordsNames[i], src->mTextureCoordsNames[i]);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiAnimMesh **_dest, const aiAnimMesh *src) {
+ if (nullptr == _dest || nullptr == src) {
+ return;
+ }
+
+ aiAnimMesh *dest = *_dest = new aiAnimMesh();
+
+ // get a flat copy
+ *dest = *src;
+
+ // 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);
+}
+
+// ------------------------------------------------------------------------------------------------
+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
+ *dest = *src;
+
+ // 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 = nullptr;
+ 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
+ *dest = *src;
+
+ // and reallocate all arrays
+ CopyPtrArray(dest->mChannels, src->mChannels, dest->mNumChannels);
+ CopyPtrArray(dest->mMorphMeshChannels, src->mMorphMeshChannels, dest->mNumMorphMeshChannels);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiNodeAnim **_dest, const aiNodeAnim *src) {
+ if (nullptr == _dest || nullptr == src) {
+ return;
+ }
+
+ aiNodeAnim *dest = *_dest = new aiNodeAnim();
+
+ // get a flat copy
+ *dest = *src;
+
+ // and reallocate all arrays
+ GetArrayCopy(dest->mPositionKeys, dest->mNumPositionKeys);
+ GetArrayCopy(dest->mScalingKeys, dest->mNumScalingKeys);
+ GetArrayCopy(dest->mRotationKeys, dest->mNumRotationKeys);
+}
+
+void SceneCombiner::Copy(aiMeshMorphAnim **_dest, const aiMeshMorphAnim *src) {
+ if (nullptr == _dest || nullptr == src) {
+ return;
+ }
+
+ aiMeshMorphAnim *dest = *_dest = new aiMeshMorphAnim();
+
+ // get a flat copy
+ *dest = *src;
+
+ // and reallocate all arrays
+ GetArrayCopy(dest->mKeys, dest->mNumKeys);
+ for (ai_uint i = 0; i < dest->mNumKeys; ++i) {
+ dest->mKeys[i].mValues = new unsigned int[dest->mKeys[i].mNumValuesAndWeights];
+ dest->mKeys[i].mWeights = new double[dest->mKeys[i].mNumValuesAndWeights];
+ ::memcpy(dest->mKeys[i].mValues, src->mKeys[i].mValues, dest->mKeys[i].mNumValuesAndWeights * sizeof(unsigned int));
+ ::memcpy(dest->mKeys[i].mWeights, src->mKeys[i].mWeights, dest->mKeys[i].mNumValuesAndWeights * sizeof(double));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+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
+ *dest = *src;
+}
+
+// ------------------------------------------------------------------------------------------------
+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
+ *dest = *src;
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiBone **_dest, const aiBone *src) {
+ if (nullptr == _dest || nullptr == src) {
+ return;
+ }
+
+ aiBone *dest = *_dest = new aiBone();
+
+ // get a flat copy
+ *dest = *src;
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiNode **_dest, const aiNode *src) {
+ ai_assert(nullptr != _dest);
+ ai_assert(nullptr != src);
+
+ aiNode *dest = *_dest = new aiNode();
+
+ // get a flat copy
+ *dest = *src;
+
+ 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);
+
+ 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);
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiString **_dest, const aiString *src) {
+ if (nullptr == _dest || nullptr == src) {
+ return;
+ }
+
+ aiString *dest = *_dest = new aiString();
+
+ // get a flat copy
+ *dest = *src;
+}
+
+#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0)
+#pragma GCC diagnostic pop
+#endif
+
+} // Namespace Assimp