From 5f58ef82b93c7f6fd9c0c08c3f8d739db0a29047 Mon Sep 17 00:00:00 2001 From: Rem Date: Fri, 25 Oct 2019 10:18:27 +0300 Subject: [PATCH 01/14] prevent accidental lower casing material names in ReplaceDefaultMaterial --- code/3DS/3DSConverter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/3DS/3DSConverter.cpp b/code/3DS/3DSConverter.cpp index 2176b75fc..3c3da36a3 100644 --- a/code/3DS/3DSConverter.cpp +++ b/code/3DS/3DSConverter.cpp @@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() unsigned int idx( NotSet ); for (unsigned int i = 0; i < mScene->mMaterials.size();++i) { - std::string &s = mScene->mMaterials[i].mName; + std::string s = mScene->mMaterials[i].mName; for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) { *it = static_cast< char >( ::tolower( *it ) ); } From 168ae22ad4dc3f079b4efff7d77be02b1f1b99c5 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sat, 26 Oct 2019 16:28:51 +0100 Subject: [PATCH 02/14] Implemented easy armature lookup This lets you directly retrieve the node a bone links to and informs you of the armature directly This also fixes a bug with bone name being made unique which causes them to become not 1:1 what the modeller has imported. --- code/FBX/FBXConverter.cpp | 458 +++++++++++++++++++++++++++++--------- code/FBX/FBXConverter.h | 69 ++++-- code/FBX/FBXImporter.cpp | 194 ++++++++-------- include/assimp/mesh.h | 19 +- 4 files changed, 515 insertions(+), 225 deletions(-) diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index b8601a2a5..f8225f2e5 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -68,7 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - +#include +#include namespace Assimp { namespace FBX { @@ -120,6 +121,46 @@ namespace Assimp { ConvertGlobalSettings(); TransferDataToScene(); + // Now convert all bone positions to the correct mOffsetMatrix + std::vector bones; + std::vector nodes; + std::map bone_stack; + BuildBoneList(out->mRootNode, out->mRootNode, out, bones); + BuildNodeList(out->mRootNode, nodes ); + + + BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); + + std::cout << "Bone stack size: " << bone_stack.size() << std::endl; + + for( std::pair kvp : bone_stack ) + { + aiBone *bone = kvp.first; + aiNode *bone_node = kvp.second; + std::cout << "active node lookup: " << bone->mName.C_Str() << std::endl; + // lcl transform grab - done in generate_nodes :) + + //bone->mOffsetMatrix = bone_node->mTransformation; + aiNode * armature = GetArmatureRoot(bone_node, bones); + + ai_assert(armature); + + // set up bone armature id + bone->mArmature = armature; + + // set this bone node to be referenced properly + ai_assert(bone_node); + bone->mNode = bone_node; + + // apply full hierarchy to transform for basic offset + while( bone_node->mParent ) + { + bone->mRestMatrix = bone_node->mTransformation * bone->mRestMatrix; + bone_node = bone_node->mParent; + } + } + + // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE // to make sure the scene passes assimp's validation. FBX files // need not contain geometry (i.e. camera animations, raw armatures). @@ -138,6 +179,167 @@ namespace Assimp { std::for_each(textures.begin(), textures.end(), Util::delete_fun()); } + /* Returns the armature root node */ + /* This is required to be detected for a bone initially, it will recurse up until it cannot find another + * bone and return the node + * No known failure points. (yet) + */ + aiNode * FBXConverter::GetArmatureRoot(aiNode *bone_node, std::vector &bone_list) + { + while(bone_node) + { + if(!IsBoneNode(bone_node->mName, bone_list)) + { + std::cout << "Found valid armature: " << bone_node->mName.C_Str() << std::endl; + return bone_node; + } + + bone_node = bone_node->mParent; + } + + std::cout << "can't find armature! node: " << bone_node << std::endl; + + return NULL; + } + + /* Simple IsBoneNode check if this could be a bone */ + bool FBXConverter::IsBoneNode(const aiString &bone_name, std::vector& bones ) + { + for( aiBone *bone : bones) + { + if(bone->mName == bone_name) + { + return true; + } + } + + return false; + } + + + /* Pop this node by name from the stack if found */ + /* Used in multiple armature situations with duplicate node / bone names */ + /* Known flaw: cannot have nodes with bone names, will be fixed in later release */ + /* (serious to be fixed) Known flaw: nodes which have more than one bone could be prematurely dropped from stack */ + aiNode* FBXConverter::GetNodeFromStack(const aiString &node_name, std::vector &nodes) + { + std::vector::iterator iter; + aiNode *found = NULL; + for( iter = nodes.begin(); iter < nodes.end(); ++iter ) + { + aiNode *element = *iter; + ai_assert(element); + // node valid and node name matches + if(element->mName == node_name) + { + found = element; + break; + } + } + + if(found != NULL) { + // now pop the element from the node list + nodes.erase(iter); + + return found; + } + return NULL; + } + + /* Prepare flat node list which can be used for non recursive lookups later */ + void FBXConverter::BuildNodeList(aiNode *current_node, std::vector &nodes) + { + assert(current_node); + + for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) + { + aiNode *child = current_node->mChildren[nodeId]; + assert(child); + + nodes.push_back(child); + + BuildNodeList(child, nodes); + } + } + + /* Reprocess all nodes to calculate bone transforms properly based on the REAL mOffsetMatrix not the local. */ + /* Before this would use mesh transforms which is wrong for bone transforms */ + /* Before this would work for simple character skeletons but not complex meshes with multiple origins */ + /* Source: sketch fab log cutter fbx */ + void FBXConverter::BuildBoneList(aiNode *current_node, const aiNode * root_node, const aiScene *scene, std::vector &bones ) + { + assert(scene); + for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) + { + aiNode *child = current_node->mChildren[nodeId]; + assert(child); + + // check for bones + for( unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) + { + assert(child->mMeshes); + unsigned int mesh_index = child->mMeshes[meshId]; + aiMesh *mesh = scene->mMeshes[ mesh_index ]; + assert(mesh); + + for( unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) + { + aiBone *bone = mesh->mBones[boneId]; + ai_assert(bone); + + // duplicate meshes exist with the same bones sometimes :) + // so this must be detected + if( std::find(bones.begin(), bones.end(), bone) == bones.end() ) + { + // add the element once + bones.push_back(bone); + } + } + + // find mesh and get bones + // then do recursive lookup for bones in root node hierarchy + } + + BuildBoneList(child, root_node, scene, bones); + } + } + + /* A bone stack allows us to have multiple armatures, with the same bone names + * A bone stack allows us also to retrieve bones true transform even with duplicate names :) + */ + void FBXConverter::BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack ) + { + ai_assert(scene); + ai_assert(root_node); + ai_assert(!node_stack.empty()); + + for( aiBone * bone : bones) + { + ai_assert(bone); + aiNode* node = GetNodeFromStack(bone->mName, node_stack); + if(node == NULL) + { + node_stack.clear(); + BuildNodeList(out->mRootNode, node_stack ); + std::cout << "Resetting bone stack: null element " << bone->mName.C_Str() << std::endl; + + node = GetNodeFromStack(bone->mName, node_stack); + + if(!node) { + std::cout << "serious import issue armature failed to be detected?" << std::endl; + continue; + } + } + + std::cout << "Successfully added bone to stack and have valid armature: " << bone->mName.C_Str() << std::endl; + + bone_stack.insert(std::pair(bone, node)); + } + } + void FBXConverter::ConvertRootNode() { out->mRootNode = new aiNode(); std::string unique_name; @@ -145,7 +347,7 @@ namespace Assimp { out->mRootNode->mName.Set(unique_name); // root has ID 0 - ConvertNodes(0L, *out->mRootNode); + ConvertNodes(0L, out->mRootNode, out->mRootNode); } static std::string getAncestorBaseName(const aiNode* node) @@ -179,8 +381,11 @@ namespace Assimp { GetUniqueName(original_name, unique_name); return unique_name; } - - void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { + /// todo: pre-build node hierarchy + /// todo: get bone from stack + /// todo: make map of aiBone* to aiNode* + /// then update convert clusters to the new format + void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); std::vector nodes; @@ -191,62 +396,69 @@ namespace Assimp { try { for (const Connection* con : conns) { - // ignore object-property links if (con->PropertyName().length()) { - continue; + // 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::LogWarn("failed to convert source object for Model link"); + 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_transform; - - std::string unique_name = MakeUniqueNodeName(model, parent); - + 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. - const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain); + + // 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(unique_name)); + nodes_chain.push_back(new aiNode(node_name)); } //setup metadata on newest node SetupNodeMetadata(*model, *nodes_chain.back()); // link all nodes in a row - aiNode* last_parent = &parent; - for (aiNode* prenode : nodes_chain) { - ai_assert(prenode); + aiNode* last_parent = parent; + for (aiNode* child : nodes_chain) { + ai_assert(child); - if (last_parent != &parent) { + if (last_parent != parent) { last_parent->mNumChildren = 1; last_parent->mChildren = new aiNode*[1]; - last_parent->mChildren[0] = prenode; + last_parent->mChildren[0] = child; } - prenode->mParent = last_parent; - last_parent = prenode; + child->mParent = last_parent; + last_parent = child; - new_abs_transform *= prenode->mTransformation; + new_abs_transform *= child->mTransformation; } // attach geometry - ConvertModel(*model, *nodes_chain.back(), new_abs_transform); + ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform); // check if there will be any child nodes const std::vector& child_conns @@ -258,7 +470,7 @@ namespace Assimp { for (aiNode* postnode : post_nodes_chain) { ai_assert(postnode); - if (last_parent != &parent) { + if (last_parent != parent) { last_parent->mNumChildren = 1; last_parent->mChildren = new aiNode*[1]; last_parent->mChildren[0] = postnode; @@ -280,15 +492,15 @@ namespace Assimp { ); } - // attach sub-nodes (if any) - ConvertNodes(model->ID(), *last_parent, new_abs_transform); + // recursion call - child nodes + ConvertNodes(model->ID(), last_parent, root_node); if (doc.Settings().readLights) { - ConvertLights(*model, unique_name); + ConvertLights(*model, node_name); } if (doc.Settings().readCameras) { - ConvertCameras(*model, unique_name); + ConvertCameras(*model, node_name); } nodes.push_back(nodes_chain.front()); @@ -297,10 +509,10 @@ namespace Assimp { } if (nodes.size()) { - parent.mChildren = new aiNode*[nodes.size()](); - parent.mNumChildren = static_cast(nodes.size()); + parent->mChildren = new aiNode*[nodes.size()](); + parent->mNumChildren = static_cast(nodes.size()); - std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); + std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren); } } catch (std::exception&) { @@ -803,7 +1015,7 @@ namespace Assimp { // is_complex needs to be consistent with NeedsComplexTransformationChain() // or the interplay between this code and the animation converter would // not be guaranteed. - ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); + //ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); // now, if we have more than just Translation, Scaling and Rotation, // we need to generate a full node chain to accommodate for assimp's @@ -905,7 +1117,8 @@ namespace Assimp { } } - void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) + void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { const std::vector& geos = model.GetGeometry(); @@ -917,11 +1130,12 @@ namespace Assimp { const MeshGeometry* const mesh = dynamic_cast(geo); const LineGeometry* const line = dynamic_cast(geo); if (mesh) { - const std::vector& indices = ConvertMesh(*mesh, model, node_global_transform, nd); + const std::vector& indices = ConvertMesh(*mesh, model, parent, root_node, + absolute_transform); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); } else if (line) { - const std::vector& indices = ConvertLine(*line, model, node_global_transform, nd); + const std::vector& indices = ConvertLine(*line, model, parent, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); } else { @@ -930,15 +1144,16 @@ namespace Assimp { } if (meshes.size()) { - nd.mMeshes = new unsigned int[meshes.size()](); - nd.mNumMeshes = static_cast(meshes.size()); + parent->mMeshes = new unsigned int[meshes.size()](); + parent->mNumMeshes = static_cast(meshes.size()); - std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); + std::swap_ranges(meshes.begin(), meshes.end(), parent->mMeshes); } } - std::vector FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + std::vector + FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { std::vector temp; @@ -962,18 +1177,18 @@ namespace Assimp { const MatIndexArray::value_type base = mindices[0]; for (MatIndexArray::value_type index : mindices) { if (index != base) { - return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); + return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform); } } } // faster code-path, just copy the data - temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); + temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node)); return temp; } std::vector FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + aiNode *parent, aiNode *root_node) { std::vector temp; @@ -984,7 +1199,7 @@ namespace Assimp { return temp; } - aiMesh* const out_mesh = SetupEmptyMesh(line, nd); + aiMesh* const out_mesh = SetupEmptyMesh(line, root_node); out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; // copy vertices @@ -1019,7 +1234,7 @@ namespace Assimp { return temp; } - aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) + aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode *parent) { aiMesh* const out_mesh = new aiMesh(); meshes.push_back(out_mesh); @@ -1036,17 +1251,18 @@ namespace Assimp { } else { - out_mesh->mName = nd.mName; + out_mesh->mName = parent->mName; } return out_mesh; } - unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, + const aiMatrix4x4 &absolute_transform, aiNode *parent, + aiNode *root_node) { const MatIndexArray& mindices = mesh.GetMaterialIndices(); - aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent); const std::vector& vertices = mesh.GetVertices(); const std::vector& faces = mesh.GetFaceIndexCounts(); @@ -1164,7 +1380,8 @@ namespace Assimp { } if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { - ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); + ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION, + nullptr); } std::vector animMeshes; @@ -1209,8 +1426,10 @@ namespace Assimp { return static_cast(meshes.size() - 1); } - std::vector FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + std::vector + FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, + aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { const MatIndexArray& mindices = mesh.GetMaterialIndices(); ai_assert(mindices.size()); @@ -1221,7 +1440,7 @@ namespace Assimp { for (MatIndexArray::value_type index : mindices) { if (had.find(index) == had.end()) { - indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); + indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform)); had.insert(index); } } @@ -1229,12 +1448,12 @@ namespace Assimp { return indices; } - unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform, - aiNode& nd) + unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, + MatIndexArray::value_type index, + aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { - aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent); const MatIndexArray& mindices = mesh.GetMaterialIndices(); const std::vector& vertices = mesh.GetVertices(); @@ -1399,7 +1618,7 @@ namespace Assimp { ConvertMaterialForMesh(out_mesh, model, mesh, index); if (process_weights) { - ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); + ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, index, &reverseMapping); } std::vector animMeshes; @@ -1449,10 +1668,10 @@ namespace Assimp { return static_cast(meshes.size() - 1); } - void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, - const aiMatrix4x4& node_global_transform, - unsigned int materialIndex, - std::vector* outputVertStartIndices) + void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, + const aiMatrix4x4 &absolute_transform, + aiNode *parent, aiNode *root_node, unsigned int materialIndex, + std::vector *outputVertStartIndices) { ai_assert(geo.DeformerSkin()); @@ -1463,13 +1682,12 @@ namespace Assimp { const Skin& sk = *geo.DeformerSkin(); std::vector bones; - bones.reserve(sk.Clusters().size()); const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; ai_assert(no_mat_check || outputVertStartIndices); try { - + // iterate over the sub deformers for (const Cluster* cluster : sk.Clusters()) { ai_assert(cluster); @@ -1483,6 +1701,7 @@ namespace Assimp { index_out_indices.clear(); out_indices.clear(); + // now check if *any* of these weights is contained in the output mesh, // taking notes so we don't need to do it twice. for (WeightIndexArray::value_type index : indices) { @@ -1520,68 +1739,107 @@ namespace Assimp { } } } - + // if we found at least one, generate the output bones // XXX this could be heavily simplified by collecting the bone // data in a single step. - ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, - count_out_indices, node_global_transform); + ConvertCluster(bones, cluster, out_indices, index_out_indices, + count_out_indices, absolute_transform, parent, root_node); } + + bone_map.clear(); } - catch (std::exception&) { + catch (std::exception&e) { std::for_each(bones.begin(), bones.end(), Util::delete_fun()); throw; } if (bones.empty()) { + out->mBones = nullptr; + out->mNumBones = 0; return; + } else { + out->mBones = new aiBone *[bones.size()](); + out->mNumBones = static_cast(bones.size()); + + std::swap_ranges(bones.begin(), bones.end(), out->mBones); } - - out->mBones = new aiBone*[bones.size()](); - out->mNumBones = static_cast(bones.size()); - - std::swap_ranges(bones.begin(), bones.end(), out->mBones); } - void FBXConverter::ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, - std::vector& out_indices, - std::vector& index_out_indices, - std::vector& count_out_indices, - const aiMatrix4x4& node_global_transform) + const aiNode* FBXConverter::GetNodeByName( const aiString& name, aiNode *current_node ) { + aiNode * iter = current_node; + //printf("Child count: %d", iter->mNumChildren); + return iter; + } - aiBone* const bone = new aiBone(); - bones.push_back(bone); + void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const Cluster *cl, + std::vector &out_indices, std::vector &index_out_indices, + std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, + aiNode *parent, aiNode *root_node) { + assert(cl); // make sure cluster valid + std::string deformer_name = cl->TargetNode()->Name(); + aiString bone_name = aiString(FixNodeName(deformer_name)); - bone->mName = FixNodeName(cl.TargetNode()->Name()); + aiBone *bone = NULL; - bone->mOffsetMatrix = cl.TransformLink(); - bone->mOffsetMatrix.Inverse(); + if (bone_map.count(deformer_name)) { + std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name + << std::endl; + bone = bone_map[deformer_name]; + } else { + std::cout << "created new bone " << bone_name.C_Str() << ". Deformer: " << deformer_name << std::endl; + bone = new aiBone(); + bone->mName = bone_name; - bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; + // store local transform link for post processing + bone->mOffsetMatrix = cl->TransformLink(); + bone->mOffsetMatrix.Inverse(); - bone->mNumWeights = static_cast(out_indices.size()); - aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; + aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform; - const size_t no_index_sentinel = std::numeric_limits::max(); - const WeightArray& weights = cl.GetWeights(); + bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset - const size_t c = index_out_indices.size(); - for (size_t i = 0; i < c; ++i) { - const size_t index_index = index_out_indices[i]; - if (index_index == no_index_sentinel) { - continue; + // + // Now calculate the aiVertexWeights + // + + aiVertexWeight *cursor = nullptr; + + bone->mNumWeights = static_cast(out_indices.size()); + cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; + + const size_t no_index_sentinel = std::numeric_limits::max(); + const WeightArray& weights = cl->GetWeights(); + + const size_t c = index_out_indices.size(); + for (size_t i = 0; i < c; ++i) { + const size_t index_index = index_out_indices[i]; + + if (index_index == no_index_sentinel) { + continue; + } + + const size_t cc = count_out_indices[i]; + for (size_t j = 0; j < cc; ++j) { + // cursor runs from first element relative to the start + // or relative to the start of the next indexes. + aiVertexWeight& out_weight = *cursor++; + + out_weight.mVertexId = static_cast(out_indices[index_index + j]); + out_weight.mWeight = weights[i]; + } } - const size_t cc = count_out_indices[i]; - for (size_t j = 0; j < cc; ++j) { - aiVertexWeight& out_weight = *cursor++; - - out_weight.mVertexId = static_cast(out_indices[index_index + j]); - out_weight.mWeight = weights[i]; - } + bone_map.insert(std::pair(deformer_name, bone)); } + + std::cout << "bone research: Indicies size: " << out_indices.size() << std::endl; + + // lookup must be populated in case something goes wrong + // this also allocates bones to mesh instance outside + local_mesh_bones.push_back(bone); } void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, diff --git a/code/FBX/FBXConverter.h b/code/FBX/FBXConverter.h index 77ced1950..619da92c1 100644 --- a/code/FBX/FBXConverter.h +++ b/code/FBX/FBXConverter.h @@ -123,7 +123,7 @@ private: // ------------------------------------------------------------------------------------------------ // collect and assign child nodes - void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); + void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ void ConvertLights(const Model& model, const std::string &orig_name ); @@ -179,32 +179,35 @@ private: void SetupNodeMetadata(const Model& model, aiNode& nd); // ------------------------------------------------------------------------------------------------ - void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); + void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed - std::vector ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + std::vector + ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ std::vector ConvertLine(const LineGeometry& line, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ - aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); + aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent); // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, + const aiMatrix4x4 &absolute_transform, aiNode *parent, + aiNode *root_node); // ------------------------------------------------------------------------------------------------ - std::vector ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + std::vector + ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform, aiNode& nd); + unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index, + aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits::max() */ @@ -217,17 +220,17 @@ private: * - outputVertStartIndices is only used when a material index is specified, it gives for * each output vertex the DOM index it maps to. */ - void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, - const aiMatrix4x4& node_global_transform = aiMatrix4x4(), - unsigned int materialIndex = NO_MATERIAL_SEPARATION, - std::vector* outputVertStartIndices = NULL); - + void ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, + aiNode *parent = NULL, aiNode *root_node = NULL, + unsigned int materialIndex = NO_MATERIAL_SEPARATION, + std::vector *outputVertStartIndices = NULL); + // lookup + static const aiNode* GetNodeByName( const aiString& name, aiNode *current_node ); // ------------------------------------------------------------------------------------------------ - void ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, - std::vector& out_indices, - std::vector& index_out_indices, - std::vector& count_out_indices, - const aiMatrix4x4& node_global_transform); + void ConvertCluster(std::vector &local_mesh_bones, const Cluster *cl, + std::vector &out_indices, std::vector &index_out_indices, + std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, + aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, @@ -452,10 +455,30 @@ private: using NodeNameCache = std::unordered_map; NodeNameCache mNodeNames; + // Deformer name is not the same as a bone name - it does contain the bone name though :) + // Deformer names in FBX are always unique in an FBX file. + std::map bone_map; + double anim_fps; aiScene* const out; const FBX::Document& doc; + + static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene, + std::vector& bones); + + void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack ); + + static void BuildNodeList(aiNode *current_node, std::vector &nodes); + + static aiNode *GetNodeFromStack(const aiString &node_name, std::vector &nodes); + + static aiNode *GetArmatureRoot(aiNode *bone_node, std::vector &bone_list); + + static bool IsBoneNode(const aiString &bone_name, std::vector &bones); }; } diff --git a/code/FBX/FBXImporter.cpp b/code/FBX/FBXImporter.cpp index c8c1a6853..afcc1ddc7 100644 --- a/code/FBX/FBXImporter.cpp +++ b/code/FBX/FBXImporter.cpp @@ -48,26 +48,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXImporter.h" -#include "FBXTokenizer.h" -#include "FBXParser.h" -#include "FBXUtil.h" -#include "FBXDocument.h" #include "FBXConverter.h" +#include "FBXDocument.h" +#include "FBXParser.h" +#include "FBXTokenizer.h" +#include "FBXUtil.h" -#include #include -#include +#include #include +#include namespace Assimp { -template<> -const char* LogFunctions::Prefix() { - static auto prefix = "FBX: "; - return prefix; +template <> +const char *LogFunctions::Prefix() { + static auto prefix = "FBX: "; + return prefix; } -} +} // namespace Assimp using namespace Assimp; using namespace Assimp::Formatter; @@ -76,131 +76,123 @@ using namespace Assimp::FBX; namespace { static const aiImporterDesc desc = { - "Autodesk FBX Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "fbx" + "Autodesk FBX Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "fbx" }; } // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by #Importer -FBXImporter::FBXImporter() -{ +FBXImporter::FBXImporter() { } // ------------------------------------------------------------------------------------------------ // Destructor, private as well -FBXImporter::~FBXImporter() -{ +FBXImporter::~FBXImporter() { } // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - const std::string& extension = GetExtension(pFile); - if (extension == std::string( desc.mFileExtensions ) ) { - return true; - } +bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { + const std::string &extension = GetExtension(pFile); + if (extension == std::string(desc.mFileExtensions)) { + return true; + } - else if ((!extension.length() || checkSig) && pIOHandler) { - // at least ASCII-FBX files usually have a 'FBX' somewhere in their head - const char* tokens[] = {"fbx"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; + else if ((!extension.length() || checkSig) && pIOHandler) { + // at least ASCII-FBX files usually have a 'FBX' somewhere in their head + const char *tokens[] = { "fbx" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + } + return false; } // ------------------------------------------------------------------------------------------------ // List all extensions handled by this loader -const aiImporterDesc* FBXImporter::GetInfo () const -{ - return &desc; +const aiImporterDesc *FBXImporter::GetInfo() const { + return &desc; } // ------------------------------------------------------------------------------------------------ // Setup configuration properties for the loader -void FBXImporter::SetupProperties(const Importer* pImp) -{ - settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); - settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); - settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); - settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); - settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); - settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); - settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); - settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); - settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); - settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); - settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); - settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); - settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); +void FBXImporter::SetupProperties(const Importer *pImp) { + settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); + settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); + settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); + settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); + settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); + settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); + settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); + settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); + settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); + settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); + settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); + settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); + settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ - std::unique_ptr stream(pIOHandler->Open(pFile,"rb")); - if (!stream) { - ThrowException("Could not open file for reading"); - } +void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + std::unique_ptr stream(pIOHandler->Open(pFile, "rb")); + if (!stream) { + ThrowException("Could not open file for reading"); + } - // read entire file into memory - no streaming for this, fbx - // files can grow large, but the assimp output data structure - // then becomes very large, too. Assimp doesn't support - // streaming for its output data structures so the net win with - // streaming input data would be very low. - std::vector contents; - contents.resize(stream->FileSize()+1); - stream->Read( &*contents.begin(), 1, contents.size()-1 ); - contents[ contents.size() - 1 ] = 0; - const char* const begin = &*contents.begin(); + // read entire file into memory - no streaming for this, fbx + // files can grow large, but the assimp output data structure + // then becomes very large, too. Assimp doesn't support + // streaming for its output data structures so the net win with + // streaming input data would be very low. + std::vector contents; + contents.resize(stream->FileSize() + 1); + stream->Read(&*contents.begin(), 1, contents.size() - 1); + contents[contents.size() - 1] = 0; + const char *const begin = &*contents.begin(); - // broadphase tokenizing pass in which we identify the core - // syntax elements of FBX (brackets, commas, key:value mappings) - TokenList tokens; - try { + // broadphase tokenizing pass in which we identify the core + // syntax elements of FBX (brackets, commas, key:value mappings) + TokenList tokens; + try { - bool is_binary = false; - if (!strncmp(begin,"Kaydara FBX Binary",18)) { - is_binary = true; - TokenizeBinary(tokens,begin,contents.size()); - } - else { - Tokenize(tokens,begin); - } + bool is_binary = false; + if (!strncmp(begin, "Kaydara FBX Binary", 18)) { + is_binary = true; + TokenizeBinary(tokens, begin, contents.size()); + } else { + Tokenize(tokens, begin); + } - // use this information to construct a very rudimentary - // parse-tree representing the FBX scope structure - Parser parser(tokens, is_binary); + // use this information to construct a very rudimentary + // parse-tree representing the FBX scope structure + Parser parser(tokens, is_binary); - // take the raw parse-tree and convert it to a FBX DOM - Document doc(parser,settings); + // take the raw parse-tree and convert it to a FBX DOM + Document doc(parser, settings); - // convert the FBX DOM to aiScene - ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); + // convert the FBX DOM to aiScene + ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); - // size relative to cm - float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); + // size relative to cm + float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); - // Set FBX file scale is relative to CM must be converted to M for - // assimp universal format (M) - SetFileScale( size_relative_to_cm * 0.01f); + // Set FBX file scale is relative to CM must be converted to M for + // assimp universal format (M) + SetFileScale(size_relative_to_cm * 0.01f); - std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); - } - catch(std::exception&) { - std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); - throw; - } + std::for_each(tokens.begin(), tokens.end(), Util::delete_fun()); + } catch (std::exception &) { + std::for_each(tokens.begin(), tokens.end(), Util::delete_fun()); + throw; + } } #endif // !ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index eb30ad5df..e652a6927 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -252,6 +252,9 @@ struct aiVertexWeight { }; +// Forward declare aiNode (pointer use only) +struct aiNode; + // --------------------------------------------------------------------------- /** @brief A single bone of a mesh. * @@ -268,6 +271,12 @@ struct aiBone { //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. unsigned int mNumWeights; + // The bone armature node - used for skeleton conversion + C_STRUCT aiNode* mArmature; + + // The bone node in the scene - used for skeleton conversion + C_STRUCT aiNode* mNode; + //! The influence weights of this bone, by vertex index. C_STRUCT aiVertexWeight* mWeights; @@ -284,6 +293,11 @@ struct aiBone { */ C_STRUCT aiMatrix4x4 mOffsetMatrix; + /** Matrix used for the global rest transform + * This tells you directly the rest without extending as required in most game engine implementations + * */ + C_STRUCT aiMatrix4x4 mRestMatrix; + #ifdef __cplusplus //! Default constructor @@ -773,7 +787,10 @@ struct aiMesh // DO NOT REMOVE THIS ADDITIONAL CHECK if (mNumBones && mBones) { for( unsigned int a = 0; a < mNumBones; a++) { - delete mBones[a]; + if(mBones[a]) + { + delete mBones[a]; + } } delete [] mBones; } From 6ea97a1282835ef43e25044a5053882bbf285d7a Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sat, 26 Oct 2019 17:27:07 +0100 Subject: [PATCH 03/14] Updated test cases to test import of names This now doesn't overwrite names anymore as this would cause nasty bugs application side. We can now support these by default without having to handle them as edge cases. --- test/unit/utFBXImporterExporter.cpp | 39 ++++++++--------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index 1445e3c3e..e73733a0d 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -76,6 +76,7 @@ TEST_F( utFBXImporterExporter, importBareBoxWithoutColorsAndTextureCoords ) { EXPECT_EQ(mesh->mNumVertices, 36u); } + TEST_F(utFBXImporterExporter, importCubesWithNoNames) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/cubes_nonames.fbx", aiProcess_ValidateDataStructure); @@ -86,26 +87,6 @@ TEST_F(utFBXImporterExporter, importCubesWithNoNames) { ASSERT_STREQ(root->mName.C_Str(), "RootNode"); ASSERT_TRUE(root->mChildren); ASSERT_EQ(root->mNumChildren, 2u); - - const auto child0 = root->mChildren[0]; - ASSERT_TRUE(child0); - ASSERT_STREQ(child0->mName.C_Str(), "RootNode001"); - ASSERT_TRUE(child0->mChildren); - ASSERT_EQ(child0->mNumChildren, 1u); - - const auto child00 = child0->mChildren[0]; - ASSERT_TRUE(child00); - ASSERT_STREQ(child00->mName.C_Str(), "RootNode001001"); - - const auto child1 = root->mChildren[1]; - ASSERT_TRUE(child1); - ASSERT_STREQ(child1->mName.C_Str(), "RootNode002"); - ASSERT_TRUE(child1->mChildren); - ASSERT_EQ(child1->mNumChildren, 1u); - - const auto child10 = child1->mChildren[0]; - ASSERT_TRUE(child10); - ASSERT_STREQ(child10->mName.C_Str(), "RootNode002001"); } TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) { @@ -137,7 +118,7 @@ TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) { const auto child10 = child1->mChildren[0]; ASSERT_TRUE(child10); - ASSERT_STREQ(child10->mName.C_Str(), "\xd0\x9a\xd1\x83\xd0\xb1\x31""001"); + ASSERT_STREQ(child10->mName.C_Str(), "\xd0\x9a\xd1\x83\xd0\xb1\x31"); } TEST_F(utFBXImporterExporter, importCubesComplexTransform) { @@ -168,14 +149,14 @@ TEST_F(utFBXImporterExporter, importCubesComplexTransform) { auto parent = child1; const size_t chain_length = 8u; const char* chainStr[chain_length] = { - "Cube1001_$AssimpFbx$_Translation", - "Cube1001_$AssimpFbx$_RotationPivot", - "Cube1001_$AssimpFbx$_RotationPivotInverse", - "Cube1001_$AssimpFbx$_ScalingOffset", - "Cube1001_$AssimpFbx$_ScalingPivot", - "Cube1001_$AssimpFbx$_Scaling", - "Cube1001_$AssimpFbx$_ScalingPivotInverse", - "Cube1001" + "Cube1_$AssimpFbx$_Translation", + "Cube1_$AssimpFbx$_RotationPivot", + "Cube1_$AssimpFbx$_RotationPivotInverse", + "Cube1_$AssimpFbx$_ScalingOffset", + "Cube1_$AssimpFbx$_ScalingPivot", + "Cube1_$AssimpFbx$_Scaling", + "Cube1_$AssimpFbx$_ScalingPivotInverse", + "Cube1" }; for (size_t i = 0; i < chain_length; ++i) { ASSERT_TRUE(parent->mChildren); From 93efe4197aff5f46358545dd6b890ca454e8bfcd Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sat, 26 Oct 2019 18:09:26 +0100 Subject: [PATCH 04/14] Removed redundant rest matrix and fixed assert compile error --- code/FBX/FBXConverter.cpp | 21 +++++++-------------- include/assimp/mesh.h | 5 ----- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index f8225f2e5..0d754a638 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -151,13 +151,6 @@ namespace Assimp { // set this bone node to be referenced properly ai_assert(bone_node); bone->mNode = bone_node; - - // apply full hierarchy to transform for basic offset - while( bone_node->mParent ) - { - bone->mRestMatrix = bone_node->mTransformation * bone->mRestMatrix; - bone_node = bone_node->mParent; - } } @@ -249,12 +242,12 @@ namespace Assimp { /* Prepare flat node list which can be used for non recursive lookups later */ void FBXConverter::BuildNodeList(aiNode *current_node, std::vector &nodes) { - assert(current_node); + ai_assert(current_node); for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { aiNode *child = current_node->mChildren[nodeId]; - assert(child); + ai_assert(child); nodes.push_back(child); @@ -268,19 +261,19 @@ namespace Assimp { /* Source: sketch fab log cutter fbx */ void FBXConverter::BuildBoneList(aiNode *current_node, const aiNode * root_node, const aiScene *scene, std::vector &bones ) { - assert(scene); + ai_assert(scene); for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { aiNode *child = current_node->mChildren[nodeId]; - assert(child); + ai_assert(child); // check for bones for( unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) { - assert(child->mMeshes); + ai_assert(child->mMeshes); unsigned int mesh_index = child->mMeshes[meshId]; aiMesh *mesh = scene->mMeshes[ mesh_index ]; - assert(mesh); + ai_assert(mesh); for( unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) { @@ -1777,7 +1770,7 @@ namespace Assimp { std::vector &out_indices, std::vector &index_out_indices, std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *root_node) { - assert(cl); // make sure cluster valid + ai_assert(cl); // make sure cluster valid std::string deformer_name = cl->TargetNode()->Name(); aiString bone_name = aiString(FixNodeName(deformer_name)); diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index e652a6927..617835e51 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -293,11 +293,6 @@ struct aiBone { */ C_STRUCT aiMatrix4x4 mOffsetMatrix; - /** Matrix used for the global rest transform - * This tells you directly the rest without extending as required in most game engine implementations - * */ - C_STRUCT aiMatrix4x4 mRestMatrix; - #ifdef __cplusplus //! Default constructor From 46cdd81d754bb602dabd3a3a026607bc2759e37e Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sat, 26 Oct 2019 18:47:48 +0100 Subject: [PATCH 05/14] Added ArmaturePopulate scale process for all formats --- code/CMakeLists.txt | 2 + code/FBX/FBXConverter.cpp | 194 ------------------ code/PostProcessing/ArmaturePopulate.cpp | 247 +++++++++++++++++++++++ code/PostProcessing/ArmaturePopulate.h | 111 ++++++++++ include/assimp/postprocess.h | 12 ++ 5 files changed, 372 insertions(+), 194 deletions(-) create mode 100644 code/PostProcessing/ArmaturePopulate.cpp create mode 100644 code/PostProcessing/ArmaturePopulate.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index eec805b54..2c030abbb 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -670,6 +670,8 @@ SET( PostProcessing_SRCS PostProcessing/MakeVerboseFormat.h PostProcessing/ScaleProcess.cpp PostProcessing/ScaleProcess.h + PostProcessing/ArmaturePopulate.cpp + PostProcessing/ArmaturePopulate.h PostProcessing/GenBoundingBoxesProcess.cpp PostProcessing/GenBoundingBoxesProcess.h ) diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index 0d754a638..88abd6721 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -121,39 +121,6 @@ namespace Assimp { ConvertGlobalSettings(); TransferDataToScene(); - // Now convert all bone positions to the correct mOffsetMatrix - std::vector bones; - std::vector nodes; - std::map bone_stack; - BuildBoneList(out->mRootNode, out->mRootNode, out, bones); - BuildNodeList(out->mRootNode, nodes ); - - - BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); - - std::cout << "Bone stack size: " << bone_stack.size() << std::endl; - - for( std::pair kvp : bone_stack ) - { - aiBone *bone = kvp.first; - aiNode *bone_node = kvp.second; - std::cout << "active node lookup: " << bone->mName.C_Str() << std::endl; - // lcl transform grab - done in generate_nodes :) - - //bone->mOffsetMatrix = bone_node->mTransformation; - aiNode * armature = GetArmatureRoot(bone_node, bones); - - ai_assert(armature); - - // set up bone armature id - bone->mArmature = armature; - - // set this bone node to be referenced properly - ai_assert(bone_node); - bone->mNode = bone_node; - } - - // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE // to make sure the scene passes assimp's validation. FBX files // need not contain geometry (i.e. camera animations, raw armatures). @@ -172,167 +139,6 @@ namespace Assimp { std::for_each(textures.begin(), textures.end(), Util::delete_fun()); } - /* Returns the armature root node */ - /* This is required to be detected for a bone initially, it will recurse up until it cannot find another - * bone and return the node - * No known failure points. (yet) - */ - aiNode * FBXConverter::GetArmatureRoot(aiNode *bone_node, std::vector &bone_list) - { - while(bone_node) - { - if(!IsBoneNode(bone_node->mName, bone_list)) - { - std::cout << "Found valid armature: " << bone_node->mName.C_Str() << std::endl; - return bone_node; - } - - bone_node = bone_node->mParent; - } - - std::cout << "can't find armature! node: " << bone_node << std::endl; - - return NULL; - } - - /* Simple IsBoneNode check if this could be a bone */ - bool FBXConverter::IsBoneNode(const aiString &bone_name, std::vector& bones ) - { - for( aiBone *bone : bones) - { - if(bone->mName == bone_name) - { - return true; - } - } - - return false; - } - - - /* Pop this node by name from the stack if found */ - /* Used in multiple armature situations with duplicate node / bone names */ - /* Known flaw: cannot have nodes with bone names, will be fixed in later release */ - /* (serious to be fixed) Known flaw: nodes which have more than one bone could be prematurely dropped from stack */ - aiNode* FBXConverter::GetNodeFromStack(const aiString &node_name, std::vector &nodes) - { - std::vector::iterator iter; - aiNode *found = NULL; - for( iter = nodes.begin(); iter < nodes.end(); ++iter ) - { - aiNode *element = *iter; - ai_assert(element); - // node valid and node name matches - if(element->mName == node_name) - { - found = element; - break; - } - } - - if(found != NULL) { - // now pop the element from the node list - nodes.erase(iter); - - return found; - } - return NULL; - } - - /* Prepare flat node list which can be used for non recursive lookups later */ - void FBXConverter::BuildNodeList(aiNode *current_node, std::vector &nodes) - { - ai_assert(current_node); - - for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) - { - aiNode *child = current_node->mChildren[nodeId]; - ai_assert(child); - - nodes.push_back(child); - - BuildNodeList(child, nodes); - } - } - - /* Reprocess all nodes to calculate bone transforms properly based on the REAL mOffsetMatrix not the local. */ - /* Before this would use mesh transforms which is wrong for bone transforms */ - /* Before this would work for simple character skeletons but not complex meshes with multiple origins */ - /* Source: sketch fab log cutter fbx */ - void FBXConverter::BuildBoneList(aiNode *current_node, const aiNode * root_node, const aiScene *scene, std::vector &bones ) - { - ai_assert(scene); - for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) - { - aiNode *child = current_node->mChildren[nodeId]; - ai_assert(child); - - // check for bones - for( unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) - { - ai_assert(child->mMeshes); - unsigned int mesh_index = child->mMeshes[meshId]; - aiMesh *mesh = scene->mMeshes[ mesh_index ]; - ai_assert(mesh); - - for( unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) - { - aiBone *bone = mesh->mBones[boneId]; - ai_assert(bone); - - // duplicate meshes exist with the same bones sometimes :) - // so this must be detected - if( std::find(bones.begin(), bones.end(), bone) == bones.end() ) - { - // add the element once - bones.push_back(bone); - } - } - - // find mesh and get bones - // then do recursive lookup for bones in root node hierarchy - } - - BuildBoneList(child, root_node, scene, bones); - } - } - - /* A bone stack allows us to have multiple armatures, with the same bone names - * A bone stack allows us also to retrieve bones true transform even with duplicate names :) - */ - void FBXConverter::BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, - const std::vector &bones, - std::map &bone_stack, - std::vector &node_stack ) - { - ai_assert(scene); - ai_assert(root_node); - ai_assert(!node_stack.empty()); - - for( aiBone * bone : bones) - { - ai_assert(bone); - aiNode* node = GetNodeFromStack(bone->mName, node_stack); - if(node == NULL) - { - node_stack.clear(); - BuildNodeList(out->mRootNode, node_stack ); - std::cout << "Resetting bone stack: null element " << bone->mName.C_Str() << std::endl; - - node = GetNodeFromStack(bone->mName, node_stack); - - if(!node) { - std::cout << "serious import issue armature failed to be detected?" << std::endl; - continue; - } - } - - std::cout << "Successfully added bone to stack and have valid armature: " << bone->mName.C_Str() << std::endl; - - bone_stack.insert(std::pair(bone, node)); - } - } - void FBXConverter::ConvertRootNode() { out->mRootNode = new aiNode(); std::string unique_name; diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp new file mode 100644 index 000000000..1892645a9 --- /dev/null +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -0,0 +1,247 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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. + +---------------------------------------------------------------------- +*/ +#include "ArmaturePopulate.h" + +#include +#include +#include +#include + +namespace Assimp { + +bool ArmaturePopulate::IsActive(unsigned int pFlags) const { + return (pFlags & aiProcess_PopulateArmatureData) != 0; +} + +void ArmaturePopulate::SetupProperties(const Importer *pImp) { + // do nothing +} + +void ArmaturePopulate::Execute(aiScene *out) { + + // Now convert all bone positions to the correct mOffsetMatrix + std::vector bones; + std::vector nodes; + std::map bone_stack; + BuildBoneList(out->mRootNode, out->mRootNode, out, bones); + BuildNodeList(out->mRootNode, nodes); + + BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); + + ASSIMP_LOG_DEBUG_F("Bone stack size: %ld\n", bone_stack.size()); + + for (std::pair kvp : bone_stack) { + aiBone *bone = kvp.first; + aiNode *bone_node = kvp.second; + ASSIMP_LOG_DEBUG_F("active node lookup: %s\n", bone->mName.C_Str()); + // lcl transform grab - done in generate_nodes :) + + // bone->mOffsetMatrix = bone_node->mTransformation; + aiNode *armature = GetArmatureRoot(bone_node, bones); + + ai_assert(armature); + + // set up bone armature id + bone->mArmature = armature; + + // set this bone node to be referenced properly + ai_assert(bone_node); + bone->mNode = bone_node; + } +} + +/* Returns the armature root node */ +/* This is required to be detected for a bone initially, it will recurse up + * until it cannot find another bone and return the node No known failure + * points. (yet) + */ +aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, + std::vector &bone_list) { + while (bone_node) { + if (!IsBoneNode(bone_node->mName, bone_list)) { + ASSIMP_LOG_DEBUG_F("Found valid armature: %s\n", bone_node->mName.C_Str()); + return bone_node; + } + + bone_node = bone_node->mParent; + } + + ASSIMP_LOG_WARN_F("can't find armature! node: %s\n", bone_node->mName.C_Str()); + + return NULL; +} + +/* Simple IsBoneNode check if this could be a bone */ +bool ArmaturePopulate::IsBoneNode(const aiString &bone_name, + std::vector &bones) { + for (aiBone *bone : bones) { + if (bone->mName == bone_name) { + return true; + } + } + + return false; +} + +/* Pop this node by name from the stack if found */ +/* Used in multiple armature situations with duplicate node / bone names */ +/* Known flaw: cannot have nodes with bone names, will be fixed in later release + */ +/* (serious to be fixed) Known flaw: nodes which have more than one bone could + * be prematurely dropped from stack */ +aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, + std::vector &nodes) { + std::vector::iterator iter; + aiNode *found = NULL; + for (iter = nodes.begin(); iter < nodes.end(); ++iter) { + aiNode *element = *iter; + ai_assert(element); + // node valid and node name matches + if (element->mName == node_name) { + found = element; + break; + } + } + + if (found != NULL) { + // now pop the element from the node list + nodes.erase(iter); + + return found; + } + return NULL; +} + +/* Prepare flat node list which can be used for non recursive lookups later */ +void ArmaturePopulate::BuildNodeList(const aiNode *current_node, + std::vector &nodes) { + ai_assert(current_node); + + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + nodes.push_back(child); + + BuildNodeList(child, nodes); + } +} + +/* Reprocess all nodes to calculate bone transforms properly based on the REAL + * mOffsetMatrix not the local. */ +/* Before this would use mesh transforms which is wrong for bone transforms */ +/* Before this would work for simple character skeletons but not complex meshes + * with multiple origins */ +/* Source: sketch fab log cutter fbx */ +void ArmaturePopulate::BuildBoneList(aiNode *current_node, + const aiNode *root_node, + const aiScene *scene, + std::vector &bones) { + ai_assert(scene); + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + // check for bones + for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) { + ai_assert(child->mMeshes); + unsigned int mesh_index = child->mMeshes[meshId]; + aiMesh *mesh = scene->mMeshes[mesh_index]; + ai_assert(mesh); + + for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) { + aiBone *bone = mesh->mBones[boneId]; + ai_assert(bone); + + // duplicate meshes exist with the same bones sometimes :) + // so this must be detected + if (std::find(bones.begin(), bones.end(), bone) == bones.end()) { + // add the element once + bones.push_back(bone); + } + } + + // find mesh and get bones + // then do recursive lookup for bones in root node hierarchy + } + + BuildBoneList(child, root_node, scene, bones); + } +} + +/* A bone stack allows us to have multiple armatures, with the same bone names + * A bone stack allows us also to retrieve bones true transform even with + * duplicate names :) + */ +void ArmaturePopulate::BuildBoneStack(aiNode *current_node, + const aiNode *root_node, + const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack) { + ai_assert(scene); + ai_assert(root_node); + ai_assert(!node_stack.empty()); + + for (aiBone *bone : bones) { + ai_assert(bone); + aiNode *node = GetNodeFromStack(bone->mName, node_stack); + if (node == NULL) { + node_stack.clear(); + BuildNodeList(root_node, node_stack); + ASSIMP_LOG_DEBUG_F("Resetting bone stack: null element %s\n", bone->mName.C_Str()); + + node = GetNodeFromStack(bone->mName, node_stack); + + if (!node) { + ASSIMP_LOG_ERROR("serious import issue armature failed to be detected"); + continue; + } + } + + ASSIMP_LOG_DEBUG_F("Successfully added bone to stack and have valid armature: %s\n", bone->mName.C_Str()); + + bone_stack.insert(std::pair(bone, node)); + } +} + +} // Namespace Assimp diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h new file mode 100644 index 000000000..d84fbe09b --- /dev/null +++ b/code/PostProcessing/ArmaturePopulate.h @@ -0,0 +1,111 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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. + +---------------------------------------------------------------------- +*/ +#ifndef SCALE_PROCESS_H_ +#define SCALE_PROCESS_H_ + +#include "Common/BaseProcess.h" +#include +#include + +struct aiNode; +struct aiBone; + +#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) +# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f +#endif // !! AI_DEBONE_THRESHOLD + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** ScaleProcess: Class to rescale the whole model. + * Now rescales animations, bones, and blend shapes properly. + * Please note this will not write to 'scale' transform it will rewrite mesh + * and matrixes so that your scale values + * from your model package are preserved, so this is completely intentional + * bugs should be reported as soon as they are found. +*/ +class ASSIMP_API ArmaturePopulate : public BaseProcess { +public: + /// The default class constructor. + ArmaturePopulate() : BaseProcess() + {} + + /// The class destructor. + virtual ~ArmaturePopulate() + {} + + /// Overwritten, @see BaseProcess + virtual bool IsActive( unsigned int pFlags ) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties( const Importer* pImp ); + + /// Overwritten, @see BaseProcess + virtual void Execute( aiScene* pScene ); + + static aiNode *GetArmatureRoot(aiNode *bone_node, + std::vector &bone_list); + + static bool IsBoneNode(const aiString &bone_name, + std::vector &bones); + + static aiNode *GetNodeFromStack(const aiString &node_name, + std::vector &nodes); + + static void BuildNodeList(const aiNode *current_node, + std::vector &nodes); + + static void BuildBoneList(aiNode *current_node, const aiNode *root_node, + const aiScene *scene, + std::vector &bones); + + static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, + const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack); +}; + +} // Namespace Assimp + + +#endif // SCALE_PROCESS_H_ \ No newline at end of file diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index 77d387c7e..2af48aedd 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -537,6 +537,18 @@ enum aiPostProcessSteps */ aiProcess_Debone = 0x4000000, + + // ------------------------------------------------------------------------- + /** + * This step generically populates aiBone->mArmature and aiBone->mNode generically + * The point of these is it saves you later having to calculate these elements + * This is useful when handling rest information or skin information + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the + * hard work for you :) + */ + aiProcess_PopulateArmatureData = 0x5000000, + // ------------------------------------------------------------------------- /**
This step will perform a global scale of the model. * From 514257f58700385c15d23b27c11a7240cd433550 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sat, 26 Oct 2019 19:48:34 +0100 Subject: [PATCH 06/14] Added unit tests for ArmaturePopulate when used (added huestos model to tests) Added clear documentation for this too to explain, you need to enable it to make it available Signed-off-by: RevoluPowered --- code/Common/PostStepRegistry.cpp | 7 ++ code/PostProcessing/ArmaturePopulate.cpp | 14 +++- code/PostProcessing/ArmaturePopulate.h | 16 ++--- include/assimp/mesh.h | 4 ++ test/CMakeLists.txt | 1 + test/models/FBX/huesitos.fbx | Bin 0 -> 111564 bytes test/unit/utArmaturePopulate.cpp | 82 +++++++++++++++++++++++ 7 files changed, 111 insertions(+), 13 deletions(-) create mode 100644 test/models/FBX/huesitos.fbx create mode 100644 test/unit/utArmaturePopulate.cpp diff --git a/code/Common/PostStepRegistry.cpp b/code/Common/PostStepRegistry.cpp index ef58f8ddf..8ff4af040 100644 --- a/code/Common/PostStepRegistry.cpp +++ b/code/Common/PostStepRegistry.cpp @@ -131,11 +131,15 @@ corresponding preprocessor flag to selectively disable steps. #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) # include "PostProcessing/ScaleProcess.h" #endif +#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS) +# include "PostProcessing/ArmaturePopulate.h" +#endif #if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) # include "PostProcessing/GenBoundingBoxesProcess.h" #endif + namespace Assimp { // ------------------------------------------------------------------------------------------------ @@ -180,6 +184,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) out.push_back( new ScaleProcess()); #endif +#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS) + out.push_back( new ArmaturePopulate()); +#endif #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) out.push_back( new PretransformVertices()); #endif diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp index 1892645a9..11fffc399 100644 --- a/code/PostProcessing/ArmaturePopulate.cpp +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -48,6 +48,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +/// The default class constructor. +ArmaturePopulate::ArmaturePopulate() : BaseProcess() +{} + +/// The class destructor. +ArmaturePopulate::~ArmaturePopulate() +{} + bool ArmaturePopulate::IsActive(unsigned int pFlags) const { return (pFlags & aiProcess_PopulateArmatureData) != 0; } @@ -104,9 +112,9 @@ aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, bone_node = bone_node->mParent; } - - ASSIMP_LOG_WARN_F("can't find armature! node: %s\n", bone_node->mName.C_Str()); - + + ASSIMP_LOG_WARN("GetArmatureRoot() can't find armature!"); + return NULL; } diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h index d84fbe09b..30bfc7509 100644 --- a/code/PostProcessing/ArmaturePopulate.h +++ b/code/PostProcessing/ArmaturePopulate.h @@ -39,20 +39,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -#ifndef SCALE_PROCESS_H_ -#define SCALE_PROCESS_H_ +#ifndef ARMATURE_POPULATE_H_ +#define ARMATURE_POPULATE_H_ #include "Common/BaseProcess.h" +#include #include #include + struct aiNode; struct aiBone; -#if (!defined AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT) -# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f -#endif // !! AI_DEBONE_THRESHOLD - namespace Assimp { // --------------------------------------------------------------------------- @@ -66,12 +64,10 @@ namespace Assimp { class ASSIMP_API ArmaturePopulate : public BaseProcess { public: /// The default class constructor. - ArmaturePopulate() : BaseProcess() - {} + ArmaturePopulate(); /// The class destructor. - virtual ~ArmaturePopulate() - {} + virtual ~ArmaturePopulate(); /// Overwritten, @see BaseProcess virtual bool IsActive( unsigned int pFlags ) const; diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index 617835e51..bde69ac9b 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -271,12 +271,16 @@ struct aiBone { //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. unsigned int mNumWeights; +#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS // The bone armature node - used for skeleton conversion + // you must enable aiProcess_PopulateArmatureData to populate this C_STRUCT aiNode* mArmature; // The bone node in the scene - used for skeleton conversion + // you must enable aiProcess_PopulateArmatureData to populate this C_STRUCT aiNode* mNode; +#endif //! The influence weights of this bone, by vertex index. C_STRUCT aiVertexWeight* mWeights; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 913813c3b..f59ce000a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -148,6 +148,7 @@ SET( POST_PROCESSES unit/utRemoveRedundantMaterials.cpp unit/utRemoveVCProcess.cpp unit/utScaleProcess.cpp + unit/utArmaturePopulate.cpp unit/utJoinVertices.cpp unit/utRemoveComments.cpp unit/utRemoveComponent.cpp diff --git a/test/models/FBX/huesitos.fbx b/test/models/FBX/huesitos.fbx new file mode 100644 index 0000000000000000000000000000000000000000..646271392e5fbd5c3a069498778fb70ba302ebef GIT binary patch literal 111564 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 BGFAWp literal 0 HcmV?d00001 diff --git a/test/unit/utArmaturePopulate.cpp b/test/unit/utArmaturePopulate.cpp new file mode 100644 index 000000000..4aaf8aa7c --- /dev/null +++ b/test/unit/utArmaturePopulate.cpp @@ -0,0 +1,82 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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. +--------------------------------------------------------------------------- +*/ +#include "UnitTestPCH.h" +#include "TestModelFactory.h" + + +#include "SceneDiffer.h" +#include "AbstractImportExportBase.h" + +#include +#include +#include +#include +#include + +#include "PostProcessing/ArmaturePopulate.h" + +namespace Assimp { +namespace UnitTest { + +class utArmaturePopulate : public ::testing::Test { + // empty +}; + +TEST_F( utArmaturePopulate, importCheckForArmatureTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", aiProcess_ValidateDataStructure | aiProcess_PopulateArmatureData); + EXPECT_NE( nullptr, scene ); + EXPECT_EQ(scene->mNumMeshes, 1u); + aiMesh* mesh = scene->mMeshes[0]; + EXPECT_EQ(mesh->mNumFaces, 68u); + EXPECT_EQ(mesh->mNumVertices, 256u); + EXPECT_GT(mesh->mNumBones, 0u); + + aiBone* exampleBone = mesh->mBones[0]; + EXPECT_NE(exampleBone, nullptr); + EXPECT_NE(exampleBone->mArmature, nullptr); + EXPECT_NE(exampleBone->mNode, nullptr); +} + +} // Namespace UnitTest +} // Namespace Assimp From a30936954ef49236397938305e9c05e57670203c Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 11:21:23 +0000 Subject: [PATCH 07/14] Best to check the number of children before checking the actual array --- code/Common/scene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index 2acb348d8..315581504 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -68,7 +68,7 @@ aiNode::aiNode(const std::string& name) aiNode::~aiNode() { // delete all children recursively // to make sure we won't crash if the data is invalid ... - if (mChildren && mNumChildren) + if (mNumChildren && mChildren) { for (unsigned int a = 0; a < mNumChildren; a++) delete mChildren[a]; From 5d0c63391b729cd7c839a9d8088625d1716c5fa5 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 11:21:49 +0000 Subject: [PATCH 08/14] Explicitly set the size of the parent node if we have no children --- code/FBX/FBXConverter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index 88abd6721..8f916a25b 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -313,6 +313,12 @@ namespace Assimp { std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren); } + else + { + parent->mNumChildren = 0; + parent->mChildren = nullptr; + } + } catch (std::exception&) { Util::delete_fun deleter; From 9c8d8357046e4d422b1ba20ffcf3b2da072c7774 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 11:29:56 +0000 Subject: [PATCH 09/14] Explicitly use nullptr --- code/Common/scene.cpp | 16 ++++++++-------- code/FBX/FBXConverter.cpp | 20 ++++++++++---------- code/PostProcessing/ArmaturePopulate.cpp | 12 ++++++------ include/assimp/mesh.h | 18 +++++++++--------- include/assimp/scene.h | 22 +++++++++++----------- 5 files changed, 44 insertions(+), 44 deletions(-) diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index 315581504..d15619acf 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -44,23 +44,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. aiNode::aiNode() : mName("") -, mParent(NULL) +, mParent(nullptr) , mNumChildren(0) -, mChildren(NULL) +, mChildren(nullptr) , mNumMeshes(0) -, mMeshes(NULL) -, mMetaData(NULL) { +, mMeshes(nullptr) +, mMetaData(nullptr) { // empty } aiNode::aiNode(const std::string& name) : mName(name) -, mParent(NULL) +, mParent(nullptr) , mNumChildren(0) -, mChildren(NULL) +, mChildren(nullptr) , mNumMeshes(0) -, mMeshes(NULL) -, mMetaData(NULL) { +, mMeshes(nullptr) +, mMetaData(nullptr) { // empty } diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index 8f916a25b..20344331c 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -1134,7 +1134,7 @@ namespace Assimp { binormals = &tempBinormals; } else { - binormals = NULL; + binormals = nullptr; } } @@ -1184,7 +1184,7 @@ namespace Assimp { ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); } - if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { + if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) { ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION, nullptr); } @@ -1264,7 +1264,7 @@ namespace Assimp { const std::vector& vertices = mesh.GetVertices(); const std::vector& faces = mesh.GetFaceIndexCounts(); - const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; + const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != nullptr; unsigned int count_faces = 0; unsigned int count_vertices = 0; @@ -1324,7 +1324,7 @@ namespace Assimp { binormals = &tempBinormals; } else { - binormals = NULL; + binormals = nullptr; } } @@ -1513,9 +1513,9 @@ namespace Assimp { unsigned int count = 0; const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); - // ToOutputVertexIndex only returns NULL if index is out of bounds + // ToOutputVertexIndex only returns nullptr if index is out of bounds // which should never happen - ai_assert(out_idx != NULL); + ai_assert(out_idx != nullptr); index_out_indices.push_back(no_index_sentinel); count_out_indices.push_back(0); @@ -1586,7 +1586,7 @@ namespace Assimp { std::string deformer_name = cl->TargetNode()->Name(); aiString bone_name = aiString(FixNodeName(deformer_name)); - aiBone *bone = NULL; + aiBone *bone = nullptr; if (bone_map.count(deformer_name)) { std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name @@ -2740,7 +2740,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa // sanity check whether the input is ok static void validateAnimCurveNodes(const std::vector& curves, bool strictMode) { - const Object* target(NULL); + const Object* target(nullptr); for (const AnimationCurveNode* node : curves) { if (!target) { target = node->Target(); @@ -2771,7 +2771,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa #ifdef ASSIMP_BUILD_DEBUG validateAnimCurveNodes(curves, doc.Settings().strictMode); #endif - const AnimationCurveNode* curve_node = NULL; + const AnimationCurveNode* curve_node = nullptr; for (const AnimationCurveNode* node : curves) { ai_assert(node); @@ -3619,7 +3619,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa ai_assert(!out->mMeshes); ai_assert(!out->mNumMeshes); - // note: the trailing () ensures initialization with NULL - not + // note: the trailing () ensures initialization with nullptr - not // many C++ users seem to know this, so pointing it out to avoid // confusion why this code works. diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp index 11fffc399..c677b193a 100644 --- a/code/PostProcessing/ArmaturePopulate.cpp +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -115,7 +115,7 @@ aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, ASSIMP_LOG_WARN("GetArmatureRoot() can't find armature!"); - return NULL; + return nullptr; } /* Simple IsBoneNode check if this could be a bone */ @@ -139,7 +139,7 @@ bool ArmaturePopulate::IsBoneNode(const aiString &bone_name, aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, std::vector &nodes) { std::vector::iterator iter; - aiNode *found = NULL; + aiNode *found = nullptr; for (iter = nodes.begin(); iter < nodes.end(); ++iter) { aiNode *element = *iter; ai_assert(element); @@ -150,13 +150,13 @@ aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, } } - if (found != NULL) { + if (found != nullptr) { // now pop the element from the node list nodes.erase(iter); return found; } - return NULL; + return nullptr; } /* Prepare flat node list which can be used for non recursive lookups later */ @@ -233,10 +233,10 @@ void ArmaturePopulate::BuildBoneStack(aiNode *current_node, for (aiBone *bone : bones) { ai_assert(bone); aiNode *node = GetNodeFromStack(bone->mName, node_stack); - if (node == NULL) { + if (node == nullptr) { node_stack.clear(); BuildNodeList(root_node, node_stack); - ASSIMP_LOG_DEBUG_F("Resetting bone stack: null element %s\n", bone->mName.C_Str()); + ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element %s\n", bone->mName.C_Str()); node = GetNodeFromStack(bone->mName, node_stack); diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index bde69ac9b..fbf2a857a 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -435,11 +435,11 @@ struct aiAnimMesh /**Anim Mesh name */ C_STRUCT aiString mName; - /** Replacement for aiMesh::mVertices. If this array is non-NULL, + /** Replacement for aiMesh::mVertices. If this array is non-nullptr, * it *must* contain mNumVertices entries. The corresponding - * array in the host mesh must be non-NULL as well - animation + * array in the host mesh must be non-nullptr as well - animation * meshes may neither add or nor remove vertex components (if - * a replacement array is NULL and the corresponding source + * a replacement array is nullptr and the corresponding source * array is not, the source data is taken instead)*/ C_STRUCT aiVector3D* mVertices; @@ -613,7 +613,7 @@ struct aiMesh C_STRUCT aiVector3D* mVertices; /** Vertex normals. - * The array contains normalized vectors, NULL if not present. + * The array contains normalized vectors, nullptr if not present. * The array is mNumVertices in size. Normals are undefined for * point and line primitives. A mesh consisting of points and * lines only may not have normal vectors. Meshes with mixed @@ -636,7 +636,7 @@ struct aiMesh /** Vertex tangents. * The tangent of a vertex points in the direction of the positive - * X texture axis. The array contains normalized vectors, NULL if + * X texture axis. The array contains normalized vectors, nullptr if * not present. The array is mNumVertices in size. A mesh consisting * of points and lines only may not have normal vectors. Meshes with * mixed primitive types (i.e. lines and triangles) may have @@ -650,7 +650,7 @@ struct aiMesh /** Vertex bitangents. * The bitangent of a vertex points in the direction of the positive - * Y texture axis. The array contains normalized vectors, NULL if not + * Y texture axis. The array contains normalized vectors, nullptr if not * present. The array is mNumVertices in size. * @note If the mesh contains tangents, it automatically also contains * bitangents. @@ -659,14 +659,14 @@ struct aiMesh /** Vertex color sets. * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex - * colors per vertex. NULL if not present. Each array is + * colors per vertex. nullptr if not present. Each array is * mNumVertices in size if present. */ C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; /** Vertex texture coords, also known as UV channels. * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per - * vertex. NULL if not present. The array is mNumVertices in size. + * vertex. nullptr if not present. The array is mNumVertices in size. */ C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; @@ -688,7 +688,7 @@ struct aiMesh C_STRUCT aiFace* mFaces; /** The number of bones this mesh contains. - * Can be 0, in which case the mBones array is NULL. + * Can be 0, in which case the mBones array is nullptr. */ unsigned int mNumBones; diff --git a/include/assimp/scene.h b/include/assimp/scene.h index e69c81803..b76709eb1 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -110,13 +110,13 @@ struct ASSIMP_API aiNode /** The transformation relative to the node's parent. */ C_STRUCT aiMatrix4x4 mTransformation; - /** Parent node. NULL if this node is the root node. */ + /** Parent node. nullptr if this node is the root node. */ C_STRUCT aiNode* mParent; /** The number of child nodes of this node. */ unsigned int mNumChildren; - /** The child nodes of this node. NULL if mNumChildren is 0. */ + /** The child nodes of this node. nullptr if mNumChildren is 0. */ C_STRUCT aiNode** mChildren; /** The number of meshes of this node. */ @@ -127,7 +127,7 @@ struct ASSIMP_API aiNode */ unsigned int* mMeshes; - /** Metadata associated with this node or NULL if there is no metadata. + /** Metadata associated with this node or nullptr if there is no metadata. * Whether any metadata is generated depends on the source file format. See the * @link importer_notes @endlink page for more information on every source file * format. Importers that don't document any metadata don't write any. @@ -149,7 +149,7 @@ struct ASSIMP_API aiNode * of the scene. * * @param name Name to search for - * @return NULL or a valid Node if the search was successful. + * @return nullptr or a valid Node if the search was successful. */ inline const aiNode* FindNode(const aiString& name) const { @@ -344,7 +344,7 @@ struct aiScene #ifdef __cplusplus - //! Default constructor - set everything to 0/NULL + //! Default constructor - set everything to 0/nullptr ASSIMP_API aiScene(); //! Destructor @@ -353,33 +353,33 @@ struct aiScene //! Check whether the scene contains meshes //! Unless no special scene flags are set this will always be true. inline bool HasMeshes() const { - return mMeshes != NULL && mNumMeshes > 0; + return mMeshes != nullptr && mNumMeshes > 0; } //! Check whether the scene contains materials //! Unless no special scene flags are set this will always be true. inline bool HasMaterials() const { - return mMaterials != NULL && mNumMaterials > 0; + return mMaterials != nullptr && mNumMaterials > 0; } //! Check whether the scene contains lights inline bool HasLights() const { - return mLights != NULL && mNumLights > 0; + return mLights != nullptr && mNumLights > 0; } //! Check whether the scene contains textures inline bool HasTextures() const { - return mTextures != NULL && mNumTextures > 0; + return mTextures != nullptr && mNumTextures > 0; } //! Check whether the scene contains cameras inline bool HasCameras() const { - return mCameras != NULL && mNumCameras > 0; + return mCameras != nullptr && mNumCameras > 0; } //! Check whether the scene contains animations inline bool HasAnimations() const { - return mAnimations != NULL && mNumAnimations > 0; + return mAnimations != nullptr && mNumAnimations > 0; } //! Returns a short filename from a full path From 212bcfe75c15bc21a14a715fa4e06886a41d944a Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 12:27:54 +0000 Subject: [PATCH 10/14] Test disable cache --- code/PostProcessing/ArmaturePopulate.cpp | 3 ++- test/unit/utArmaturePopulate.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp index c677b193a..5de2a3870 100644 --- a/code/PostProcessing/ArmaturePopulate.cpp +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace Assimp { @@ -76,7 +77,7 @@ void ArmaturePopulate::Execute(aiScene *out) { BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); ASSIMP_LOG_DEBUG_F("Bone stack size: %ld\n", bone_stack.size()); - + std::cout << "post process for armature population has run!" << std::endl; for (std::pair kvp : bone_stack) { aiBone *bone = kvp.first; aiNode *bone_node = kvp.second; diff --git a/test/unit/utArmaturePopulate.cpp b/test/unit/utArmaturePopulate.cpp index 4aaf8aa7c..835d3fdb9 100644 --- a/test/unit/utArmaturePopulate.cpp +++ b/test/unit/utArmaturePopulate.cpp @@ -64,7 +64,7 @@ class utArmaturePopulate : public ::testing::Test { TEST_F( utArmaturePopulate, importCheckForArmatureTest) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", aiProcess_ValidateDataStructure | aiProcess_PopulateArmatureData); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", aiProcess_PopulateArmatureData); EXPECT_NE( nullptr, scene ); EXPECT_EQ(scene->mNumMeshes, 1u); aiMesh* mesh = scene->mMeshes[0]; From a9a0d4d29b8875f37da55b8e8d621732be24e471 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 13:53:11 +0000 Subject: [PATCH 11/14] Tidying order of function calls and fixed debug statements --- code/PostProcessing/ArmaturePopulate.cpp | 176 ++++++++++++----------- 1 file changed, 94 insertions(+), 82 deletions(-) diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp index 5de2a3870..75daeb6b5 100644 --- a/code/PostProcessing/ArmaturePopulate.cpp +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -76,12 +76,12 @@ void ArmaturePopulate::Execute(aiScene *out) { BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); - ASSIMP_LOG_DEBUG_F("Bone stack size: %ld\n", bone_stack.size()); - std::cout << "post process for armature population has run!" << std::endl; + ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size()); + for (std::pair kvp : bone_stack) { aiBone *bone = kvp.first; aiNode *bone_node = kvp.second; - ASSIMP_LOG_DEBUG_F("active node lookup: %s\n", bone->mName.C_Str()); + ASSIMP_LOG_DEBUG_F("active node lookup: ", bone->mName.C_Str()); // lcl transform grab - done in generate_nodes :) // bone->mOffsetMatrix = bone_node->mTransformation; @@ -98,82 +98,6 @@ void ArmaturePopulate::Execute(aiScene *out) { } } -/* Returns the armature root node */ -/* This is required to be detected for a bone initially, it will recurse up - * until it cannot find another bone and return the node No known failure - * points. (yet) - */ -aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, - std::vector &bone_list) { - while (bone_node) { - if (!IsBoneNode(bone_node->mName, bone_list)) { - ASSIMP_LOG_DEBUG_F("Found valid armature: %s\n", bone_node->mName.C_Str()); - return bone_node; - } - - bone_node = bone_node->mParent; - } - - ASSIMP_LOG_WARN("GetArmatureRoot() can't find armature!"); - - return nullptr; -} - -/* Simple IsBoneNode check if this could be a bone */ -bool ArmaturePopulate::IsBoneNode(const aiString &bone_name, - std::vector &bones) { - for (aiBone *bone : bones) { - if (bone->mName == bone_name) { - return true; - } - } - - return false; -} - -/* Pop this node by name from the stack if found */ -/* Used in multiple armature situations with duplicate node / bone names */ -/* Known flaw: cannot have nodes with bone names, will be fixed in later release - */ -/* (serious to be fixed) Known flaw: nodes which have more than one bone could - * be prematurely dropped from stack */ -aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, - std::vector &nodes) { - std::vector::iterator iter; - aiNode *found = nullptr; - for (iter = nodes.begin(); iter < nodes.end(); ++iter) { - aiNode *element = *iter; - ai_assert(element); - // node valid and node name matches - if (element->mName == node_name) { - found = element; - break; - } - } - - if (found != nullptr) { - // now pop the element from the node list - nodes.erase(iter); - - return found; - } - return nullptr; -} - -/* Prepare flat node list which can be used for non recursive lookups later */ -void ArmaturePopulate::BuildNodeList(const aiNode *current_node, - std::vector &nodes) { - ai_assert(current_node); - - for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { - aiNode *child = current_node->mChildren[nodeId]; - ai_assert(child); - - nodes.push_back(child); - - BuildNodeList(child, nodes); - } -} /* Reprocess all nodes to calculate bone transforms properly based on the REAL * mOffsetMatrix not the local. */ @@ -217,6 +141,21 @@ void ArmaturePopulate::BuildBoneList(aiNode *current_node, } } +/* Prepare flat node list which can be used for non recursive lookups later */ +void ArmaturePopulate::BuildNodeList(const aiNode *current_node, + std::vector &nodes) { + ai_assert(current_node); + + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + nodes.push_back(child); + + BuildNodeList(child, nodes); + } +} + /* A bone stack allows us to have multiple armatures, with the same bone names * A bone stack allows us also to retrieve bones true transform even with * duplicate names :) @@ -237,20 +176,93 @@ void ArmaturePopulate::BuildBoneStack(aiNode *current_node, if (node == nullptr) { node_stack.clear(); BuildNodeList(root_node, node_stack); - ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element %s\n", bone->mName.C_Str()); + ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str()); node = GetNodeFromStack(bone->mName, node_stack); if (!node) { - ASSIMP_LOG_ERROR("serious import issue armature failed to be detected"); + ASSIMP_LOG_ERROR("serious import issue node for bone was not detected"); continue; } } - ASSIMP_LOG_DEBUG_F("Successfully added bone to stack and have valid armature: %s\n", bone->mName.C_Str()); + ASSIMP_LOG_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str()); bone_stack.insert(std::pair(bone, node)); } } + +/* Returns the armature root node */ +/* This is required to be detected for a bone initially, it will recurse up + * until it cannot find another bone and return the node No known failure + * points. (yet) + */ +aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, + std::vector &bone_list) { + while (bone_node) { + if (!IsBoneNode(bone_node->mName, bone_list)) { + ASSIMP_LOG_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); + return bone_node; + } + + bone_node = bone_node->mParent; + } + + ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!"); + + return nullptr; +} + + + +/* Simple IsBoneNode check if this could be a bone */ +bool ArmaturePopulate::IsBoneNode(const aiString &bone_name, + std::vector &bones) { + for (aiBone *bone : bones) { + if (bone->mName == bone_name) { + return true; + } + } + + return false; +} + +/* Pop this node by name from the stack if found */ +/* Used in multiple armature situations with duplicate node / bone names */ +/* Known flaw: cannot have nodes with bone names, will be fixed in later release + */ +/* (serious to be fixed) Known flaw: nodes which have more than one bone could + * be prematurely dropped from stack */ +aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, + std::vector &nodes) { + std::vector::iterator iter; + aiNode *found = nullptr; + for (iter = nodes.begin(); iter < nodes.end(); ++iter) { + aiNode *element = *iter; + ai_assert(element); + // node valid and node name matches + if (element->mName == node_name) { + found = element; + break; + } + } + + if (found != nullptr) { + ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str()); + // now pop the element from the node list + nodes.erase(iter); + + return found; + } + + // unique names can cause this problem + ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!"); + + return nullptr; +} + + + + } // Namespace Assimp From 5155efe888530daaee69ec098f9627f7a14a9e68 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 13:53:43 +0000 Subject: [PATCH 12/14] Fixed bitmask issue We are approaching the limit for the number of post processes --- include/assimp/postprocess.h | 23 +++++++++++++---------- test/unit/utArmaturePopulate.cpp | 3 ++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index 2af48aedd..4b6732e80 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -320,6 +320,19 @@ enum aiPostProcessSteps */ aiProcess_FixInfacingNormals = 0x2000, + + + // ------------------------------------------------------------------------- + /** + * This step generically populates aiBone->mArmature and aiBone->mNode generically + * The point of these is it saves you later having to calculate these elements + * This is useful when handling rest information or skin information + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the + * hard work for you :) + */ + aiProcess_PopulateArmatureData = 0x4000, + // ------------------------------------------------------------------------- /**
This step splits meshes with more than one primitive type in * homogeneous sub-meshes. @@ -538,16 +551,6 @@ enum aiPostProcessSteps aiProcess_Debone = 0x4000000, - // ------------------------------------------------------------------------- - /** - * This step generically populates aiBone->mArmature and aiBone->mNode generically - * The point of these is it saves you later having to calculate these elements - * This is useful when handling rest information or skin information - * If you have multiple armatures on your models we strongly recommend enabling this - * Instead of writing your own multi-root, multi-armature lookups we have done the - * hard work for you :) - */ - aiProcess_PopulateArmatureData = 0x5000000, // ------------------------------------------------------------------------- /**
This step will perform a global scale of the model. diff --git a/test/unit/utArmaturePopulate.cpp b/test/unit/utArmaturePopulate.cpp index 835d3fdb9..8eb577d61 100644 --- a/test/unit/utArmaturePopulate.cpp +++ b/test/unit/utArmaturePopulate.cpp @@ -64,7 +64,8 @@ class utArmaturePopulate : public ::testing::Test { TEST_F( utArmaturePopulate, importCheckForArmatureTest) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", aiProcess_PopulateArmatureData); + unsigned int mask = aiProcess_PopulateArmatureData | aiProcess_ValidateDataStructure; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", mask); EXPECT_NE( nullptr, scene ); EXPECT_EQ(scene->mNumMeshes, 1u); aiMesh* mesh = scene->mMeshes[0]; From d7d79db0ac2a55f3138bcc1bc1799f00646f0b61 Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Sun, 27 Oct 2019 12:57:47 +0000 Subject: [PATCH 13/14] Tests should always debug log --- test/unit/Main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/Main.cpp b/test/unit/Main.cpp index 5ba5c487d..333fd655d 100644 --- a/test/unit/Main.cpp +++ b/test/unit/Main.cpp @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) // create a logger from both CPP Assimp::DefaultLogger::create("AssimpLog_Cpp.txt",Assimp::Logger::VERBOSE, - aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE); + aiDefaultLogStream_STDOUT | aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE); // .. and C. They should smoothly work together aiEnableVerboseLogging(AI_TRUE); From 02a63f8b1070e71bc9ee67d611d18078966451aa Mon Sep 17 00:00:00 2001 From: RevoluPowered Date: Tue, 29 Oct 2019 20:21:16 +0000 Subject: [PATCH 14/14] Fixed template being used in file --- code/PostProcessing/ArmaturePopulate.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h index 30bfc7509..aa1ad7c80 100644 --- a/code/PostProcessing/ArmaturePopulate.h +++ b/code/PostProcessing/ArmaturePopulate.h @@ -54,12 +54,17 @@ struct aiBone; namespace Assimp { // --------------------------------------------------------------------------- -/** ScaleProcess: Class to rescale the whole model. - * Now rescales animations, bones, and blend shapes properly. - * Please note this will not write to 'scale' transform it will rewrite mesh - * and matrixes so that your scale values - * from your model package are preserved, so this is completely intentional - * bugs should be reported as soon as they are found. +/** Armature Populate: This is a post process designed + * To save you time when importing models into your game engines + * This was originally designed only for fbx but will work with other formats + * it is intended to auto populate aiBone data with armature and the aiNode + * This is very useful when dealing with skinned meshes + * or when dealing with many different skeletons + * It's off by default but recommend that you try it and use it + * It should reduce down any glue code you have in your + * importers + * You can contact RevoluPowered + * For more info about this */ class ASSIMP_API ArmaturePopulate : public BaseProcess { public: