Merge branch 'master' into master

pull/3521/head
Kim Kulling 2020-12-09 13:59:27 +01:00 committed by GitHub
commit c168c4689e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 201 additions and 139 deletions

View File

@ -16,13 +16,16 @@ A library to import and export various 3d-model-formats including scene-post-pro
<br> <br>
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. 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. 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).
<br>
You find a bug in the docs? Use [Doc-Repo](https://github.com/assimp/assimp-docs).
<br>
Please check our Wiki as well: https://github.com/assimp/assimp/wiki 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 If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb

View File

@ -185,6 +185,17 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
return unique_name; 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<aiNode> mOwnership;
aiNode* mNode;
};
/// todo: pre-build node hierarchy /// todo: pre-build node hierarchy
/// todo: get bone from stack /// todo: get bone from stack
/// todo: make map of aiBone* to aiNode* /// todo: make map of aiBone* to aiNode*
@ -192,13 +203,12 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN
void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) { void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
std::vector<aiNode *> nodes; std::vector<PotentialNode> nodes;
nodes.reserve(conns.size()); nodes.reserve(conns.size());
std::vector<aiNode *> nodes_chain; std::vector<PotentialNode> nodes_chain;
std::vector<aiNode *> post_nodes_chain; std::vector<PotentialNode> post_nodes_chain;
try {
for (const Connection *con : conns) { for (const Connection *con : conns) {
// ignore object-property links // ignore object-property links
if (con->PropertyName().length()) { if (con->PropertyName().length()) {
@ -237,31 +247,31 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
ai_assert(nodes_chain.size()); ai_assert(nodes_chain.size());
if (need_additional_node) { if (need_additional_node) {
nodes_chain.push_back(new aiNode(node_name)); nodes_chain.emplace_back(PotentialNode(node_name));
} }
//setup metadata on newest node //setup metadata on newest node
SetupNodeMetadata(*model, *nodes_chain.back()); SetupNodeMetadata(*model, *nodes_chain.back().mNode);
// link all nodes in a row // link all nodes in a row
aiNode *last_parent = parent; aiNode *last_parent = parent;
for (aiNode *child : nodes_chain) { for (PotentialNode& child : nodes_chain) {
ai_assert(child); ai_assert(child.mNode);
if (last_parent != parent) { if (last_parent != parent) {
last_parent->mNumChildren = 1; last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode *[1]; last_parent->mChildren = new aiNode *[1];
last_parent->mChildren[0] = child; last_parent->mChildren[0] = child.mOwnership.release();
} }
child->mParent = last_parent; child->mParent = last_parent;
last_parent = child; last_parent = child.mNode;
new_abs_transform *= child->mTransformation; new_abs_transform *= child->mTransformation;
} }
// attach geometry // attach geometry
ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform); ConvertModel(*model, nodes_chain.back().mNode, root_node, new_abs_transform);
// check if there will be any child nodes // check if there will be any child nodes
const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model"); const std::vector<const Connection *> &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model");
@ -269,27 +279,23 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
// if so, link the geometric transform inverse nodes // if so, link the geometric transform inverse nodes
// before we attach any child nodes // before we attach any child nodes
if (child_conns.size()) { if (child_conns.size()) {
for (aiNode *postnode : post_nodes_chain) { for (PotentialNode& postnode : post_nodes_chain) {
ai_assert(postnode); ai_assert(postnode.mNode);
if (last_parent != parent) { if (last_parent != parent) {
last_parent->mNumChildren = 1; last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode *[1]; last_parent->mChildren = new aiNode *[1];
last_parent->mChildren[0] = postnode; last_parent->mChildren[0] = postnode.mOwnership.release();
} }
postnode->mParent = last_parent; postnode->mParent = last_parent;
last_parent = postnode; last_parent = postnode.mNode;
new_abs_transform *= postnode->mTransformation; new_abs_transform *= postnode->mTransformation;
} }
} else { } else {
// free the nodes we allocated as we don't need them // free the nodes we allocated as we don't need them
Util::delete_fun<aiNode> deleter; post_nodes_chain.clear();
std::for_each(
post_nodes_chain.begin(),
post_nodes_chain.end(),
deleter);
} }
// recursion call - child nodes // recursion call - child nodes
@ -303,7 +309,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
ConvertCameras(*model, node_name); ConvertCameras(*model, node_name);
} }
nodes.push_back(nodes_chain.front()); nodes.push_back(std::move(nodes_chain.front()));
nodes_chain.clear(); nodes_chain.clear();
} }
} }
@ -312,18 +318,15 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node)
parent->mChildren = new aiNode *[nodes.size()](); parent->mChildren = new aiNode *[nodes.size()]();
parent->mNumChildren = static_cast<unsigned int>(nodes.size()); parent->mNumChildren = static_cast<unsigned int>(nodes.size());
std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren); for (unsigned int i = 0; i < nodes.size(); ++i)
{
parent->mChildren[i] = nodes[i].mOwnership.release();
}
nodes.clear();
} else { } else {
parent->mNumChildren = 0; parent->mNumChildren = 0;
parent->mChildren = nullptr; parent->mChildren = nullptr;
} }
} catch (std::exception &) {
Util::delete_fun<aiNode> 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);
}
} }
void FBXConverter::ConvertLights(const Model &model, const std::string &orig_name) { void FBXConverter::ConvertLights(const Model &model, const std::string &orig_name) {
@ -681,8 +684,8 @@ std::string FBXConverter::NameTransformationChainNode(const std::string &name, T
return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp); return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
} }
bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector<aiNode *> &output_nodes, bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std::string &name, std::vector<PotentialNode> &output_nodes,
std::vector<aiNode *> &post_output_nodes) { std::vector<PotentialNode> &post_output_nodes) {
const PropertyTable &props = model.Props(); const PropertyTable &props = model.Props();
const Model::RotOrder rot = model.RotationOrder(); const Model::RotOrder rot = model.RotationOrder();
@ -828,7 +831,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
chain[i] = chain[i].Inverse(); chain[i] = chain[i].Inverse();
} }
aiNode *nd = new aiNode(); PotentialNode nd;
nd->mName.Set(NameTransformationChainNode(name, comp)); nd->mName.Set(NameTransformationChainNode(name, comp));
nd->mTransformation = chain[i]; nd->mTransformation = chain[i];
@ -836,9 +839,9 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std
if (comp == TransformationComp_GeometricScalingInverse || if (comp == TransformationComp_GeometricScalingInverse ||
comp == TransformationComp_GeometricRotationInverse || comp == TransformationComp_GeometricRotationInverse ||
comp == TransformationComp_GeometricTranslationInverse) { comp == TransformationComp_GeometricTranslationInverse) {
post_output_nodes.push_back(nd); post_output_nodes.emplace_back(std::move(nd));
} else { } 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 // else, we can just multiply the matrices together
aiNode *nd = new aiNode(); PotentialNode nd;
output_nodes.push_back(nd);
// name passed to the method is already unique // name passed to the method is already unique
nd->mName.Set(name); 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++) { for (unsigned int i = TransformationComp_Translation; i < TransformationComp_MAXIMUM; i++) {
nd->mTransformation = nd->mTransformation * chain[i]; nd->mTransformation = nd->mTransformation * chain[i];
} }
output_nodes.push_back(std::move(nd));
return false; return false;
} }

