First concepts

kimkulling/create_skeleton_data_issue_4015
Kim Kulling 2022-04-05 20:07:22 +02:00
parent 7aa87a9765
commit e5747dad9b
8 changed files with 182 additions and 69 deletions

View File

@ -1435,6 +1435,16 @@ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, co
return static_cast<unsigned int>(mMeshes.size() - 1); return static_cast<unsigned int>(mMeshes.size() - 1);
} }
void ConvertWeightsToSkeleton(const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, aiNode *parent, unsigned int materialIndex,
std::vector<unsigned int> *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, void FBXConverter::ConvertWeights(aiMesh *out, const MeshGeometry &geo,
const aiMatrix4x4 &absolute_transform, const aiMatrix4x4 &absolute_transform,
aiNode *parent, unsigned int materialIndex, 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<aiBone *> &local_mesh_bones, const Cluster *cl, void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices, std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform, std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,

View File

@ -60,6 +60,7 @@ struct ImportSettings {
readLights(true), readLights(true),
readAnimations(true), readAnimations(true),
readWeights(true), readWeights(true),
useSkeleton(false),
preservePivots(true), preservePivots(true),
optimizeEmptyAnimationCurves(true), optimizeEmptyAnimationCurves(true),
useLegacyEmbeddedTextureNaming(false), useLegacyEmbeddedTextureNaming(false),
@ -112,6 +113,11 @@ struct ImportSettings {
* Default value is true. */ * Default value is true. */
bool readWeights; 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 /** preserve transformation pivots and offsets. Since these can
* not directly be represented in assimp, additional dummy * not directly be represented in assimp, additional dummy
* nodes will be generated. Note that settings this to false * nodes will be generated. Note that settings this to false

View File

@ -90,12 +90,9 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer // Constructor to be privately used by #Importer
FBXImporter::FBXImporter() { FBXImporter::FBXImporter() :
} mSettings() {
// empty
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FBXImporter::~FBXImporter() {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -115,20 +112,21 @@ const aiImporterDesc *FBXImporter::GetInfo() const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader // Setup configuration properties for the loader
void FBXImporter::SetupProperties(const Importer *pImp) { void FBXImporter::SetupProperties(const Importer *pImp) {
settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); mSettings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); mSettings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); mSettings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); mSettings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); mSettings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); mSettings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); mSettings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
settings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true); mSettings.readWeights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_WEIGHTS, true);
settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); mSettings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); mSettings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); mSettings.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); mSettings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); mSettings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); 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; contents[contents.size() - 1] = 0;
const char *const begin = &*contents.begin(); 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) // syntax elements of FBX (brackets, commas, key:value mappings)
TokenList tokens; TokenList tokens;
try { try {
@ -173,15 +171,14 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
Parser parser(tokens, is_binary); Parser parser(tokens, is_binary);
// take the raw parse-tree and convert it to a FBX DOM // 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 // convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); ConvertToAssimpScene(pScene, doc, mSettings.removeEmptyBones);
// size relative to cm // size relative to cm
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); 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. // BaseImporter later asserts that fileScale is non-zero.
ThrowException("The UnitScaleFactor must be non-zero"); ThrowException("The UnitScaleFactor must be non-zero");
} }

View File

@ -69,13 +69,14 @@ typedef class basic_formatter<char, std::char_traits<char>, std::allocator<char>
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> { class FBXImporter : public BaseImporter, public LogFunctions<FBXImporter> {
public: public:
/// @brief The class constructor.
FBXImporter(); FBXImporter();
~FBXImporter() override;
// -------------------- /// @brief The class destructor, default implementation.
bool CanRead(const std::string &pFile, ~FBXImporter() override = default;
IOSystem *pIOHandler,
bool checkSig) const override; /// @brief Will check the file for readability.
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
protected: protected:
// -------------------- // --------------------
@ -90,7 +91,7 @@ protected:
IOSystem *pIOHandler) override; IOSystem *pIOHandler) override;
private: private:
FBX::ImportSettings settings; FBX::ImportSettings mSettings;
}; // !class FBXImporter }; // !class FBXImporter
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, 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; const aiMatrix4x4 &childTransform = pNode->mChildren[a]->mTransformation;
aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4); aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4);
ai_real distanceToChild = childpos.Length(); ai_real distanceToChild = childpos.Length();
if (distanceToChild < 0.0001) if (distanceToChild < ai_epsilon) {
continue; continue;
}
aiVector3D up = aiVector3D(childpos).Normalize(); aiVector3D up = aiVector3D(childpos).Normalize();
aiVector3D orth(1.0, 0.0, 0.0); 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); orth.Set(0.0, 1.0, 0.0);
}
aiVector3D front = (up ^ orth).Normalize(); aiVector3D front = (up ^ orth).Normalize();
aiVector3D side = (front ^ up).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 // add all the vertices to the bone's influences
bone->mNumWeights = numVertices; bone->mNumWeights = numVertices;
bone->mWeights = new aiVertexWeight[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); bone->mWeights[a] = aiVertexWeight(vertexStartIndex + a, 1.0);
}
// HACK: (thom) transform all vertices to the bone's local space. Should be done before adding // 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. // 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 // 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]); CreateGeometry(pNode->mChildren[a]);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -691,6 +691,15 @@ enum aiComponent
#define AI_CONFIG_FBX_CONVERT_TO_M \ #define AI_CONFIG_FBX_CONVERT_TO_M \
"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 /** @brief Set the vertex animation keyframe to be imported
* *

View File

@ -120,7 +120,7 @@ extern "C" {
* primitive are actually present in a mesh. The #aiProcess_SortByPType flag * primitive are actually present in a mesh. The #aiProcess_SortByPType flag
* executes a special post-processing algorithm which splits meshes with * executes a special post-processing algorithm which splits meshes with
* *different* primitive types mixed up (e.g. lines and triangles) in several * *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 * #AI_CONFIG_PP_SBP_REMOVE) to force #aiProcess_SortByPType to remove
* specific kinds of primitives from the imported scene, completely and forever. * specific kinds of primitives from the imported scene, completely and forever.
* In many cases you'll probably want to set this setting to * In many cases you'll probably want to set this setting to
@ -269,12 +269,12 @@ struct aiBone {
unsigned int mNumWeights; unsigned int mNumWeights;
#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS #ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
// The bone armature node - used for skeleton conversion /// The bone armature node - used for skeleton conversion
// you must enable aiProcess_PopulateArmatureData to populate this /// you must enable aiProcess_PopulateArmatureData to populate this
C_STRUCT aiNode *mArmature; C_STRUCT aiNode *mArmature;
// The bone node in the scene - used for skeleton conversion /// The bone node in the scene - used for skeleton conversion
// you must enable aiProcess_PopulateArmatureData to populate this /// you must enable aiProcess_PopulateArmatureData to populate this
C_STRUCT aiNode *mNode; C_STRUCT aiNode *mNode;
#endif #endif
@ -296,7 +296,7 @@ struct aiBone {
#ifdef __cplusplus #ifdef __cplusplus
//! Default constructor /// @brief Default constructor
aiBone() AI_NO_EXCEPT aiBone() AI_NO_EXCEPT
: mName(), : mName(),
mNumWeights(0), mNumWeights(0),
@ -309,7 +309,7 @@ struct aiBone {
// empty // empty
} }
//! Copy constructor /// @brief Copy constructor
aiBone(const aiBone &other) : aiBone(const aiBone &other) :
mName(other.mName), mName(other.mName),
mNumWeights(other.mNumWeights), mNumWeights(other.mNumWeights),
@ -319,14 +319,27 @@ struct aiBone {
#endif #endif
mWeights(nullptr), mWeights(nullptr),
mOffsetMatrix(other.mOffsetMatrix) { mOffsetMatrix(other.mOffsetMatrix) {
if (other.mWeights && other.mNumWeights) { copyVertexWeights(other);
mWeights = new aiVertexWeight[mNumWeights]; }
::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight));
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 //! Assignment operator
aiBone &operator=(const aiBone &other) { aiBone &operator = (const aiBone &other) {
if (this == &other) { if (this == &other) {
return *this; return *this;
} }
@ -334,21 +347,13 @@ struct aiBone {
mName = other.mName; mName = other.mName;
mNumWeights = other.mNumWeights; mNumWeights = other.mNumWeights;
mOffsetMatrix = other.mOffsetMatrix; mOffsetMatrix = other.mOffsetMatrix;
copyVertexWeights(other);
if (other.mWeights && other.mNumWeights) {
if (mWeights) {
delete[] mWeights;
}
mWeights = new aiVertexWeight[mNumWeights];
::memcpy(mWeights, other.mWeights, mNumWeights * sizeof(aiVertexWeight));
}
return *this; return *this;
} }
bool operator==(const aiBone &rhs) const { bool operator==(const aiBone &rhs) const {
if (mName != rhs.mName || mNumWeights != rhs.mNumWeights) { if (mName != rhs.mName || mNumWeights != rhs.mNumWeights ) {
return false; return false;
} }
@ -937,17 +942,92 @@ struct aiMesh {
#endif // __cplusplus #endif // __cplusplus
}; };
struct aiSkeleton { struct aiSkeletonBone {
C_STRUCT aiString mName; /// The parent bone index, is -1 one if this bone represents the root bone.
unsigned int mNumWeights; 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; 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 #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 // 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 #endif // __cplusplus
}; };

View File

@ -343,6 +343,16 @@ struct aiScene
*/ */
C_STRUCT aiString mName; C_STRUCT aiString mName;
/**
*
*/
unsigned int mNumSkeletons;
/**
*
*/
C_STRUCT aiSkeleton **mSkeletons;
#ifdef __cplusplus #ifdef __cplusplus
//! Default constructor - set everything to 0/nullptr //! Default constructor - set everything to 0/nullptr
@ -383,6 +393,10 @@ struct aiScene
return mAnimations != nullptr && mNumAnimations > 0; return mAnimations != nullptr && mNumAnimations > 0;
} }
bool hasSkeletons() const {
return mSkeletons != nullptr && mNumSkeletons > 0;
}
//! Returns a short filename from a full path //! Returns a short filename from a full path
static const char* GetShortFilename(const char* filename) { static const char* GetShortFilename(const char* filename) {
const char* lastSlash = strrchr(filename, '/'); const char* lastSlash = strrchr(filename, '/');