diff --git a/code/AssetLib/FBX/FBXAnimation.cpp b/code/AssetLib/FBX/FBXAnimation.cpp index 2fa3b7b05..af92ebe51 100644 --- a/code/AssetLib/FBX/FBXAnimation.cpp +++ b/code/AssetLib/FBX/FBXAnimation.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -87,11 +86,6 @@ AnimationCurve::AnimationCurve(uint64_t id, const Element &element, const std::s } } -// ------------------------------------------------------------------------------------------------ -AnimationCurve::~AnimationCurve() { - // empty -} - // ------------------------------------------------------------------------------------------------ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, const std::string &name, const Document &doc, const char *const *target_prop_whitelist /*= nullptr*/, @@ -147,11 +141,6 @@ AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element &element, cons props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false); } -// ------------------------------------------------------------------------------------------------ -AnimationCurveNode::~AnimationCurveNode() { - // empty -} - // ------------------------------------------------------------------------------------------------ const AnimationCurveMap &AnimationCurveNode::Curves() const { if (curves.empty()) { @@ -193,11 +182,6 @@ AnimationLayer::AnimationLayer(uint64_t id, const Element &element, const std::s props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true); } -// ------------------------------------------------------------------------------------------------ -AnimationLayer::~AnimationLayer() { - // empty -} - // ------------------------------------------------------------------------------------------------ AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist /*= nullptr*/, size_t whitelist_size /*= 0*/) const { @@ -279,11 +263,6 @@ AnimationStack::AnimationStack(uint64_t id, const Element &element, const std::s } } -// ------------------------------------------------------------------------------------------------ -AnimationStack::~AnimationStack() { - // empty -} - } // namespace FBX } // namespace Assimp diff --git a/code/AssetLib/FBX/FBXCommon.h b/code/AssetLib/FBX/FBXCommon.h index ec7459c9e..c592c5649 100644 --- a/code/AssetLib/FBX/FBXCommon.h +++ b/code/AssetLib/FBX/FBXCommon.h @@ -50,7 +50,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace FBX { -const std::string NULL_RECORD = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit +static constexpr size_t NumNullRecords = 25; +const char NULL_RECORD[NumNullRecords] = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?) diff --git a/code/AssetLib/FBX/FBXCompileConfig.h b/code/AssetLib/FBX/FBXCompileConfig.h index 75787d303..927a3c5f6 100644 --- a/code/AssetLib/FBX/FBXCompileConfig.h +++ b/code/AssetLib/FBX/FBXCompileConfig.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index 72c78dd6d..83d95dada 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -65,12 +65,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include #include -#include -#include namespace Assimp { namespace FBX { @@ -187,8 +184,7 @@ std::string FBXConverter::MakeUniqueNodeName(const Model *const model, const aiN /// This struct manages nodes which may or may not end up in the node hierarchy. /// When a node becomes a child of another node, that node becomes its owner and mOwnership should be released. -struct FBXConverter::PotentialNode -{ +struct FBXConverter::PotentialNode { PotentialNode() : mOwnership(new aiNode), mNode(mOwnership.get()) {} PotentialNode(const std::string& name) : mOwnership(new aiNode(name)), mNode(mOwnership.get()) {} aiNode* operator->() { return mNode; } @@ -231,7 +227,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) if (nullptr != model) { nodes_chain.clear(); post_nodes_chain.clear(); - + aiMatrix4x4 new_abs_transform = parent->mTransformation; std::string node_name = FixNodeName(model->Name()); // even though there is only a single input node, the design of // assimp (or rather: the complicated transformation chain that @@ -268,7 +264,7 @@ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) } // attach geometry - ConvertModel(*model, nodes_chain.back().mNode, root_node); + ConvertModel(*model, nodes_chain.back().mNode, root_node, new_abs_transform); // check if there will be any child nodes const std::vector &child_conns = doc.GetConnectionsByDestinationSequenced(model->ID(), "Model"); @@ -447,7 +443,7 @@ void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueNam auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count unsigned int &i = it_pair.first->second; while (!it_pair.second) { - i++; + ++i; std::ostringstream ext; ext << name << std::setfill('0') << std::setw(3) << i; uniqueName = ext.str(); @@ -646,7 +642,6 @@ void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D &rot bool FBXConverter::NeedsComplexTransformationChain(const Model &model) { const PropertyTable &props = model.Props(); - bool ok; const auto zero_epsilon = ai_epsilon; const aiVector3D all_ones(1.0f, 1.0f, 1.0f); @@ -660,6 +655,7 @@ bool FBXConverter::NeedsComplexTransformationChain(const Model &model) { bool scale_compare = (comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling); + bool ok = true; const aiVector3D &v = PropertyGet(props, NameTransformationCompProperty(comp), ok); if (ok && scale_compare) { if ((v - all_ones).SquareLength() > zero_epsilon) { @@ -894,18 +890,17 @@ void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) { } } -void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node) { +void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform) { const std::vector &geos = model.GetGeometry(); std::vector meshes; meshes.reserve(geos.size()); for (const Geometry *geo : geos) { - const MeshGeometry *const mesh = dynamic_cast(geo); const LineGeometry *const line = dynamic_cast(geo); if (mesh) { - const std::vector &indices = ConvertMesh(*mesh, model, parent, root_node); + 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, root_node); @@ -926,7 +921,7 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root } std::vector -FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node) { +FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform) { std::vector temp; MeshMap::const_iterator it = meshes_converted.find(&mesh); @@ -949,13 +944,13 @@ FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode * const MatIndexArray::value_type base = mindices[0]; for (MatIndexArray::value_type index : mindices) { if (index != base) { - return ConvertMeshMultiMaterial(mesh, model, parent, root_node); + return ConvertMeshMultiMaterial(mesh, model, absolute_transform, parent, root_node); } } } // faster code-path, just copy the data - temp.push_back(ConvertMeshSingleMaterial(mesh, model, parent, root_node)); + temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node)); return temp; } @@ -1023,7 +1018,35 @@ aiMesh *FBXConverter::SetupEmptyMesh(const Geometry &mesh, aiNode *parent) { return out_mesh; } -unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, +static aiSkeleton *createAiSkeleton(SkeletonBoneContainer &sbc) { + if (sbc.MeshArray.empty() || sbc.SkeletonBoneToMeshLookup.empty()) { + return nullptr; + } + + aiSkeleton *skeleton = new aiSkeleton; + for (auto *mesh : sbc.MeshArray) { + auto it = sbc.SkeletonBoneToMeshLookup.find(mesh); + if (it == sbc.SkeletonBoneToMeshLookup.end()) { + continue; + } + SkeletonBoneArray *ba = it->second; + if (ba == nullptr) { + continue; + } + + skeleton->mNumBones = static_cast(ba->size()); + skeleton->mBones = new aiSkeletonBone*[skeleton->mNumBones]; + size_t index = 0; + for (auto bone : (* ba)) { + skeleton->mBones[index] = bone; + ++index; + } + } + + return skeleton; +} + +unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *) { const MatIndexArray &mindices = mesh.GetMaterialIndices(); aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent); @@ -1142,8 +1165,15 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); } - if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) { - ConvertWeights(out_mesh, mesh, parent, NO_MATERIAL_SEPARATION, nullptr); + if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr && !doc.Settings().useSkeleton) { + ConvertWeights(out_mesh, mesh, absolute_transform, parent, NO_MATERIAL_SEPARATION, nullptr); + } else if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr && doc.Settings().useSkeleton) { + SkeletonBoneContainer sbc; + ConvertWeightsToSkeleton(out_mesh, mesh, absolute_transform, parent, NO_MATERIAL_SEPARATION, nullptr, sbc); + aiSkeleton *skeleton = createAiSkeleton(sbc); + if (skeleton != nullptr) { + mSkeletons.emplace_back(skeleton); + } } std::vector animMeshes; @@ -1190,7 +1220,7 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c } std::vector -FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, +FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *root_node) { const MatIndexArray &mindices = mesh.GetMaterialIndices(); ai_assert(mindices.size()); @@ -1201,7 +1231,7 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo for (MatIndexArray::value_type index : mindices) { if (had.find(index) == had.end()) { - indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node)); + indices.push_back(ConvertMeshMultiMaterial(mesh, model, absolute_transform, index, parent, root_node)); had.insert(index); } } @@ -1209,9 +1239,8 @@ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &mo return indices; } -unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, - MatIndexArray::value_type index, - aiNode *parent, aiNode *) { +unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, + MatIndexArray::value_type index, aiNode *parent, aiNode *) { aiMesh *const out_mesh = SetupEmptyMesh(mesh, parent); const MatIndexArray &mindices = mesh.GetMaterialIndices(); @@ -1374,7 +1403,7 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co ConvertMaterialForMesh(out_mesh, model, mesh, index); if (process_weights) { - ConvertWeights(out_mesh, mesh, parent, index, &reverseMapping); + ConvertWeights(out_mesh, mesh, absolute_transform, parent, index, &reverseMapping); } std::vector animMeshes; @@ -1424,19 +1453,47 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co return static_cast(mMeshes.size() - 1); } -void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, +static void copyBoneToSkeletonBone(aiMesh *mesh, aiBone *bone, aiSkeletonBone *skeletonBone ) { + skeletonBone->mNumnWeights = bone->mNumWeights; + skeletonBone->mWeights = bone->mWeights; + skeletonBone->mOffsetMatrix = bone->mOffsetMatrix; + skeletonBone->mMeshId = mesh; + skeletonBone->mNode = bone->mNode; + skeletonBone->mParent = -1; +} + +void FBXConverter::ConvertWeightsToSkeleton(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent, unsigned int materialIndex, + std::vector *outputVertStartIndices, SkeletonBoneContainer &skeletonContainer) { + + if (skeletonContainer.SkeletonBoneToMeshLookup.find(out) != skeletonContainer.SkeletonBoneToMeshLookup.end()) { + return; + } + + ConvertWeights(out, geo, absolute_transform, parent, materialIndex, outputVertStartIndices); + skeletonContainer.MeshArray.emplace_back(out); + SkeletonBoneArray *ba = new SkeletonBoneArray; + for (size_t i = 0; i < out->mNumBones; ++i) { + aiBone *bone = out->mBones[i]; + if (bone == nullptr) { + continue; + } + aiSkeletonBone *skeletonBone = new aiSkeletonBone; + copyBoneToSkeletonBone(out, bone, skeletonBone); + ba->emplace_back(skeletonBone); + } + skeletonContainer.SkeletonBoneToMeshLookup[out] = ba; +} + +void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent, unsigned int materialIndex, std::vector *outputVertStartIndices) { ai_assert(geo.DeformerSkin()); - std::vector out_indices; - std::vector index_out_indices; - std::vector count_out_indices; + std::vector out_indices, index_out_indices, count_out_indices; const Skin &sk = *geo.DeformerSkin(); - std::vector bones; - + std::vector bones; const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; ai_assert(no_mat_check || outputVertStartIndices); @@ -1496,7 +1553,7 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, // XXX this could be heavily simplified by collecting the bone // data in a single step. ConvertCluster(bones, cluster, out_indices, index_out_indices, - count_out_indices, parent); + count_out_indices, absolute_transform, parent); } bone_map.clear(); @@ -1509,25 +1566,20 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, 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); } -const aiNode *GetNodeByName(aiNode *current_node) { - aiNode *iter = current_node; - //printf("Child count: %d", iter->mNumChildren); - return iter; -} - -void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const Cluster *cl, +void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const Cluster *cluster, std::vector &out_indices, std::vector &index_out_indices, - std::vector &count_out_indices, aiNode *) { - ai_assert(cl); // make sure cluster valid - std::string deformer_name = cl->TargetNode()->Name(); + std::vector &count_out_indices, const aiMatrix4x4 & /* absolute_transform*/, + aiNode *) { + ai_assert(cluster != nullptr); // make sure cluster valid + + std::string deformer_name = cluster->TargetNode()->Name(); aiString bone_name = aiString(FixNodeName(deformer_name)); aiBone *bone = nullptr; @@ -1540,10 +1592,10 @@ void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const bone = new aiBone(); bone->mName = bone_name; - bone->mOffsetMatrix = cl->Transform(); + bone->mOffsetMatrix = cluster->Transform(); // store local transform link for post processing /* - bone->mOffsetMatrix = cl->TransformLink(); + bone->mOffsetMatrix = cluster->TransformLink(); bone->mOffsetMatrix.Inverse(); aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform; @@ -1560,7 +1612,7 @@ void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; const size_t no_index_sentinel = std::numeric_limits::max(); - const WeightArray &weights = cl->GetWeights(); + const WeightArray &weights = cluster->GetWeights(); const size_t c = index_out_indices.size(); for (size_t i = 0; i < c; ++i) { @@ -2605,7 +2657,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) { meshMorphAnim->mNumKeys = numKeys; meshMorphAnim->mKeys = new aiMeshMorphKey[numKeys]; unsigned int j = 0; - for (auto animIt : *animData) { + for (auto &animIt : *animData) { morphKeyData *keyData = animIt.second; unsigned int numValuesAndWeights = static_cast(keyData->values.size()); meshMorphAnim->mKeys[j].mNumValuesAndWeights = numValuesAndWeights; @@ -3625,6 +3677,12 @@ void FBXConverter::TransferDataToScene() { std::swap_ranges(textures.begin(), textures.end(), mSceneOut->mTextures); } + + if (!mSkeletons.empty()) { + mSceneOut->mSkeletons = new aiSkeleton *[mSkeletons.size()]; + mSceneOut->mNumSkeletons = static_cast(mSkeletons.size()); + std::swap_ranges(mSkeletons.begin(), mSkeletons.end(), mSceneOut->mSkeletons); + } } void FBXConverter::ConvertOrphanedEmbeddedTextures() { diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h index 5559f0b7c..41acb6ffe 100644 --- a/code/AssetLib/FBX/FBXConverter.h +++ b/code/AssetLib/FBX/FBXConverter.h @@ -75,7 +75,18 @@ typedef std::map morphAnimData; namespace Assimp { namespace FBX { +class MeshGeometry; + +using SkeletonBoneArray = std::vector; +using SkeletonBoneToMesh = std::map; + +struct SkeletonBoneContainer { + std::vector MeshArray; + SkeletonBoneToMesh SkeletonBoneToMeshLookup; +}; + class Document; + /** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated @@ -180,12 +191,12 @@ private: void SetupNodeMetadata(const Model& model, aiNode& nd); // ------------------------------------------------------------------------------------------------ - void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node); + 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, aiNode *parent, aiNode *root_node); + ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ std::vector ConvertLine(const LineGeometry& line, aiNode *root_node); @@ -194,15 +205,15 @@ private: aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent); // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, + 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, aiNode *parent, aiNode *root_node); + ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index, + unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, const aiMatrix4x4 &absolute_transform, MatIndexArray::value_type index, aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ @@ -216,14 +227,19 @@ 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 MeshGeometry &geo, aiNode *parent = nullptr, + void ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent = nullptr, unsigned int materialIndex = NO_MATERIAL_SEPARATION, std::vector *outputVertStartIndices = nullptr); + // ------------------------------------------------------------------------------------------------ + void ConvertWeightsToSkeleton(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, + aiNode *parent, unsigned int materialIndex, std::vector *outputVertStartIndices, + SkeletonBoneContainer &skeletonContainer); + // ------------------------------------------------------------------------------------------------ void ConvertCluster(std::vector &local_mesh_bones, const Cluster *cl, std::vector &out_indices, std::vector &index_out_indices, - std::vector &count_out_indices, aiNode *parent ); + std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, aiNode *parent); // ------------------------------------------------------------------------------------------------ void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, @@ -296,7 +312,8 @@ private: void ConvertAnimationStack(const AnimationStack& st); // ------------------------------------------------------------------------------------------------ - void ProcessMorphAnimDatas(std::map* morphAnimDatas, const BlendShapeChannel* bsc, const AnimationCurveNode* node); + void ProcessMorphAnimDatas(std::map* morphAnimDatas, + const BlendShapeChannel* bsc, const AnimationCurveNode* node); // ------------------------------------------------------------------------------------------------ void GenerateNodeAnimations(std::vector& node_anims, @@ -445,6 +462,7 @@ private: double anim_fps; + std::vector mSkeletons; aiScene* const mSceneOut; const FBX::Document& doc; bool mRemoveEmptyBones; diff --git a/code/AssetLib/FBX/FBXDeformer.cpp b/code/AssetLib/FBX/FBXDeformer.cpp index ba245ed6d..ed6330666 100644 --- a/code/AssetLib/FBX/FBXDeformer.cpp +++ b/code/AssetLib/FBX/FBXDeformer.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -58,16 +57,14 @@ namespace FBX { using namespace Util; // ------------------------------------------------------------------------------------------------ -Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : Object(id,element,name) -{ +Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name) : + Object(id,element,name) { const Scope& sc = GetRequiredScope(element); const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true); } - // ------------------------------------------------------------------------------------------------ Deformer::~Deformer() { diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index b49ee625a..79a7509f4 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -67,7 +67,7 @@ namespace FBX { using namespace Util; // ------------------------------------------------------------------------------------------------ -LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) : +LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) : doc(doc), element(element), id(id), flags() { // empty } @@ -544,7 +544,7 @@ std::vector Document::GetConnectionsSequenced(uint64_t id, bo ai_assert( count != 0 ); ai_assert( count <= MAX_CLASSNAMES); - size_t lengths[MAX_CLASSNAMES]; + size_t lengths[MAX_CLASSNAMES] = {}; const size_t c = count; for (size_t i = 0; i < c; ++i) { diff --git a/code/AssetLib/FBX/FBXDocument.h b/code/AssetLib/FBX/FBXDocument.h index c61a47410..c362e6475 100644 --- a/code/AssetLib/FBX/FBXDocument.h +++ b/code/AssetLib/FBX/FBXDocument.h @@ -164,7 +164,7 @@ class NodeAttribute : public Object { public: NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~NodeAttribute(); + virtual ~NodeAttribute() = default; const PropertyTable& Props() const { ai_assert(props.get()); @@ -180,7 +180,7 @@ class CameraSwitcher : public NodeAttribute { public: CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~CameraSwitcher(); + virtual ~CameraSwitcher() = default; int CameraID() const { return cameraId; @@ -225,7 +225,7 @@ class Camera : public NodeAttribute { public: Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Camera(); + virtual ~Camera() = default; fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0)) fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0)) @@ -250,21 +250,21 @@ public: class Null : public NodeAttribute { public: Null(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Null(); + virtual ~Null() = default; }; /** DOM base class for FBX limb node markers attached to a node */ class LimbNode : public NodeAttribute { public: LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~LimbNode(); + virtual ~LimbNode() = default; }; /** DOM base class for FBX lights attached to a node */ class Light : public NodeAttribute { public: Light(uint64_t id, const Element& element, const Document& doc, const std::string& name); - virtual ~Light(); + virtual ~Light() = default; enum Type { Type_Point, @@ -690,7 +690,7 @@ using KeyValueList = std::vector; class AnimationCurve : public Object { public: AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc); - virtual ~AnimationCurve(); + virtual ~AnimationCurve() = default; /** get list of keyframe positions (time). * Invariant: |GetKeys()| > 0 */ @@ -731,7 +731,7 @@ public: AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc, const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0); - virtual ~AnimationCurveNode(); + virtual ~AnimationCurveNode() = default; const PropertyTable& Props() const { ai_assert(props.get()); @@ -776,7 +776,7 @@ using AnimationCurveNodeList = std::vector; class AnimationLayer : public Object { public: AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc); - virtual ~AnimationLayer(); + virtual ~AnimationLayer() = default; const PropertyTable& Props() const { ai_assert(props.get()); @@ -799,7 +799,7 @@ using AnimationLayerList = std::vector; class AnimationStack : public Object { public: AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc); - virtual ~AnimationStack(); + virtual ~AnimationStack() = default; fbx_simple_property(LocalStart, int64_t, 0L) fbx_simple_property(LocalStop, int64_t, 0L) diff --git a/code/AssetLib/FBX/FBXDocumentUtil.cpp b/code/AssetLib/FBX/FBXDocumentUtil.cpp index 68185e564..c41eb2747 100644 --- a/code/AssetLib/FBX/FBXDocumentUtil.cpp +++ b/code/AssetLib/FBX/FBXDocumentUtil.cpp @@ -59,14 +59,12 @@ namespace Util { // ------------------------------------------------------------------------------------------------ // signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError. -void DOMError(const std::string& message, const Token& token) -{ +void DOMError(const std::string& message, const Token& token) { throw DeadlyImportError("FBX-DOM", Util::GetTokenText(&token), message); } // ------------------------------------------------------------------------------------------------ -void DOMError(const std::string& message, const Element* element /*= nullptr*/) -{ +void DOMError(const std::string& message, const Element* element /*= nullptr*/) { if(element) { DOMError(message,element->KeyToken()); } @@ -76,8 +74,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/) // ------------------------------------------------------------------------------------------------ // print warning, do return -void DOMWarning(const std::string& message, const Token& token) -{ +void DOMWarning(const std::string& message, const Token& token) { if(DefaultLogger::get()) { ASSIMP_LOG_WARN("FBX-DOM", Util::GetTokenText(&token), message); } diff --git a/code/AssetLib/FBX/FBXDocumentUtil.h b/code/AssetLib/FBX/FBXDocumentUtil.h index 2d76ee031..d32c12e1a 100644 --- a/code/AssetLib/FBX/FBXDocumentUtil.h +++ b/code/AssetLib/FBX/FBXDocumentUtil.h @@ -74,13 +74,11 @@ std::shared_ptr GetPropertyTable(const Document& doc, // ------------------------------------------------------------------------------------------------ template -inline -const T* ProcessSimpleConnection(const Connection& con, - bool is_object_property_conn, - const char* name, - const Element& element, - const char** propNameOut = nullptr) -{ +inline const T* ProcessSimpleConnection(const Connection& con, + bool is_object_property_conn, + const char* name, + const Element& element, + const char** propNameOut = nullptr) { if (is_object_property_conn && !con.PropertyName().length()) { DOMWarning("expected incoming " + std::string(name) + " link to be an object-object connection, ignoring", diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 695672883..563ac68f0 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -255,7 +255,7 @@ void FBXExporter::WriteBinaryHeader() void FBXExporter::WriteBinaryFooter() { - outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); + outfile->Write(NULL_RECORD, NumNullRecords, 1); outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); diff --git a/code/AssetLib/FBX/FBXImportSettings.h b/code/AssetLib/FBX/FBXImportSettings.h index 90e64bf04..698901180 100644 --- a/code/AssetLib/FBX/FBXImportSettings.h +++ b/code/AssetLib/FBX/FBXImportSettings.h @@ -60,6 +60,7 @@ struct ImportSettings { readLights(true), readAnimations(true), readWeights(true), + useSkeleton(false), preservePivots(true), optimizeEmptyAnimationCurves(true), useLegacyEmbeddedTextureNaming(false), @@ -112,6 +113,11 @@ struct ImportSettings { * Default value is true. */ bool readWeights; + /** will convert all animation data into a skeleton (experimental) + * Default value is false. + */ + bool useSkeleton; + /** preserve transformation pivots and offsets. Since these can * not directly be represented in assimp, additional dummy * nodes will be generated. Note that settings this to false diff --git a/code/AssetLib/FBX/FBXImporter.cpp b/code/AssetLib/FBX/FBXImporter.cpp index 0f63acc8f..7ff194905 100644 --- a/code/AssetLib/FBX/FBXImporter.cpp +++ b/code/AssetLib/FBX/FBXImporter.cpp @@ -90,12 +90,9 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by #Importer -FBXImporter::FBXImporter() { -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -FBXImporter::~FBXImporter() { +FBXImporter::FBXImporter() : + mSettings() { + // empty } // ------------------------------------------------------------------------------------------------ @@ -115,20 +112,21 @@ const aiImporterDesc *FBXImporter::GetInfo() const { // ------------------------------------------------------------------------------------------------ // 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.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, 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); + mSettings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); + mSettings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); + mSettings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); + mSettings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); + mSettings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); + mSettings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); + mSettings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); + mSettings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true); + mSettings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); + mSettings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); + mSettings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); + mSettings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); + mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); + mSettings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); + mSettings.useSkeleton = pImp->GetPropertyBool(AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER, false); } // ------------------------------------------------------------------------------------------------ @@ -155,7 +153,7 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy contents[contents.size() - 1] = 0; const char *const begin = &*contents.begin(); - // broadphase tokenizing pass in which we identify the core + // broad-phase tokenized pass in which we identify the core // syntax elements of FBX (brackets, commas, key:value mappings) TokenList tokens; try { @@ -173,15 +171,14 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy Parser parser(tokens, is_binary); // take the raw parse-tree and convert it to a FBX DOM - Document doc(parser, settings); + Document doc(parser, mSettings); // convert the FBX DOM to aiScene - ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); + ConvertToAssimpScene(pScene, doc, mSettings.removeEmptyBones); // size relative to cm float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); - if (size_relative_to_cm == 0.0) - { + if (size_relative_to_cm == 0.0) { // BaseImporter later asserts that fileScale is non-zero. ThrowException("The UnitScaleFactor must be non-zero"); } diff --git a/code/AssetLib/FBX/FBXImporter.h b/code/AssetLib/FBX/FBXImporter.h index a212efe11..9acabaddd 100644 --- a/code/AssetLib/FBX/FBXImporter.h +++ b/code/AssetLib/FBX/FBXImporter.h @@ -69,13 +69,14 @@ typedef class basic_formatter, std::allocator // ------------------------------------------------------------------------------------------- class FBXImporter : public BaseImporter, public LogFunctions { public: + /// @brief The class constructor. FBXImporter(); - ~FBXImporter() override; - // -------------------- - bool CanRead(const std::string &pFile, - IOSystem *pIOHandler, - bool checkSig) const override; + /// @brief The class destructor, default implementation. + ~FBXImporter() override = default; + + /// @brief Will check the file for readability. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; protected: // -------------------- @@ -90,7 +91,7 @@ protected: IOSystem *pIOHandler) override; private: - FBX::ImportSettings settings; + FBX::ImportSettings mSettings; }; // !class FBXImporter } // end of namespace Assimp diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 1f92fa1a7..a0fb0e57e 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -54,18 +53,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXImportSettings.h" #include "FBXDocumentUtil.h" - namespace Assimp { namespace FBX { using namespace Util; // ------------------------------------------------------------------------------------------------ -Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) - : Object(id, element, name) - , skin() -{ - const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); +Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) : + Object(id, element, name), skin() { + const std::vector &conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); for(const Connection* con : conns) { const Skin* const sk = ProcessSimpleConnection(*con, false, "Skin -> Geometry", element); if(sk) { @@ -78,12 +74,6 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, } } -// ------------------------------------------------------------------------------------------------ -Geometry::~Geometry() -{ - // empty -} - // ------------------------------------------------------------------------------------------------ const std::vector& Geometry::GetBlendShapes() const { return blendShapes; @@ -183,18 +173,12 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin if(doc.Settings().readAllLayers || index == 0) { const Scope& layer = GetRequiredScope(*(*it).second); ReadLayer(layer); - } - else { + } else { FBXImporter::LogWarn("ignoring additional geometry layers"); } } } -// ------------------------------------------------------------------------------------------------ -MeshGeometry::~MeshGeometry() { - // empty -} - // ------------------------------------------------------------------------------------------------ const std::vector& MeshGeometry::GetVertices() const { return m_vertices; diff --git a/code/AssetLib/FBX/FBXMeshGeometry.h b/code/AssetLib/FBX/FBXMeshGeometry.h index 3cca38aa5..ad24877e4 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.h +++ b/code/AssetLib/FBX/FBXMeshGeometry.h @@ -55,22 +55,25 @@ namespace FBX { /** * DOM base class for all kinds of FBX geometry */ -class Geometry : public Object -{ +class Geometry : public Object { public: + /// @brief The class constructor with all parameters. + /// @param id The id. + /// @param element + /// @param name + /// @param doc Geometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - virtual ~Geometry(); + virtual ~Geometry() = default; - /** Get the Skin attached to this geometry or nullptr */ + /// Get the Skin attached to this geometry or nullptr const Skin* DeformerSkin() const; - /** Get the BlendShape attached to this geometry or nullptr */ + /// Get the BlendShape attached to this geometry or nullptr const std::vector& GetBlendShapes() const; private: const Skin* skin; std::vector blendShapes; - }; typedef std::vector MatIndexArray; @@ -79,14 +82,13 @@ typedef std::vector MatIndexArray; /** * DOM class for FBX geometry of type "Mesh" */ -class MeshGeometry : public Geometry -{ +class MeshGeometry : public Geometry { public: /** The class constructor */ MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); /** The class destructor */ - virtual ~MeshGeometry(); + virtual ~MeshGeometry() = default; /** Get a list of all vertex points, non-unique*/ const std::vector& GetVertices() const; @@ -130,6 +132,7 @@ public: /** Determine the face to which a particular output vertex index belongs. * This mapping is always unique. */ unsigned int FaceForVertexIndex( unsigned int in_index ) const; + private: void ReadLayer( const Scope& layer ); void ReadLayerElement( const Scope& layerElement ); diff --git a/code/AssetLib/FBX/FBXNodeAttribute.cpp b/code/AssetLib/FBX/FBXNodeAttribute.cpp index a144f417a..34fcdcf77 100644 --- a/code/AssetLib/FBX/FBXNodeAttribute.cpp +++ b/code/AssetLib/FBX/FBXNodeAttribute.cpp @@ -57,114 +57,65 @@ namespace FBX { using namespace Util; // ------------------------------------------------------------------------------------------------ -NodeAttribute::NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: Object(id,element,name) -, props() -{ - const Scope& sc = GetRequiredScope(element); +NodeAttribute::NodeAttribute(uint64_t id, const Element &element, const Document &doc, const std::string &name) : + Object(id, element, name), props() { + const Scope &sc = GetRequiredScope(element); - const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2)); + const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2)); // hack on the deriving type but Null/LimbNode attributes are the only case in which // the property table is by design absent and no warning should be generated // for it. const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode"); - props = GetPropertyTable(doc,"NodeAttribute.Fbx" + classname,element,sc, is_null_or_limb); + props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb); } - // ------------------------------------------------------------------------------------------------ -NodeAttribute::~NodeAttribute() -{ - // empty -} +CameraSwitcher::CameraSwitcher(uint64_t id, const Element &element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { + const Scope &sc = GetRequiredScope(element); + const Element *const CameraId = sc["CameraId"]; + const Element *const CameraName = sc["CameraName"]; + const Element *const CameraIndexName = sc["CameraIndexName"]; - -// ------------------------------------------------------------------------------------------------ -CameraSwitcher::CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name) - : NodeAttribute(id,element,doc,name) -{ - const Scope& sc = GetRequiredScope(element); - const Element* const CameraId = sc["CameraId"]; - const Element* const CameraName = sc["CameraName"]; - const Element* const CameraIndexName = sc["CameraIndexName"]; - - if(CameraId) { - cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId,0)); + if (CameraId) { + cameraId = ParseTokenAsInt(GetRequiredToken(*CameraId, 0)); } - if(CameraName) { - cameraName = GetRequiredToken(*CameraName,0).StringContents(); + if (CameraName) { + cameraName = GetRequiredToken(*CameraName, 0).StringContents(); } - if(CameraIndexName && CameraIndexName->Tokens().size()) { - cameraIndexName = GetRequiredToken(*CameraIndexName,0).StringContents(); + if (CameraIndexName && CameraIndexName->Tokens().size()) { + cameraIndexName = GetRequiredToken(*CameraIndexName, 0).StringContents(); } } // ------------------------------------------------------------------------------------------------ -CameraSwitcher::~CameraSwitcher() -{ +Camera::Camera(uint64_t id, const Element &element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { // empty } // ------------------------------------------------------------------------------------------------ -Camera::Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ +Light::Light(uint64_t id, const Element &element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { // empty } // ------------------------------------------------------------------------------------------------ -Camera::~Camera() -{ +Null::Null(uint64_t id, const Element &element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { // empty } // ------------------------------------------------------------------------------------------------ -Light::Light(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ +LimbNode::LimbNode(uint64_t id, const Element &element, const Document &doc, const std::string &name) : + NodeAttribute(id, element, doc, name) { // empty } +} // namespace FBX +} // namespace Assimp -// ------------------------------------------------------------------------------------------------ -Light::~Light() -{ -} - - -// ------------------------------------------------------------------------------------------------ -Null::Null(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ - -} - - -// ------------------------------------------------------------------------------------------------ -Null::~Null() -{ - -} - - -// ------------------------------------------------------------------------------------------------ -LimbNode::LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name) -: NodeAttribute(id,element,doc,name) -{ - -} - - -// ------------------------------------------------------------------------------------------------ -LimbNode::~LimbNode() -{ - -} - -} -} - -#endif +#endif // ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index e20377a3c..488e075dc 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -162,12 +162,6 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token) while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET); } -// ------------------------------------------------------------------------------------------------ -Element::~Element() -{ - // no need to delete tokens, they are owned by the parser -} - // ------------------------------------------------------------------------------------------------ Scope::Scope(Parser& parser,bool topLevel) { @@ -226,12 +220,6 @@ Parser::Parser (const TokenList& tokens, bool is_binary) root.reset(new Scope(*this,true)); } -// ------------------------------------------------------------------------------------------------ -Parser::~Parser() -{ - // empty -} - // ------------------------------------------------------------------------------------------------ TokenPtr Parser::AdvanceToNextToken() { @@ -961,8 +949,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) // ------------------------------------------------------------------------------------------------ // read an array of uints -void ParseVectorDataArray(std::vector& out, const Element& el) -{ +void ParseVectorDataArray(std::vector& out, const Element& el) { out.resize( 0 ); const TokenList& tok = el.Tokens(); if(tok.empty()) { @@ -1186,7 +1173,6 @@ aiMatrix4x4 ReadMatrix(const Element& element) return result; } - // ------------------------------------------------------------------------------------------------ // wrapper around ParseTokenAsString() with ParseError handling std::string ParseTokenAsString(const Token& t) diff --git a/code/AssetLib/FBX/FBXParser.h b/code/AssetLib/FBX/FBXParser.h index 314481e42..6aeedb211 100644 --- a/code/AssetLib/FBX/FBXParser.h +++ b/code/AssetLib/FBX/FBXParser.h @@ -87,7 +87,7 @@ class Element { public: Element(const Token& key_token, Parser& parser); - ~Element(); + ~Element() = default; const Scope* Compound() const { return compound.get(); @@ -160,7 +160,7 @@ public: /** Parse given a token list. Does not take ownership of the tokens - * the objects must persist during the entire parser lifetime */ Parser (const TokenList& tokens,bool is_binary); - ~Parser(); + ~Parser() = default; const Scope& GetRootScope() const { return *root.get(); diff --git a/code/Common/SkeletonMeshBuilder.cpp b/code/Common/SkeletonMeshBuilder.cpp index 5ea30d9c7..d00b96d2d 100644 --- a/code/Common/SkeletonMeshBuilder.cpp +++ b/code/Common/SkeletonMeshBuilder.cpp @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -97,13 +96,14 @@ void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) { const aiMatrix4x4 &childTransform = pNode->mChildren[a]->mTransformation; aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4); ai_real distanceToChild = childpos.Length(); - if (distanceToChild < 0.0001) + if (distanceToChild < ai_epsilon) { continue; + } aiVector3D up = aiVector3D(childpos).Normalize(); - aiVector3D orth(1.0, 0.0, 0.0); - if (std::fabs(orth * up) > 0.99) + if (std::fabs(orth * up) > 0.99) { orth.Set(0.0, 1.0, 0.0); + } aiVector3D front = (up ^ orth).Normalize(); aiVector3D side = (front ^ up).Normalize(); @@ -183,8 +183,9 @@ void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) { // add all the vertices to the bone's influences bone->mNumWeights = numVertices; bone->mWeights = new aiVertexWeight[numVertices]; - for (unsigned int a = 0; a < numVertices; a++) + for (unsigned int a = 0; a < numVertices; ++a) { bone->mWeights[a] = aiVertexWeight(vertexStartIndex + a, 1.0); + } // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding // them to the array, but I'm tired now and I'm annoyed. @@ -194,8 +195,9 @@ void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) { } // and finally recurse into the children list - for (unsigned int a = 0; a < pNode->mNumChildren; a++) + for (unsigned int a = 0; a < pNode->mNumChildren; ++a) { CreateGeometry(pNode->mChildren[a]); + } } // ------------------------------------------------------------------------------------------------ diff --git a/code/Common/Version.cpp b/code/Common/Version.cpp index 86d3f2220..808c3598d 100644 --- a/code/Common/Version.cpp +++ b/code/Common/Version.cpp @@ -135,6 +135,9 @@ ASSIMP_API aiScene::aiScene() : mNumCameras(0), mCameras(nullptr), mMetaData(nullptr), + mName(), + mNumSkeletons(0), + mSkeletons(nullptr), mPrivate(new Assimp::ScenePrivateData()) { // empty } @@ -180,7 +183,8 @@ ASSIMP_API aiScene::~aiScene() { delete[] mCameras; aiMetadata::Dealloc(mMetaData); - mMetaData = nullptr; + delete[] mSkeletons; + delete static_cast(mPrivate); } diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index bf0076572..1ed5a2114 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -691,6 +691,15 @@ enum aiComponent #define AI_CONFIG_FBX_CONVERT_TO_M \ "AI_CONFIG_FBX_CONVERT_TO_M" +// --------------------------------------------------------------------------- +/** @brief Will enable the skeleton structo to store bone data. + * + * This will decouple the bone coupling to the mesh. This feature is + * experimental. + */ +#define AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER \ + "AI_CONFIG_FBX_USE_SKELETON_BONE_CONTAINER" + // --------------------------------------------------------------------------- /** @brief Set the vertex animation keyframe to be imported * diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index 5704e7ca0..ffac27bb0 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -120,7 +120,7 @@ extern "C" { * primitive are actually present in a mesh. The #aiProcess_SortByPType flag * executes a special post-processing algorithm which splits meshes with * *different* primitive types mixed up (e.g. lines and triangles) in several - * 'clean' submeshes. Furthermore there is a configuration option ( + * 'clean' sub-meshes. Furthermore there is a configuration option ( * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove * specific kinds of primitives from the imported scene, completely and forever. * In many cases you'll probably want to set this setting to @@ -269,12 +269,12 @@ struct aiBone { 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 + /// 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 + /// The bone node in the scene - used for skeleton conversion + /// you must enable aiProcess_PopulateArmatureData to populate this C_STRUCT aiNode *mNode; #endif @@ -296,7 +296,7 @@ struct aiBone { #ifdef __cplusplus - //! Default constructor + /// @brief Default constructor aiBone() AI_NO_EXCEPT : mName(), mNumWeights(0), @@ -309,7 +309,7 @@ struct aiBone { // empty } - //! Copy constructor + /// @brief Copy constructor aiBone(const aiBone &other) : mName(other.mName), mNumWeights(other.mNumWeights), @@ -319,14 +319,27 @@ struct aiBone { #endif mWeights(nullptr), mOffsetMatrix(other.mOffsetMatrix) { - if (other.mWeights && other.mNumWeights) { - mWeights = new aiVertexWeight[mNumWeights]; - ::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight)); + copyVertexWeights(other); + } + + void copyVertexWeights( const aiBone &other ) { + if (other.mWeights == nullptr || other.mNumWeights == 0) { + mWeights = nullptr; + mNumWeights = 0; + return; } + + mNumWeights = other.mNumWeights; + if (mWeights) { + delete[] mWeights; + } + + mWeights = new aiVertexWeight[mNumWeights]; + ::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight)); } //! Assignment operator - aiBone &operator=(const aiBone &other) { + aiBone &operator = (const aiBone &other) { if (this == &other) { return *this; } @@ -334,21 +347,13 @@ struct aiBone { mName = other.mName; mNumWeights = other.mNumWeights; mOffsetMatrix = other.mOffsetMatrix; - - if (other.mWeights && other.mNumWeights) { - if (mWeights) { - delete[] mWeights; - } - - mWeights = new aiVertexWeight[mNumWeights]; - ::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight)); - } + copyVertexWeights(other); return *this; } bool operator==(const aiBone &rhs) const { - if (mName != rhs.mName || mNumWeights != rhs.mNumWeights) { + if (mName != rhs.mName || mNumWeights != rhs.mNumWeights ) { return false; } @@ -937,6 +942,100 @@ struct aiMesh { #endif // __cplusplus }; +struct aiSkeletonBone { + /// The parent bone index, is -1 one if this bone represents the root bone. + int mParent; + + +#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 + /// @brief The number of weights + unsigned int mNumnWeights; + + /// The mesh index, which will get influenced by the weight. + C_STRUCT aiMesh *mMeshId; + + /// The influence weights of this bone, by vertex index. + C_STRUCT aiVertexWeight *mWeights; + + /** Matrix that transforms from bone space to mesh space in bind pose. + * + * This matrix describes the position of the mesh + * in the local space of this bone when the skeleton was bound. + * Thus it can be used directly to determine a desired vertex position, + * given the world-space transform of the bone when animated, + * and the position of the vertex in mesh space. + * + * It is sometimes called an inverse-bind matrix, + * or inverse bind pose matrix. + */ + C_STRUCT aiMatrix4x4 mOffsetMatrix; + + /// Matrix that transforms the locale bone in bind pose. + C_STRUCT aiMatrix4x4 mLocalMatrix; + +#ifdef __cplusplus + aiSkeletonBone() : + mParent(-1), + mArmature(nullptr), + mNode(nullptr), + mNumnWeights(0), + mMeshId(nullptr), + mWeights(nullptr), + mOffsetMatrix(), + mLocalMatrix() { + // empty + } + + ~aiSkeletonBone() { + delete[] mWeights; + mWeights = nullptr; + } +#endif // __cplusplus +}; +/** + * @brief + */ +struct aiSkeleton { + /** + * + */ + C_STRUCT aiString mName; + + /** + * + */ + unsigned int mNumBones; + + /** + * + */ + C_STRUCT aiSkeletonBone **mBones; + +#ifdef __cplusplus + /** + * + */ + aiSkeleton() AI_NO_EXCEPT : mName(), mNumBones(0), mBones(nullptr) { + // empty + } + + /** + * + */ + ~aiSkeleton() { + delete[] mBones; + } +#endif // __cplusplus +}; #ifdef __cplusplus } #endif //! extern "C" diff --git a/include/assimp/scene.h b/include/assimp/scene.h index f4c6d7960..f6dfd82b0 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -79,8 +79,7 @@ extern "C" { * the imported scene does consist of only a single root node without children. */ // ------------------------------------------------------------------------------- -struct ASSIMP_API aiNode -{ +struct ASSIMP_API aiNode { /** The name of the node. * * The name might be empty (length of zero) but all nodes which @@ -343,6 +342,16 @@ struct aiScene */ C_STRUCT aiString mName; + /** + * + */ + unsigned int mNumSkeletons; + + /** + * + */ + C_STRUCT aiSkeleton **mSkeletons; + #ifdef __cplusplus //! Default constructor - set everything to 0/nullptr @@ -383,6 +392,10 @@ struct aiScene return mAnimations != nullptr && mNumAnimations > 0; } + bool hasSkeletons() const { + return mSkeletons != nullptr && mNumSkeletons > 0; + } + //! Returns a short filename from a full path static const char* GetShortFilename(const char* filename) { const char* lastSlash = strrchr(filename, '/'); diff --git a/test/models/FBX/animation_with_skeleton.fbx b/test/models/FBX/animation_with_skeleton.fbx new file mode 100644 index 000000000..a7476bc64 Binary files /dev/null and b/test/models/FBX/animation_with_skeleton.fbx differ diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index 9b8f2fa0d..9fa293aaf 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -423,3 +423,10 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsSpecularGloss) { ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1)); } + +TEST_F(utFBXImporterExporter, importSkeletonTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/animation_with_skeleton.fbx", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + ASSERT_TRUE(scene->mRootNode); +}