View File

@ -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<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes); struct PotentialNode;
bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<PotentialNode>& output_nodes, std::vector<PotentialNode>& post_output_nodes);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void SetupNodeMetadata(const Model& model, aiNode& nd); void SetupNodeMetadata(const Model& model, aiNode& nd);

View File

@ -49,7 +49,9 @@ using namespace glTFCommon::Util;
namespace Util { namespace Util {
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out) { 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) { if (inLength < 4) {
out = 0; out = 0;

View File

@ -249,7 +249,10 @@ inline char EncodeCharBase64(uint8_t b) {
} }
inline uint8_t DecodeCharBase64(char c) { inline uint8_t DecodeCharBase64(char c) {
return DATA<true>::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<true>::tableDecodeBase64[size_t(c & 0x7F)]; // TODO faster with lookup table or ifs?
} }
size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out); size_t DecodeBase64(const char *in, size_t inLength, uint8_t *&out);

View File

@ -1124,6 +1124,14 @@ private:
IOStream *OpenFile(std::string path, const char *mode, bool absolute = false); 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 } // namespace glTF2
// Include the implementation of the methods // Include the implementation of the methods

View File

@ -273,17 +273,21 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
} }
if (!mDict->IsArray()) { 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]; Value &obj = (*mDict)[i];
if (!obj.IsObject()) { 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()) { 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); 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 } // namespace
template <class T> template <class T>
@ -766,11 +762,12 @@ void Accessor::ExtractData(T *&outData) {
const size_t targetElemSize = sizeof(T); const size_t targetElemSize = sizeof(T);
if (elemSize > targetElemSize) { 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())) { const size_t maxSize = (bufferView ? bufferView->byteLength : sparse->data.size());
throw DeadlyImportError("GLTF: count*stride out of range"); if (count*stride > maxSize) {
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
} }
outData = new T[count]; outData = new T[count];

View File

@ -918,7 +918,10 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (!node.meshes.empty()) { if (!node.meshes.empty()) {
// GLTF files contain at most 1 mesh per node. // 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 mesh_idx = node.meshes[0].GetIndex();
int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx]; int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx];

View File

@ -1033,7 +1033,10 @@ ELSE()
INCLUDE_DIRECTORIES( "../contrib" ) INCLUDE_DIRECTORIES( "../contrib" )
INCLUDE_DIRECTORIES( "../contrib/pugixml/src" ) INCLUDE_DIRECTORIES( "../contrib/pugixml/src" )
ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 ) ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 )
option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON )
if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR)
ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS ) ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS )
endif()
ENDIF() ENDIF()
# VC2010 fixes # VC2010 fixes

View File

@ -408,6 +408,45 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
} }
} }
// ... and copy all the morph targets for all the vertices which made it into the new submesh
if (pMesh->mNumAnimMeshes > 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... // I have the strange feeling that this will break apart at some point in time...
} }
} }