diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index 32872108b..c79d43cbf 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1435,6 +1435,16 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co return static_cast(mMeshes.size() - 1); } +void ConvertWeightsToSkeleton(const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent, unsigned int materialIndex, + std::vector *outputVertStartIndices) { + ai_assert(geo.DeformerSkin() != nullptr); + + const Skin &sk = *geo.DeformerSkin(); + for (auto &cluster : sk.Clusters()) { + cluster->Transform(); + } +} + void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent, unsigned int materialIndex, @@ -1529,12 +1539,6 @@ void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo, } } -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, std::vector &out_indices, std::vector &index_out_indices, std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, 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/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/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 87b9de262..cd334f786 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,17 +942,92 @@ struct aiMesh { #endif // __cplusplus }; -struct aiSkeleton { - C_STRUCT aiString mName; - unsigned int mNumWeights; +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 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 - aiSkeleton() AI_NO_EXCEPT : mName(), mNumWeights(0), mWeights(nullptr) { + aiSkeletonBone() : + mParent(-1), + mArmature(nullptr), + mNode(nullptr), + mNumnWeights(0), + mWeights(nullptr), + mOffsetMatrix(), + mLocalMatrix() { // empty } - ~aiSkeleton() { + ~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 }; diff --git a/include/assimp/scene.h b/include/assimp/scene.h index f4c6d7960..2dac27da2 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -343,6 +343,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 +393,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, '/');