diff --git a/Readme.md b/Readme.md
index 87d5b40d8..c6212bcc0 100644
--- a/Readme.md
+++ b/Readme.md
@@ -16,13 +16,16 @@ A library to import and export various 3d-model-formats including scene-post-pro
APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
-
-[Check the latest doc](https://assimp-docs.readthedocs.io/en/latest/).
-
Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
-This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
+### Latest Doc's ###
+Please check the latest documents at [Asset-Importer-Lib-Doc](https://assimp-docs.readthedocs.io/en/latest/).
+### Get involved ###
+This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
+
+You find a bug in the docs? Use [Doc-Repo](https://github.com/assimp/assimp-docs).
+
Please check our Wiki as well: https://github.com/assimp/assimp/wiki
If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp
index efecf450f..e8fd7967b 100644
--- a/code/AssetLib/FBX/FBXConverter.cpp
+++ b/code/AssetLib/FBX/FBXConverter.cpp
@@ -185,6 +185,17 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
return unique_name;
}
+/// This struct manages nodes which may or may not end up in the node hierarchy.
+/// When a node becomes a child of another node, that node becomes its owner and mOwnership should be released.
+struct FBXConverter::PotentialNode
+{
+ PotentialNode() : mOwnership(new aiNode), mNode(mOwnership.get()) {}
+ PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {}
+ aiNode* operator->() { return mNode; }
+ std::unique_ptr mOwnership;
+ aiNode* mNode;
+};
+
/// todo: pre-build node hierarchy
/// todo: get bone from stack
/// todo: make map of aiBone* to aiNode*
@@ -192,137 +203,129 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
const std::vector &conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
- std::vector nodes;
+ std::vector nodes;
nodes.reserve(conns.size());
- std::vector nodes_chain;
- std::vector post_nodes_chain;
+ std::vector nodes_chain;
+ std::vector post_nodes_chain;
- try {
- for (const Connection *con : conns) {
- // ignore object-property links
- if (con->PropertyName().length()) {
- // really important we document why this is ignored.
- FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
- continue; //?
+ for (const Connection *con : conns) {
+ // ignore object-property links
+ if (con->PropertyName().length()) {
+ // really important we document why this is ignored.
+ FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
+ continue; //?
+ }
+
+ // convert connection source object into Object base class
+ const Object *const object = con->SourceObject();
+ if (nullptr == object) {
+ FBXImporter::LogError("failed to convert source object for Model link");
+ continue;
+ }
+
+ // FBX Model::Cube, Model::Bone001, etc elements
+ // This detects if we can cast the object into this model structure.
+ const Model *const model = dynamic_cast(object);
+
+ if (nullptr != model) {
+ nodes_chain.clear();
+ post_nodes_chain.clear();
+
+ aiMatrix4x4 new_abs_transform = parent->mTransformation;
+ std::string node_name = FixNodeName(model->Name());
+ // even though there is only a single input node, the design of
+ // assimp (or rather: the complicated transformation chain that
+ // is employed by fbx) means that we may need multiple aiNode's
+ // to represent a fbx node's transformation.
+
+ // generate node transforms - this includes pivot data
+ // if need_additional_node is true then you t
+ const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
+
+ // assert that for the current node we must have at least a single transform
+ ai_assert(nodes_chain.size());
+
+ if (need_additional_node) {
+ nodes_chain.emplace_back(PotentialNode(node_name));
}
- // convert connection source object into Object base class
- const Object *const object = con->SourceObject();
- if (nullptr == object) {
- FBXImporter::LogError("failed to convert source object for Model link");
- continue;
- }
+ //setup metadata on newest node
+ SetupNodeMetadata(*model, *nodes_chain.back().mNode);
- // FBX Model::Cube, Model::Bone001, etc elements
- // This detects if we can cast the object into this model structure.
- const Model *const model = dynamic_cast(object);
+ // link all nodes in a row
+ aiNode *last_parent = parent;
+ for (PotentialNode& child : nodes_chain) {
+ ai_assert(child.mNode);
- if (nullptr != model) {
- nodes_chain.clear();
- post_nodes_chain.clear();
-
- aiMatrix4x4 new_abs_transform = parent->mTransformation;
- std::string node_name = FixNodeName(model->Name());
- // even though there is only a single input node, the design of
- // assimp (or rather: the complicated transformation chain that
- // is employed by fbx) means that we may need multiple aiNode's
- // to represent a fbx node's transformation.
-
- // generate node transforms - this includes pivot data
- // if need_additional_node is true then you t
- const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
-
- // assert that for the current node we must have at least a single transform
- ai_assert(nodes_chain.size());
-
- if (need_additional_node) {
- nodes_chain.push_back(new aiNode(node_name));
+ if (last_parent != parent) {
+ last_parent->mNumChildren = 1;
+ last_parent->mChildren = new aiNode *[1];
+ last_parent->mChildren[0] = child.mOwnership.release();
}
- //setup metadata on newest node
- SetupNodeMetadata(*model, *nodes_chain.back());
+ child->mParent = last_parent;
+ last_parent = child.mNode;
- // link all nodes in a row
- aiNode *last_parent = parent;
- for (aiNode *child : nodes_chain) {
- ai_assert(child);
+ new_abs_transform *= child->mTransformation;
+ }
+
+ // attach geometry
+ ConvertModel(*model, nodes_chain.back().mNode, root_node, new_abs_transform);
+
+ // check if there will be any child nodes
+ const std::vector &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
+
+ // if so, link the geometric transform inverse nodes
+ // before we attach any child nodes
+ if (child_conns.size()) {
+ for (PotentialNode& postnode : post_nodes_chain) {
+ ai_assert(postnode.mNode);
if (last_parent != parent) {
last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode *[1];
- last_parent->mChildren[0] = child;
+ last_parent->mChildren[0] = postnode.mOwnership.release();
}
- child->mParent = last_parent;
- last_parent = child;
+ postnode->mParent = last_parent;
+ last_parent = postnode.mNode;
- new_abs_transform *= child->mTransformation;
+ new_abs_transform *= postnode->mTransformation;
}
-
- // attach geometry
- ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform);
-
- // check if there will be any child nodes
- const std::vector &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
-
- // if so, link the geometric transform inverse nodes
- // before we attach any child nodes
- if (child_conns.size()) {
- for (aiNode *postnode : post_nodes_chain) {
- ai_assert(postnode);
-
- if (last_parent != parent) {
- last_parent->mNumChildren = 1;
- last_parent->mChildren = new aiNode *[1];
- last_parent->mChildren[0] = postnode;
- }
-
- postnode->mParent = last_parent;
- last_parent = postnode;
-
- new_abs_transform *= postnode->mTransformation;
- }
- } else {
- // free the nodes we allocated as we don't need them
- Util::delete_fun deleter;
- std::for_each(
- post_nodes_chain.begin(),
- post_nodes_chain.end(),
- deleter);
- }
-
- // recursion call - child nodes
- ConvertNodes(model->ID(), last_parent, root_node);
-
- if (doc.Settings().readLights) {
- ConvertLights(*model, node_name);
- }
-
- if (doc.Settings().readCameras) {
- ConvertCameras(*model, node_name);
- }
-
- nodes.push_back(nodes_chain.front());
- nodes_chain.clear();
+ } else {
+ // free the nodes we allocated as we don't need them
+ post_nodes_chain.clear();
}
+
+ // recursion call - child nodes
+ ConvertNodes(model->ID(), last_parent, root_node);
+
+ if (doc.Settings().readLights) {
+ ConvertLights(*model, node_name);
+ }
+
+ if (doc.Settings().readCameras) {
+ ConvertCameras(*model, node_name);
+ }
+
+ nodes.push_back(std::move(nodes_chain.front()));
+ nodes_chain.clear();
}
+ }
- if (nodes.size()) {
- parent->mChildren = new aiNode *[nodes.size()]();
- parent->mNumChildren = static_cast(nodes.size());
+ if (nodes.size()) {
+ parent->mChildren = new aiNode *[nodes.size()]();
+ parent->mNumChildren = static_cast(nodes.size());
- std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren);
- } else {
- parent->mNumChildren = 0;
- parent->mChildren = nullptr;
+ for (unsigned int i = 0; i < nodes.size(); ++i)
+ {
+ parent->mChildren[i] = nodes[i].mOwnership.release();
}
-
- } catch (std::exception &) {
- Util::delete_fun deleter;
- std::for_each(nodes.begin(), nodes.end(), deleter);
- std::for_each(nodes_chain.begin(), nodes_chain.end(), deleter);
- std::for_each(post_nodes_chain.begin(), post_nodes_chain.end(), deleter);
+ nodes.clear();
+ } else {
+ parent->mNumChildren = 0;
+ parent->mChildren = nullptr;
}
}
@@ -681,8 +684,8 @@ std::string FBXConverter::NameTransformationChainNode(const std::string &name, T
return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
}
-bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector &output_nodes,
- std::vector &post_output_nodes) {
+bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector &output_nodes,
+ std::vector &post_output_nodes) {
const PropertyTable &props = model.Props();
const Model::RotOrder rot = model.RotationOrder();
@@ -828,7 +831,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
chain[i] = chain[i].Inverse();
}
- aiNode *nd = new aiNode();
+ PotentialNode nd;
nd->mName.Set(NameTransformationChainNode(name, comp));
nd->mTransformation = chain[i];
@@ -836,9 +839,9 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
if (comp == TransformationComp_GeometricScalingInverse ||
comp == TransformationComp_GeometricRotationInverse ||
comp == TransformationComp_GeometricTranslationInverse) {
- post_output_nodes.push_back(nd);
+ post_output_nodes.emplace_back(std::move(nd));
} else {
- output_nodes.push_back(nd);
+ output_nodes.emplace_back(std::move(nd));
}
}
@@ -847,8 +850,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
}
// else, we can just multiply the matrices together
- aiNode *nd = new aiNode();
- output_nodes.push_back(nd);
+ PotentialNode nd;
// name passed to the method is already unique
nd->mName.Set(name);
@@ -857,6 +859,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
for (unsigned int i = TransformationComp_Translation; i < TransformationComp_MAXIMUM; i++) {
nd->mTransformation = nd->mTransformation * chain[i];
}
+ output_nodes.push_back(std::move(nd));
return false;
}
diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h
index 0ae0da662..52f978a7b 100644
--- a/code/AssetLib/FBX/FBXConverter.h
+++ b/code/AssetLib/FBX/FBXConverter.h
@@ -171,9 +171,10 @@ private:
// ------------------------------------------------------------------------------------------------
/**
- * note: memory for output_nodes will be managed by the caller
+ * note: memory for output_nodes is managed by the caller, via the PotentialNode struct.
*/
- bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector& output_nodes, std::vector& post_output_nodes);
+ struct PotentialNode;
+ bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector& output_nodes, std::vector& post_output_nodes);
// ------------------------------------------------------------------------------------------------
void SetupNodeMetadata(const Model& model, aiNode& nd);
diff --git a/code/AssetLib/glTF/glTFCommon.cpp b/code/AssetLib/glTF/glTFCommon.cpp
index 6bea18a0a..01ba31209 100644
--- a/code/AssetLib/glTF/glTFCommon.cpp
+++ b/code/AssetLib/glTF/glTFCommon.cpp
@@ -49,7 +49,9 @@ using namespace glTFCommon::Util;
namespace Util {
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out) {
- ai_assert(inLength % 4 == 0);
+ if (inLength % 4 != 0) {
+ throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength);
+ }
if (inLength < 4) {
out = 0;
diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h
index 977fc0da4..6d402b0e3 100644
--- a/code/AssetLib/glTF/glTFCommon.h
+++ b/code/AssetLib/glTF/glTFCommon.h
@@ -249,7 +249,10 @@ inline char EncodeCharBase64(uint8_t b) {
}
inline uint8_t DecodeCharBase64(char c) {
- return DATA::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
+ if (c & 0x80) {
+ throw DeadlyImportError("Invalid base64 char value: ", size_t(c));
+ }
+ return DATA::tableDecodeBase64[size_t(c & 0x7F)]; // TODO faster with lookup table or ifs?
}
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out);
diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h
index 23c19cc58..be149f86e 100644
--- a/code/AssetLib/glTF2/glTF2Asset.h
+++ b/code/AssetLib/glTF2/glTF2Asset.h
@@ -1124,6 +1124,14 @@ private:
IOStream *OpenFile(std::string path, const char *mode, bool absolute = false);
};
+inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) {
+ std::string context = id;
+ if (!name.empty()) {
+ context += " (\"" + name + "\")";
+ }
+ return context;
+}
+
} // namespace glTF2
// Include the implementation of the methods
diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl
index c892e6697..8fc8bf24e 100644
--- a/code/AssetLib/glTF2/glTF2Asset.inl
+++ b/code/AssetLib/glTF2/glTF2Asset.inl
@@ -273,17 +273,21 @@ Ref LazyDict::Retrieve(unsigned int i) {
}
if (!mDict->IsArray()) {
- throw DeadlyImportError("GLTF: Field is not an array \"", mDictId, "\"");
+ throw DeadlyImportError("GLTF: Field \"", mDictId, "\" is not an array");
+ }
+
+ if (i >= mDict->Size()) {
+ throw DeadlyImportError("GLTF: Array index ", i, " is out of bounds (", mDict->Size(), ") for \"", mDictId, "\"");
}
Value &obj = (*mDict)[i];
if (!obj.IsObject()) {
- throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" is not a JSON object");
+ throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" is not a JSON object");
}
if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) {
- throw DeadlyImportError("GLTF: Object at index \"", to_string(i), "\" has recursive reference to itself");
+ throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" has recursive reference to itself");
}
mRecursiveReferenceCheck.insert(i);
@@ -741,14 +745,6 @@ inline void CopyData(size_t count,
}
}
-inline std::string getContextForErrorMessages(const std::string& id, const std::string& name) {
- std::string context = id;
- if (!name.empty()) {
- context += " (\"" + name + "\")";
- }
- return context;
-}
-
} // namespace
template
@@ -766,11 +762,12 @@ void Accessor::ExtractData(T *&outData) {
const size_t targetElemSize = sizeof(T);
if (elemSize > targetElemSize) {
- throw DeadlyImportError("GLTF: elemSize > targetElemSize");
+ throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
}
- if (count*stride > (bufferView ? bufferView->byteLength : sparse->data.size())) {
- throw DeadlyImportError("GLTF: count*stride out of range");
+ const size_t maxSize = (bufferView ? bufferView->byteLength : sparse->data.size());
+ if (count*stride > maxSize) {
+ throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
}
outData = new T[count];
diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp
index 3fb7889af..672bac52d 100644
--- a/code/AssetLib/glTF2/glTF2Importer.cpp
+++ b/code/AssetLib/glTF2/glTF2Importer.cpp
@@ -918,7 +918,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &
if (!node.meshes.empty()) {
// GLTF files contain at most 1 mesh per node.
- assert(node.meshes.size() == 1);
+ if (node.meshes.size() > 1)
+ {
+ throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(), " meshes in ", getContextForErrorMessages(node.id, node.name), ", but only 1 mesh per node allowed.");
+ }
int mesh_idx = node.meshes[0].GetIndex();
int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx];
diff --git a/code/Common/ScenePreprocessor.cpp b/code/Common/ScenePreprocessor.cpp
index 55aa04ad2..2b73d9452 100644
--- a/code/Common/ScenePreprocessor.cpp
+++ b/code/Common/ScenePreprocessor.cpp
@@ -96,8 +96,9 @@ void ScenePreprocessor::ProcessMesh(aiMesh *mesh) {
if (!mesh->mTextureCoords[i]) {
mesh->mNumUVComponents[i] = 0;
} else {
- if (!mesh->mNumUVComponents[i])
+ if (!mesh->mNumUVComponents[i]) {
mesh->mNumUVComponents[i] = 2;
+ }
aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices;
@@ -105,16 +106,19 @@ void ScenePreprocessor::ProcessMesh(aiMesh *mesh) {
// as if they were 2D channels .. just in case an application doesn't handle
// this case
if (2 == mesh->mNumUVComponents[i]) {
- for (; p != end; ++p)
+ for (; p != end; ++p) {
p->z = 0.f;
+ }
} else if (1 == mesh->mNumUVComponents[i]) {
- for (; p != end; ++p)
+ for (; p != end; ++p) {
p->z = p->y = 0.f;
+ }
} else if (3 == mesh->mNumUVComponents[i]) {
// Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
for (; p != end; ++p) {
- if (p->z != 0)
+ if (p->z != 0) {
break;
+ }
}
if (p == end) {
ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
@@ -151,7 +155,6 @@ void ScenePreprocessor::ProcessMesh(aiMesh *mesh) {
// If tangents and normals are given but no bitangents compute them
if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) {
-
mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i];
@@ -165,11 +168,9 @@ void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) {
for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
aiNodeAnim *channel = anim->mChannels[i];
- /* If the exact duration of the animation is not given
- * compute it now.
- */
+ // If the exact duration of the animation is not given
+ // compute it now.
if (anim->mDuration == -1.) {
-
// Position keys
for (unsigned int j = 0; j < channel->mNumPositionKeys; ++j) {
aiVectorKey &key = channel->mPositionKeys[j];
@@ -192,11 +193,10 @@ void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) {
}
}
- /* Check whether the animation channel has no rotation
- * or position tracks. In this case we generate a dummy
- * track from the information we have in the transformation
- * matrix of the corresponding node.
- */
+ // Check whether the animation channel has no rotation
+ // or position tracks. In this case we generate a dummy
+ // track from the information we have in the transformation
+ // matrix of the corresponding node.
if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) {
// Find the node that belongs to this animation
aiNode *node = scene->mRootNode->FindNode(channel->mNodeName);
@@ -210,6 +210,10 @@ void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) {
// No rotation keys? Generate a dummy track
if (!channel->mNumRotationKeys) {
+ if (channel->mRotationKeys) {
+ delete[] channel->mRotationKeys;
+ channel->mRotationKeys = nullptr;
+ }
ai_assert(!channel->mRotationKeys);
channel->mNumRotationKeys = 1;
channel->mRotationKeys = new aiQuatKey[1];
@@ -225,6 +229,10 @@ void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) {
// No scaling keys? Generate a dummy track
if (!channel->mNumScalingKeys) {
+ if (channel->mScalingKeys) {
+ delete[] channel->mScalingKeys;
+ channel->mScalingKeys = nullptr;
+ }
ai_assert(!channel->mScalingKeys);
channel->mNumScalingKeys = 1;
channel->mScalingKeys = new aiVectorKey[1];
@@ -240,6 +248,10 @@ void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) {
// No position keys? Generate a dummy track
if (!channel->mNumPositionKeys) {
+ if (channel->mPositionKeys) {
+ delete[] channel->mPositionKeys;
+ channel->mPositionKeys = nullptr;
+ }
ai_assert(!channel->mPositionKeys);
channel->mNumPositionKeys = 1;
channel->mPositionKeys = new aiVectorKey[1];
diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp
index 1fd26c757..b39444859 100644
--- a/code/PostProcessing/SplitByBoneCountProcess.cpp
+++ b/code/PostProcessing/SplitByBoneCountProcess.cpp
@@ -408,6 +408,45 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumAnimMeshes > 0) {
+ newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes;
+ newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes];
+
+ for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) {
+ aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx];
+ aiAnimMesh* newTarget = new aiAnimMesh;
+ newTarget->mName = origTarget->mName;
+ newTarget->mWeight = origTarget->mWeight;
+ newTarget->mNumVertices = numSubMeshVertices;
+ newTarget->mVertices = new aiVector3D[numSubMeshVertices];
+ newMesh->mAnimMeshes[morphIdx] = newTarget;
+
+ if (origTarget->HasNormals()) {
+ newTarget->mNormals = new aiVector3D[numSubMeshVertices];
+ }
+
+ if (origTarget->HasTangentsAndBitangents()) {
+ newTarget->mTangents = new aiVector3D[numSubMeshVertices];
+ newTarget->mBitangents = new aiVector3D[numSubMeshVertices];
+ }
+
+ for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) {
+ // find the source vertex for it in the source mesh
+ unsigned int previousIndex = previousVertexIndices[vi];
+ newTarget->mVertices[vi] = origTarget->mVertices[previousIndex];
+
+ if (newTarget->HasNormals()) {
+ newTarget->mNormals[vi] = origTarget->mNormals[previousIndex];
+ }
+ if (newTarget->HasTangentsAndBitangents()) {
+ newTarget->mTangents[vi] = origTarget->mTangents[previousIndex];
+ newTarget->mBitangents[vi] = origTarget->mBitangents[previousIndex];
+ }
+ }
+ }
+ }
+
// I have the strange feeling that this will break apart at some point in time...
}
}
diff --git a/port/AndroidJNI/README.md b/port/AndroidJNI/README.md
index 0b95efd04..003b1da1a 100644
--- a/port/AndroidJNI/README.md
+++ b/port/AndroidJNI/README.md
@@ -14,6 +14,11 @@ To use this module please provide following cmake defines:
"SOME_PATH" is a path to your cmake android toolchain script.
+
+The build script for this port is based on [android-cmake](https://github.com/taka-no-me/android-cmake).
+See its documentation for more Android-specific cmake options (e.g. -DANDROID_ABI for the target ABI).
+Check [Asset-Importer-Docs](https://assimp-docs.readthedocs.io/en/latest/) for more information.
+
### Code ###
A small example how to wrap assimp for Android:
```cpp