diff --git a/Build.md b/Build.md index d6abe7b67..f48f50495 100644 --- a/Build.md +++ b/Build.md @@ -14,7 +14,8 @@ The assimp port in vcpkg is kept up to date by Microsoft team members and commun ## Install on Ubuntu You can install the Asset-Importer-Lib via apt: ``` -sudo apt-get install assimp +sudp apt-get update +sudo apt-get install libassimp-dev ``` ## Install pyassimp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2aa66421c..a4f711528 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,7 @@ OPTION( ASSIMP_BUILD_ZLIB ) OPTION( ASSIMP_BUILD_ASSIMP_TOOLS "If the supplementary tools for Assimp are built in addition to the library." - ON + OFF ) OPTION ( ASSIMP_BUILD_SAMPLES "If the official samples are built as well (needs Glut)." diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..e4915cd1c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Supported Versions + +Use this section to tell people about which versions of your project are +currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 5.2.4 | :white_check_mark: | + +## Reporting a Vulnerability + +If you have found any security vulnerability you can contact us via +kim.kulling@googlemail.com + diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index 63f18b455..333d169aa 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -74,6 +74,8 @@ namespace XmlTag { const char* const pid = "pid"; const char* const pindex = "pindex"; const char* const p1 = "p1"; + const char *const p2 = "p2"; + const char *const p3 = "p3"; const char* const name = "name"; const char* const type = "type"; const char* const build = "build"; diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index 740bc5658..9d7a1800f 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -64,7 +64,7 @@ bool validateColorString(const char *color) { return true; } -aiFace ReadTriangle(XmlNode &node) { +aiFace ReadTriangle(XmlNode &node, unsigned int &texId0, unsigned int &texId1, unsigned int &texId2) { aiFace face; face.mNumIndices = 3; @@ -73,6 +73,10 @@ aiFace ReadTriangle(XmlNode &node) { face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + texId0 = static_cast(std::atoi(node.attribute(XmlTag::p1).as_string())); + texId1 = static_cast(std::atoi(node.attribute(XmlTag::p2).as_string())); + texId2 = static_cast(std::atoi(node.attribute(XmlTag::p3).as_string())); + return face; } @@ -412,6 +416,8 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); + unsigned int texId[3]; + aiFace face = ReadTriangle(currentNode, texId[0], texId[1], texId[2]); if (hasPid && hasP1) { auto it = mResourcesDictionnary.find(pid); if (it != mResourcesDictionnary.end()) { @@ -420,6 +426,11 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) { if (mesh->mTextureCoords[0] == nullptr) { + mesh->mNumUVComponents[0] = 2; + for (unsigned int i = 1; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { + mesh->mNumUVComponents[i] = 0; + } + Texture2DGroup *group = static_cast(it->second); const std::string name = ai_to_string(group->mTexId); for (size_t i = 0; i < mMaterials.size(); ++i) { @@ -427,8 +438,9 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { mesh->mMaterialIndex = static_cast(i); } } - mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()]; - for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) { + mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0); } } @@ -436,7 +448,6 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { } } - aiFace face = ReadTriangle(currentNode); faces.push_back(face); } } @@ -578,11 +589,15 @@ aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basemater } void XmlSerializer::StoreMaterialsInScene(aiScene *scene) { - if (nullptr == scene || mMaterials.empty()) { + if (nullptr == scene) { return; } scene->mNumMaterials = static_cast(mMaterials.size()); + if (scene->mNumMaterials == 0) { + return; + } + scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; for (size_t i = 0; i < mMaterials.size(); ++i) { scene->mMaterials[i] = mMaterials[i]; diff --git a/code/AssetLib/Collada/ColladaHelper.h b/code/AssetLib/Collada/ColladaHelper.h index 31d7b5ae5..2930f5108 100644 --- a/code/AssetLib/Collada/ColladaHelper.h +++ b/code/AssetLib/Collada/ColladaHelper.h @@ -621,6 +621,11 @@ struct Animation { for (std::vector::iterator it = pParent->mSubAnims.begin(); it != pParent->mSubAnims.end();) { Animation *anim = *it; + // Assign the first animation name to the parent if empty. + // This prevents the animation name from being lost when animations are combined + if (mName.empty()) { + mName = anim->mName; + } CombineSingleChannelAnimationsRecursively(anim); if (childrenAnimationsHaveDifferentChannels && anim->mChannels.size() == 1 && diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 775ba44d2..fdc9c1c8f 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -102,6 +102,7 @@ ColladaLoader::ColladaLoader() : mTextures(), mAnims(), noSkeletonMesh(false), + removeEmptyBones(false), ignoreUpDirection(false), useColladaName(false), mNodeNameCounter(0) { @@ -130,6 +131,7 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool // ------------------------------------------------------------------------------------------------ void ColladaLoader::SetupProperties(const Importer *pImp) { noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0; + removeEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true) != 0; ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0; useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0; } @@ -798,9 +800,10 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc // count the number of bones which influence vertices of the current submesh size_t numRemainingBones = 0; for (const auto & dstBone : dstBones) { - if (!dstBone.empty()) { - ++numRemainingBones; + if (dstBone.empty() && removeEmptyBones) { + continue; } + ++numRemainingBones; } // create bone array and copy bone weights one by one @@ -809,7 +812,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc size_t boneCount = 0; for (size_t a = 0; a < numBones; ++a) { // omit bones without weights - if (dstBones[a].empty()) { + if (dstBones[a].empty() && removeEmptyBones) { continue; } diff --git a/code/AssetLib/Collada/ColladaLoader.h b/code/AssetLib/Collada/ColladaLoader.h index 246abed22..870c12a5a 100644 --- a/code/AssetLib/Collada/ColladaLoader.h +++ b/code/AssetLib/Collada/ColladaLoader.h @@ -237,6 +237,7 @@ protected: std::vector mAnims; bool noSkeletonMesh; + bool removeEmptyBones; bool ignoreUpDirection; bool useColladaName; 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 5cd549e35..3ae96d54d 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,9 +642,8 @@ void FBXConverter::GetRotationMatrix(Model::RotOrder mode, const aiVector3D &rot bool FBXConverter::NeedsComplexTransformationChain(const Model &model) { const PropertyTable &props = model.Props(); - bool ok; - const float zero_epsilon = ai_epsilon; + const auto zero_epsilon = ai_epsilon; const aiVector3D all_ones(1.0f, 1.0f, 1.0f); for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) { const TransformationComp comp = static_cast(i); @@ -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; @@ -3180,7 +3232,7 @@ aiNodeAnim* FBXConverter::GenerateSimpleNodeAnim(const std::string& name, bool ok = false; - const float zero_epsilon = ai_epsilon; + const auto zero_epsilon = ai_epsilon; const aiVector3D& preRotation = PropertyGet(props, "PreRotation", ok); if (ok && preRotation.SquareLength() > zero_epsilon) { @@ -3325,6 +3377,10 @@ FBXConverter::KeyFrameListList FBXConverter::GetRotationKeyframeList(const std:: Keys->push_back(tnew); Values->push_back(vnew); } + else { + // Something broke + break; + } tp = tnew; vp = vnew; } @@ -3625,6 +3681,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/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 0061634a6..b16565559 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -874,7 +874,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // Batch loader used to load external models BatchLoader batch(pIOHandler); - // batch.SetBasePath(pFile); + //batch.SetBasePath(pFile); cameras.reserve(5); lights.reserve(5); diff --git a/code/AssetLib/LWO/LWOLoader.cpp b/code/AssetLib/LWO/LWOLoader.cpp index c4e85bc50..44517a1a6 100644 --- a/code/AssetLib/LWO/LWOLoader.cpp +++ b/code/AssetLib/LWO/LWOLoader.cpp @@ -287,7 +287,7 @@ void LWOImporter::InternReadFile(const std::string &pFile, if (UINT_MAX == iDefaultSurface) { pSorted.erase(pSorted.end() - 1); } - for (unsigned int p = 0, j = 0; j < mSurfaces->size(); ++j) { + for (unsigned int j = 0; j < mSurfaces->size(); ++j) { SortedRep &sorted = pSorted[j]; if (sorted.empty()) continue; @@ -425,7 +425,6 @@ void LWOImporter::InternReadFile(const std::string &pFile, } else { ASSIMP_LOG_VERBOSE_DEBUG("LWO2: No need to compute normals, they're already there"); } - ++p; } } diff --git a/code/AssetLib/MDL/MDLMaterialLoader.cpp b/code/AssetLib/MDL/MDLMaterialLoader.cpp index 2de43d241..db4b534f2 100644 --- a/code/AssetLib/MDL/MDLMaterialLoader.cpp +++ b/code/AssetLib/MDL/MDLMaterialLoader.cpp @@ -449,6 +449,9 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7( unsigned int iWidth, unsigned int iHeight) { std::unique_ptr pcNew; + if (szCurrent == nullptr) { + return; + } // get the type of the skin unsigned int iMasked = (unsigned int)(iType & 0xF); diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index c8e47933d..2883f9612 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -151,45 +151,46 @@ namespace Grammar { } static TokenType matchTokenType(const char *tokenType) { - if (MetricType == tokenType) { + const size_t len = std::strlen(tokenType); + if (0 == strncmp(MetricType, tokenType, len)) { return MetricToken; - } else if (NameType == tokenType) { + } else if (0 == strncmp(NameType, tokenType, len)) { return NameToken; - } else if (ObjectRefType == tokenType) { + } else if (0 == strncmp(ObjectRefType, tokenType, len)) { return ObjectRefToken; - } else if (MaterialRefType == tokenType) { + } else if (0 == strncmp(MaterialRefType, tokenType, len)) { return MaterialRefToken; - } else if (MetricKeyType == tokenType) { + } else if (0 == strncmp(MetricKeyType, tokenType, len)) { return MetricKeyToken; - } else if (GeometryNodeType == tokenType) { + } else if (0 == strncmp(GeometryNodeType, tokenType, len)) { return GeometryNodeToken; - } else if (CameraNodeType == tokenType) { + } else if (0 == strncmp(CameraNodeType, tokenType, len)) { return CameraNodeToken; - } else if (LightNodeType == tokenType) { + } else if (0 == strncmp(LightNodeType, tokenType, len)) { return LightNodeToken; - } else if (GeometryObjectType == tokenType) { + } else if (0 == strncmp(GeometryObjectType, tokenType, len)) { return GeometryObjectToken; - } else if (CameraObjectType == tokenType) { + } else if (0 == strncmp(CameraObjectType, tokenType, len)) { return CameraObjectToken; - } else if (LightObjectType == tokenType) { + } else if (0 == strncmp(LightObjectType, tokenType, len)) { return LightObjectToken; - } else if (TransformType == tokenType) { + } else if (0 == strncmp(TransformType, tokenType, len)) { return TransformToken; - } else if (MeshType == tokenType) { + } else if (0 == strncmp(MeshType, tokenType, len)) { return MeshToken; - } else if (VertexArrayType == tokenType) { + } else if (0 == strncmp(VertexArrayType, tokenType, len)) { return VertexArrayToken; - } else if (IndexArrayType == tokenType) { + } else if (0 == strncmp(IndexArrayType, tokenType, len)) { return IndexArrayToken; - } else if (MaterialType == tokenType) { + } else if (0 == strncmp(MaterialType, tokenType, len)) { return MaterialToken; - } else if (ColorType == tokenType) { + } else if (0 == strncmp(ColorType, tokenType, len)) { return ColorToken; - } else if (ParamType == tokenType) { + } else if (0 == strncmp(ParamType, tokenType, len)) { return ParamToken; - } else if (TextureType == tokenType) { + } else if (0 == strncmp(TextureType, tokenType, len)) { return TextureToken; - } else if (AttenType == tokenType) { + } else if (0 == strncmp(AttenType, tokenType, len)) { return AttenToken; } @@ -256,11 +257,6 @@ OpenGEXImporter::RefInfo::RefInfo(aiNode *node, Type type, std::vector m_Names; RefInfo( aiNode *node, Type type, std::vector &names ); - ~RefInfo(); + ~RefInfo() = default; RefInfo( const RefInfo & ) = delete; RefInfo &operator = ( const RefInfo & ) = delete; diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index ffd8d223e..da7591d52 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -683,7 +683,7 @@ bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &she } // Default Sheen color factor {0,0,0} disables Sheen, so do not export - if (sheen.sheenColorFactor == defaultSheenFactor) { + if (sheen.sheenColorFactor[0] == defaultSheenFactor[0] && sheen.sheenColorFactor[1] == defaultSheenFactor[1] && sheen.sheenColorFactor[2] == defaultSheenFactor[2]) { return false; } @@ -908,7 +908,7 @@ Ref FindSkeletonRootJoint(Ref &skinRef) { do { startNodeRef = parentNodeRef; parentNodeRef = startNodeRef->parent; - } while (!parentNodeRef->jointName.empty()); + } while (parentNodeRef && !parentNodeRef->jointName.empty()); return parentNodeRef; } diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index 17fc44f9a..f2c772187 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -63,7 +63,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) { -#if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) +#if defined _WIN32 && (!defined __GNUC__ || !defined __CLANG__ && __MSVCRT_VERSION__ >= 0x0601) template <> inline size_t select_ftell<8>(FILE *file) { return (size_t)::_ftelli64(file); @@ -74,7 +74,7 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { return ::_fseeki64(file, offset, origin); } -#endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) +#endif } // namespace @@ -149,13 +149,20 @@ size_t DefaultIOStream::FileSize() const { // // See here for details: // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file -#if defined _WIN64 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) +#if defined _WIN32 && (!defined __GNUC__ || !defined __CLANG__ && __MSVCRT_VERSION__ >= 0x0601) struct __stat64 fileStat; //using fileno + fstat avoids having to handle the filename int err = _fstat64(_fileno(mFile), &fileStat); if (0 != err) return 0; mCachedSize = (size_t)(fileStat.st_size); +#elif defined _WIN32 + struct _stat32 fileStat; + //using fileno + fstat avoids having to handle the filename + int err = _fstat32(_fileno(mFile), &fileStat); + if (0 != err) + return 0; + mCachedSize = (size_t)(fileStat.st_size); #elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__ struct stat fileStat; int err = stat(mFilename.c_str(), &fileStat); diff --git a/code/Common/ScenePreprocessor.cpp b/code/Common/ScenePreprocessor.cpp index 2ef291eeb..60133f651 100644 --- a/code/Common/ScenePreprocessor.cpp +++ b/code/Common/ScenePreprocessor.cpp @@ -105,36 +105,39 @@ void ScenePreprocessor::ProcessMesh(aiMesh *mesh) { for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { if (!mesh->mTextureCoords[i]) { mesh->mNumUVComponents[i] = 0; - } else { - if (!mesh->mNumUVComponents[i]) { - mesh->mNumUVComponents[i] = 2; + continue; + } + + if (!mesh->mNumUVComponents[i]) { + mesh->mNumUVComponents[i] = 2; + } + + aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices; + + // Ensure unused components are zeroed. This will make 1D texture channels work + // as if they were 2D channels .. just in case an application doesn't handle + // this case + if (2 == mesh->mNumUVComponents[i]) { + size_t num = 0; + for (; p != end; ++p) { + p->z = 0.f; + num++; } - - aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices; - - // Ensure unused components are zeroed. This will make 1D texture channels work - // as if they were 2D channels .. just in case an application doesn't handle - // this case - if (2 == mesh->mNumUVComponents[i]) { - for (; p != end; ++p) { - p->z = 0.f; - } - } else if (1 == mesh->mNumUVComponents[i]) { - for (; p != end; ++p) { - p->z = p->y = 0.f; - } - } else if (3 == mesh->mNumUVComponents[i]) { - // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element - for (; p != end; ++p) { - if (p->z != 0) { - break; - } - } - if (p == end) { - ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D."); - mesh->mNumUVComponents[i] = 2; + } else if (1 == mesh->mNumUVComponents[i]) { + for (; p != end; ++p) { + p->z = p->y = 0.f; + } + } else if (3 == mesh->mNumUVComponents[i]) { + // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element + for (; p != end; ++p) { + if (p->z != 0) { + break; } } + if (p == end) { + ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D."); + mesh->mNumUVComponents[i] = 2; + } } } 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/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 2f80c908e..7ac4ddd4e 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -128,7 +128,7 @@ bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; pFile->Seek(0, aiOrigin_SET); - pFile->Read(reinterpret_cast(imageContent), imageSize, 1); + pFile->Read(reinterpret_cast(imageContent), static_cast(imageSize), 1); mIOHandler->Close(pFile); // Enlarging the textures table diff --git a/code/res/assimp.rc b/code/res/assimp.rc index c36d812f8..fd10935f6 100644 --- a/code/res/assimp.rc +++ b/code/res/assimp.rc @@ -1,5 +1,9 @@ #include "revision.h" +#ifdef __GNUC__ +#include "winresrc.h" +#else #include "winres.h" +#endif LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL #pragma code_page(1252) diff --git a/contrib/unzip/crypt.c b/contrib/unzip/crypt.c index 299ce03d2..4cc731b3e 100644 --- a/contrib/unzip/crypt.c +++ b/contrib/unzip/crypt.c @@ -43,10 +43,10 @@ #include "crypt.h" -#ifdef _WIN32 +#ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4244) -#endif // _WIN32 +#endif // _MSC_VER /***************************************************************************/ @@ -164,8 +164,8 @@ int crypthead(const char *passwd, uint8_t *buf, int buf_size, uint32_t *pkeys, return n; } -#ifdef _WIN32 +#ifdef _MSC_VER # pragma warning(pop) -#endif // _WIN32 +#endif // _MSC_VER /***************************************************************************/ diff --git a/contrib/unzip/ioapi.c b/contrib/unzip/ioapi.c index 30a296d0f..d9ae01e7d 100644 --- a/contrib/unzip/ioapi.c +++ b/contrib/unzip/ioapi.c @@ -23,8 +23,10 @@ #ifdef _WIN32 # define snprintf _snprintf +#ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4131 4100) +#endif # ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-parameter" @@ -357,9 +359,9 @@ void fill_fopen64_filefunc(zlib_filefunc64_def *pzlib_filefunc_def) pzlib_filefunc_def->opaque = NULL; } -#ifdef _WIN32 +#ifdef _MSC_VER # pragma warning(pop) # ifdef __clang__ # pragma clang diagnostic pop # endif -#endif // _WIN32 +#endif // _MSC_VER diff --git a/contrib/unzip/unzip.c b/contrib/unzip/unzip.c index f1eddeeda..b2f045b0a 100644 --- a/contrib/unzip/unzip.c +++ b/contrib/unzip/unzip.c @@ -73,10 +73,10 @@ # define TRYFREE(p) {if (p) free(p);} #endif -#ifdef _WIN32 +#ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4131 4244 4189 4245) -#endif // _WIN32 +#endif // _MSC_VER const char unz_copyright[] = " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; @@ -1995,6 +1995,6 @@ extern int ZEXPORT unzEndOfFile(unzFile file) return 0; } -#ifdef _WIN32 +#ifdef _MSC_VER # pragma warning(pop) -#endif // _WIN32 \ No newline at end of file +#endif // _MSC_VER \ No newline at end of file diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 28f2be8e8..67dfdeebe 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -99,295 +99,129 @@ template class TXmlParser { public: /// @brief The default class constructor. - TXmlParser() : - mDoc(nullptr), - mData() { - // empty - } + TXmlParser(); /// @brief The class destructor. - ~TXmlParser() { - clear(); - } + ~TXmlParser(); /// @brief Will clear the parsed xml-file. - void clear() { - if (mData.empty()) { - mDoc = nullptr; - return; - } - mData.clear(); - delete mDoc; - mDoc = nullptr; - } + void clear(); /// @brief Will search for a child-node by its name /// @param name [in] The name of the child-node. /// @return The node instance or nullptr, if nothing was found. - TNodeType *findNode(const std::string &name) { - if (name.empty()) { - return nullptr; - } - - if (nullptr == mDoc) { - return nullptr; - } - - find_node_by_name_predicate predicate(name); - mCurrent = mDoc->find_node(predicate); - if (mCurrent.empty()) { - return nullptr; - } - - return &mCurrent; - } + TNodeType *findNode(const std::string &name); /// @brief Will return true, if the node is a child-node. /// @param name [in] The name of the child node to look for. /// @return true, if the node is a child-node or false if not. - bool hasNode(const std::string &name) { - return nullptr != findNode(name); - } + bool hasNode(const std::string &name); /// @brief Will parse an xml-file from a given stream. /// @param stream The input stream. /// @return true, if the parsing was successful, false if not. - bool parse(IOStream *stream) { - if (nullptr == stream) { - ASSIMP_LOG_DEBUG("Stream is nullptr."); - return false; - } + bool parse(IOStream *stream); - const size_t len = stream->FileSize(); - mData.resize(len + 1); - memset(&mData[0], '\0', len + 1); - stream->Read(&mData[0], 1, len); - - mDoc = new pugi::xml_document(); - pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); - if (parse_result.status == pugi::status_ok) { - return true; - } - - ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); - - return false; - } - - /// @brief Will return truem if a root node is there. + /// @brief Will return true if a root node is there. /// @return true in case of an existing root. - bool hasRoot() const { - return nullptr != mDoc; - } + bool hasRoot() const; + /// @brief Will return the document pointer, is nullptr if no xml-file was parsed. /// @return The pointer showing to the document. - pugi::xml_document *getDocument() const { - return mDoc; - } + pugi::xml_document *getDocument() const; /// @brief Will return the root node, const version. /// @return The root node. - const TNodeType getRootNode() const { - static pugi::xml_node none; - if (nullptr == mDoc) { - return none; - } - return mDoc->root(); - } + const TNodeType getRootNode() const; /// @brief Will return the root node, non-const version. /// @return The root node. - TNodeType getRootNode() { - static pugi::xml_node none; - if (nullptr == mDoc) { - return none; - } - return mDoc->root(); - } + TNodeType getRootNode(); /// @brief Will check if a node with the given name is in. /// @param node [in] The node to look in. /// @param name [in] The name of the child-node. /// @return true, if node was found, false if not. - static inline bool hasNode(XmlNode &node, const char *name) { - pugi::xml_node child = node.find_child(find_node_by_name_predicate(name)); - return !child.empty(); - } + static inline bool hasNode(XmlNode &node, const char *name); /// @brief Will check if an attribute is part of the XmlNode. /// @param xmlNode [in] The node to search in. /// @param name [in} The attribute name to look for. /// @return true, if the was found, false if not. - static inline bool hasAttribute(XmlNode &xmlNode, const char *name) { - pugi::xml_attribute attr = xmlNode.attribute(name); - return !attr.empty(); - } + static inline bool hasAttribute(XmlNode &xmlNode, const char *name); /// @brief Will try to get an unsigned int attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The unsigned int value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is an unsigned int. - static inline bool getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } - - val = attr.as_uint(); - return true; - } + static inline bool getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val); /// @brief Will try to get an int attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The int value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is an int. - static inline bool getIntAttribute(XmlNode &xmlNode, const char *name, int &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } - - val = attr.as_int(); - return true; - } + static inline bool getIntAttribute(XmlNode &xmlNode, const char *name, int &val); /// @brief Will try to get a real attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The real value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is a real. - static inline bool getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } -#ifdef ASSIMP_DOUBLE_PRECISION - val = attr.as_double(); -#else - val = attr.as_float(); -#endif - return true; - } + static inline bool getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val); /// @brief Will try to get a float attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The float value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is a float. - static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } - - val = attr.as_float(); - return true; - } + static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val); /// @brief Will try to get a double attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The double value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is a double. - static inline bool getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } - - val = attr.as_double(); - return true; - } + static inline bool getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val); /// @brief Will try to get a std::string attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The std::string value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is a std::string. - static inline bool getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } - - val = attr.as_string(); - return true; - } + static inline bool getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val); /// @brief Will try to get a bool attribute value. /// @param xmlNode [in] The node to search in. /// @param name [in] The attribute name to look for. /// @param val [out] The bool value from the attribute. /// @return true, if the node contains an attribute with the given name and if the value is a bool. - static inline bool getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val) { - pugi::xml_attribute attr = xmlNode.attribute(name); - if (attr.empty()) { - return false; - } - - val = attr.as_bool(); - return true; - } + static inline bool getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val); /// @brief Will try to get the value of the node as a string. /// @param node [in] The node to search in. /// @param text [out] The value as a text. /// @return true, if the value can be read out. - static inline bool getValueAsString(XmlNode &node, std::string &text) { - text = std::string(); - if (node.empty()) { - return false; - } - - text = node.text().as_string(); - - return true; - } + static inline bool getValueAsString(XmlNode &node, std::string &text); /// @brief Will try to get the value of the node as a float. /// @param node [in] The node to search in. /// @param text [out] The value as a float. /// @return true, if the value can be read out. - static inline bool getValueAsFloat(XmlNode &node, ai_real &v) { - if (node.empty()) { - return false; - } - - v = node.text().as_float(); - - return true; - } + static inline bool getValueAsFloat(XmlNode &node, ai_real &v); /// @brief Will try to get the value of the node as an integer. /// @param node [in] The node to search in. /// @param text [out] The value as a int. /// @return true, if the value can be read out. - static inline bool getValueAsInt(XmlNode &node, int &v) { - if (node.empty()) { - return false; - } - - v = node.text().as_int(); - - return true; - } + static inline bool getValueAsInt(XmlNode &node, int &v); /// @brief Will try to get the value of the node as an bool. /// @param node [in] The node to search in. /// @param text [out] The value as a bool. /// @return true, if the value can be read out. - static inline bool getValueAsBool(XmlNode& node, bool& v) - { - if (node.empty()) { - return false; - } - - v = node.text().as_bool(); - - return true; - } + static inline bool getValueAsBool(XmlNode &node, bool &v); private: pugi::xml_document *mDoc; @@ -395,6 +229,254 @@ private: std::vector mData; }; +template +inline TXmlParser::TXmlParser() : + mDoc(nullptr), + mData() { + // empty +} + +template +inline TXmlParser::~TXmlParser() { + clear(); +} + +template +inline void TXmlParser::clear() { + if (mData.empty()) { + if (mDoc) { + delete mDoc; + } + mDoc = nullptr; + return; + } + + mData.clear(); + delete mDoc; + mDoc = nullptr; +} + +template +inline TNodeType *TXmlParser::findNode(const std::string &name) { + if (name.empty()) { + return nullptr; + } + + if (nullptr == mDoc) { + return nullptr; + } + + find_node_by_name_predicate predicate(name); + mCurrent = mDoc->find_node(predicate); + if (mCurrent.empty()) { + return nullptr; + } + + return &mCurrent; +} + +template +bool TXmlParser::hasNode(const std::string &name) { + return nullptr != findNode(name); +} + +template +bool TXmlParser::parse(IOStream *stream) { + if (hasRoot()) { + clear(); + } + + if (nullptr == stream) { + ASSIMP_LOG_DEBUG("Stream is nullptr."); + return false; + } + + const size_t len = stream->FileSize(); + mData.resize(len + 1); + memset(&mData[0], '\0', len + 1); + stream->Read(&mData[0], 1, len); + + mDoc = new pugi::xml_document(); + pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); + if (parse_result.status == pugi::status_ok) { + return true; + } + + ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); + + return false; +} + +template +bool TXmlParser::hasRoot() const { + return nullptr != mDoc; +} + +template +pugi::xml_document *TXmlParser::getDocument() const { + return mDoc; +} + +template +const TNodeType TXmlParser::getRootNode() const { + static pugi::xml_node none; + if (nullptr == mDoc) { + return none; + } + return mDoc->root(); +} + +template +TNodeType TXmlParser::getRootNode() { + static pugi::xml_node none; + if (nullptr == mDoc) { + return none; + } + + return mDoc->root(); +} + +template +inline bool TXmlParser::hasNode(XmlNode &node, const char *name) { + pugi::xml_node child = node.find_child(find_node_by_name_predicate(name)); + return !child.empty(); +} + +template +inline bool TXmlParser::hasAttribute(XmlNode &xmlNode, const char *name) { + pugi::xml_attribute attr = xmlNode.attribute(name); + return !attr.empty(); +} + +template +inline bool TXmlParser::getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } + + val = attr.as_uint(); + return true; +} + +template +inline bool TXmlParser::getIntAttribute(XmlNode &xmlNode, const char *name, int &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } + + val = attr.as_int(); + return true; +} + +template +inline bool TXmlParser::getRealAttribute(XmlNode &xmlNode, const char *name, ai_real &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } +#ifdef ASSIMP_DOUBLE_PRECISION + val = attr.as_double(); +#else + val = attr.as_float(); +#endif + return true; +} + +template +inline bool TXmlParser::getFloatAttribute(XmlNode &xmlNode, const char *name, float &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } + + val = attr.as_float(); + + return true; +} + +template +inline bool TXmlParser::getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } + + val = attr.as_double(); + + return true; +} + +template +inline bool TXmlParser::getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } + + val = attr.as_string(); + + return true; +} + +template +inline bool TXmlParser::getBoolAttribute(XmlNode &xmlNode, const char *name, bool &val) { + pugi::xml_attribute attr = xmlNode.attribute(name); + if (attr.empty()) { + return false; + } + + val = attr.as_bool(); + + return true; +} + +template +inline bool TXmlParser::getValueAsString(XmlNode &node, std::string &text) { + text = std::string(); + if (node.empty()) { + return false; + } + + text = node.text().as_string(); + + return true; +} + +template +inline bool TXmlParser::getValueAsFloat(XmlNode &node, ai_real &v) { + if (node.empty()) { + return false; + } + + v = node.text().as_float(); + + return true; +} + +template +inline bool TXmlParser::getValueAsInt(XmlNode &node, int &v) { + if (node.empty()) { + return false; + } + + v = node.text().as_int(); + + return true; +} + +template +inline bool TXmlParser::getValueAsBool(XmlNode &node, bool &v) { + if (node.empty()) { + return false; + } + + v = node.text().as_bool(); + + return true; +} + using XmlParser = TXmlParser; /// @brief This class declares an iterator to loop through all children of the root node. 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/Collada/box_nested_animation.dae b/test/models/Collada/box_nested_animation.dae new file mode 100644 index 000000000..f50113b8f --- /dev/null +++ b/test/models/Collada/box_nested_animation.dae @@ -0,0 +1,196 @@ + + + + + Blender User + Blender 2.80.40 commit date:2019-01-07, commit time:23:37, hash:91a155833e59 + + 2019-01-08T17:44:11 + 2019-01-08T17:44:11 + + Z_UP + + + + + + + + 0.8 0.8 0.8 1 + + + 0 0.5 0 1 + + + + + + + + + + + + + + + + + 1 1 1 1 1 -1 1 -1 1 1 -1 -1 -1 1 1 -1 1 -1 -1 -1 1 -1 -1 -1 + + + + + + + + + + 0 0 1 0 -1 0 -1 0 0 0 0 -1 1 0 0 0 1 0 + + + + + + + + + + 0.625 0 0.375 0.25 0.375 0 0.625 0.25 0.375 0.5 0.375 0.25 0.625 0.5 0.375 0.75 0.375 0.5 0.625 0.75 0.375 1 0.375 0.75 0.375 0.5 0.125 0.75 0.125 0.5 0.875 0.5 0.625 0.75 0.625 0.5 0.625 0 0.625 0.25 0.375 0.25 0.625 0.25 0.625 0.5 0.375 0.5 0.625 0.5 0.625 0.75 0.375 0.75 0.625 0.75 0.625 1 0.375 1 0.375 0.5 0.375 0.75 0.125 0.75 0.875 0.5 0.875 0.75 0.625 0.75 + + + + + + + + + + + + + + + 3 3 3 3 3 3 3 3 3 3 3 3 +

4 0 0 2 0 1 0 0 2 2 1 3 7 1 4 3 1 5 6 2 6 5 2 7 7 2 8 1 3 9 7 3 10 5 3 11 0 4 12 3 4 13 1 4 14 4 5 15 1 5 16 5 5 17 4 0 18 6 0 19 2 0 20 2 1 21 6 1 22 7 1 23 6 2 24 4 2 25 5 2 26 1 3 27 3 3 28 7 3 29 0 4 30 2 4 31 3 4 32 4 5 33 0 5 34 1 5 35

+
+
+
+
+ + + + 1 0 0 -1 0 1 0 1 0 0 1 1 0 0 0 1 + + Bone + + + + + + + + 0.7886752 0.2113248 0.5773504 -0.5773504 -0.5773503 0.5773503 0.5773503 1.154701 -0.2113249 -0.7886752 0.5773503 -0.5773502 0 0 0 1 + + + + + + + + 1 1 1 1 1 1 1 1 + + + + + + + + + + + + + + 1 1 1 1 1 1 1 1 + 0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 + + + + + + + + + 0.04166662 0.08333331 0.125 0.1666666 0.2083333 0.25 0.2916666 0.3333333 0.375 0.4166666 0.4583333 0.5 0.5416667 0.5833333 0.625 0.6666667 0.7083333 0.75 0.7916667 0.8333333 0.875 0.9166667 0.9583333 1 1.041667 1.083333 1.125 1.166667 1.208333 1.25 1.291667 1.333333 1.375 1.416667 1.458333 1.5 1.541667 1.583333 1.625 1.666667 + + + + + + + + 1 0 0 1 0 1 0 -1 0 0 1 0 0 0 0 1 0.9999878 3.10816e-5 0.004935208 1 0 0.9999802 -0.006297799 -1 -0.004935306 0.006297722 0.999968 0 0 0 0 1 0.999819 4.61727e-4 0.01901668 1 0 0.9997054 -0.02427293 -1 -0.01902229 0.02426853 0.9995245 0 0 0 0 1 0.9991519 0.002163141 0.04111904 1 0 0.9986191 -0.05253414 -1 -0.04117589 0.05248959 0.9977722 0 0 0 0 1 0.9975264 0.006301912 0.07000974 1 0 0.9959731 -0.08965231 -1 -0.0702928 0.08943056 0.9935095 0 0 0 0 1 0.9944467 0.01411698 0.1042901 1 0 0.9909625 -0.1341392 -1 -0.1052413 0.1333943 0.9854594 0 0 0 0 1 0.9894527 0.02671701 0.1423712 1 0 0.9828442 -0.184438 -1 -0.1448563 0.1824927 0.9724778 0 0 0 0 1 0.9821799 0.04490547 0.1825 1 0 0.9710366 -0.2389307 -1 -0.1879434 0.234673 0.9537326 0 0 0 0 1 0.9724072 0.06904543 0.2228386 1 0 0.9551992 -0.2959637 -1 -0.2332902 0.2877972 0.9288425 0 0 0 0 1 0.9600915 0.09897761 0.261587 1 0 0.9352878 -0.3538882 -1 -0.2796861 0.339765 0.8979618 0 0 0 0 1 0.9453882 0.1340003 0.2971281 1 0 0.9115852 -0.4111113 -1 -0.3259466 0.3886598 0.8618018 0 0 0 0 1 0.9286572 0.1729132 0.328172 1 0 0.8847058 -0.4661497 -1 -0.3709391 0.4328933 0.8215885 0 0 0 0 1 0.9104556 0.2141147 0.3538722 1 0 0.8555763 -0.5176768 -1 -0.4136069 0.4713217 0.7789642 0 0 0 0 1 0.8915175 0.2557371 0.3738919 1 0 0.8253933 -0.5645581 -1 -0.4529863 0.5033134 0.7358525 0 0 0 0 1 0.8727233 0.2957927 0.388408 1 0 0.7955672 -0.6058654 -1 -0.4882152 0.5287529 0.6943099 0 0 0 0 1 0.8550603 0.332307 0.3980502 1 0 0.7676533 -0.6408653 -1 -0.5185286 0.5479785 0.6563899 0 0 0 0 1 0.8395769 0.3634188 0.4037789 1 0 0.7432778 -0.6689829 -1 -0.5432408 0.5616626 0.6240388 0 0 0 0 1 0.8273312 0.3874339 0.4067161 1 0 0.7240622 -0.6897347 -1 -0.5617144 0.5706391 0.5990393 0 0 0 0 1 0.8193359 0.4028329 0.4079393 1 0 0.7115462 -0.7026393 -1 -0.5733138 0.5756976 0.5829953 0 0 0 0 1 0.8164964 0.4082482 0.4082486 1 7.75722e-8 0.707107 -0.7071065 -1 -0.5773504 0.57735 0.5773503 0 0 0 0 1 0.8190646 0.4033515 0.4079717 1 7.78161e-8 0.7111219 -0.7030687 -1 -0.5737014 0.5758587 0.5824547 0 0 0 0 1 0.8263245 0.3893851 0.4068995 1 7.85059e-8 0.7224849 -0.6913868 -1 -0.5631944 0.5713098 0.5970069 0 0 0 0 1 0.8375081 0.3675125 0.4043696 1 7.95684e-8 0.7400277 -0.6725764 -1 -0.5464249 0.5632883 0.6197791 0 0 0 0 1 0.8517552 0.3390183 0.3994742 1 8.0922e-8 0.7624427 -0.6470557 -1 -0.5239399 0.5511332 0.6494145 0 0 0 0 1 0.8681612 0.3053284 0.3912425 1 8.24806e-8 0.7883466 -0.6152314 -1 -0.4962822 0.5341201 0.6844119 0 0 0 0 1 0.8858209 0.2680094 0.3788038 1 8.41584e-8 0.8163394 -0.5775725 -1 -0.4640273 0.5116258 0.7231305 0 0 0 0 1 0.9038687 0.2287352 0.3615268 1 8.58731e-8 0.8450637 -0.5346656 -1 -0.42781 0.4832675 0.7638266 0 0 0 0 1 0.9215156 0.1892192 0.339124 1 8.75496e-8 0.8732626 -0.4872499 -1 -0.3883413 0.4490085 0.8047251 0 0 0 0 1 0.9380813 0.1511175 0.3117163 1 8.91235e-8 0.899834 -0.4362323 -1 -0.3464153 0.4092214 0.8441175 0 0 0 0 1 0.9530206 0.1159168 0.2798482 1 9.05428e-8 0.9238796 -0.3826832 -1 -0.3029055 0.3647051 0.8804763 0 0 0 0 1 0.965943 0.08482374 0.2444564 1 9.17705e-8 0.9447417 -0.3278156 -1 -0.2587547 0.3166512 0.9125667 0 0 0 0 1 0.9766233 0.05867312 0.2067956 1 9.27852e-8 0.9620277 -0.2729518 -1 -0.2149581 0.2665711 0.9395387 0 0 0 0 1 0.9850019 0.03787052 0.1683363 1 9.35812e-8 0.975616 -0.2194843 -1 -0.1725436 0.2161924 0.9609836 0 0 0 0 1 0.991176 0.02237916 0.1306496 1 9.41678e-8 0.9856446 -0.1688333 -1 -0.1325524 0.1673435 0.9769473 0 0 0 0 1 0.9953793 0.01175384 0.09529842 1 9.45671e-8 0.9924796 -0.1224106 -1 -0.09602053 0.121845 0.9878936 0 0 0 0 1 0.997952 0.005218936 0.06375288 1 9.48115e-8 0.996666 -0.08159051 -1 -0.06396614 0.08142342 0.9946249 0 0 0 0 1 0.9993011 0.001782816 0.03733916 1 9.49397e-8 0.998862 -0.04769476 -1 -0.0373817 0.04766143 0.9981638 0 0 0 0 1 0.9998515 3.78837e-4 0.01722835 1 9.4992e-8 0.9997582 -0.02198936 -1 -0.01723252 0.0219861 0.9996098 0 0 0 0 1 0.99999 2.53135e-5 0.004462156 1 9.50052e-8 0.9999838 -0.00569412 -1 -0.004462227 0.005694063 0.9999738 0 0 0 0 1 1 0 0 2 0 1 0 -1 0 0 1 0 0 0 0 1 + + + + + + + + LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR LINEAR + + + + + + + + + + + + + + + + + + + 1 0 0 1 0 1 0 -1 0 0 1 0 0 0 0 1 + + 0.7886751 -0.5773503 -0.211325 0 0.2113248 0.5773503 -0.7886751 0 0.5773503 0.5773503 0.5773502 0 0 0 0 1 + + + 0 + -0.5235989 + -2 + 2 + 2 + + + + + 0 0 0 + 0 0 1 0 + 0 1 0 0 + 1 0 0 0 + 1 1 1 + + #Armature_Bone + + + + + + + + + + + + + + + +
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/utColladaImportExport.cpp b/test/unit/utColladaImportExport.cpp index b58fa03fa..e2842732e 100644 --- a/test/unit/utColladaImportExport.cpp +++ b/test/unit/utColladaImportExport.cpp @@ -69,31 +69,44 @@ public: virtual bool importerTest() final { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure); - if (scene == nullptr) - return false; + { + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure); + if (scene == nullptr) + return false; - // Expected number of items - EXPECT_EQ(scene->mNumMeshes, 1u); - EXPECT_EQ(scene->mNumMaterials, 1u); - EXPECT_EQ(scene->mNumAnimations, 0u); - EXPECT_EQ(scene->mNumTextures, 0u); - EXPECT_EQ(scene->mNumLights, 1u); - EXPECT_EQ(scene->mNumCameras, 1u); + // Expected number of items + EXPECT_EQ(scene->mNumMeshes, 1u); + EXPECT_EQ(scene->mNumMaterials, 1u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 0u); + EXPECT_EQ(scene->mNumLights, 1u); + EXPECT_EQ(scene->mNumCameras, 1u); - // Expected common metadata - aiString value; - EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata"; - EXPECT_STREQ("Collada Importer", value.C_Str()); + // Expected common metadata + aiString value; + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata"; + EXPECT_STREQ("Collada Importer", value.C_Str()); - EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata"; - EXPECT_STREQ("1.4.1", value.C_Str()); + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata"; + EXPECT_STREQ("1.4.1", value.C_Str()); - EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, value)) << "No generator metadata"; - EXPECT_EQ(strncmp(value.C_Str(), "Maya 8.0", 8), 0) << "AI_METADATA_SOURCE_GENERATOR was: " << value.C_Str(); + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, value)) << "No generator metadata"; + EXPECT_EQ(strncmp(value.C_Str(), "Maya 8.0", 8), 0) << "AI_METADATA_SOURCE_GENERATOR was: " << value.C_Str(); - EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) << "No copyright metadata"; - EXPECT_EQ(strncmp(value.C_Str(), "Copyright 2006", 14), 0) << "AI_METADATA_SOURCE_COPYRIGHT was: " << value.C_Str(); + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) << "No copyright metadata"; + EXPECT_EQ(strncmp(value.C_Str(), "Copyright 2006", 14), 0) << "AI_METADATA_SOURCE_COPYRIGHT was: " << value.C_Str(); + } + + { + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/box_nested_animation.dae", aiProcess_ValidateDataStructure); + if (scene == nullptr) + return false; + + // Expect only one animation with the correct name + EXPECT_EQ(scene->mNumAnimations, 1u); + EXPECT_EQ(std::string(scene->mAnimations[0]->mName.C_Str()), std::string("Armature")); + + } return true; } 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); +} diff --git a/test/unit/utOpenGEXImportExport.cpp b/test/unit/utOpenGEXImportExport.cpp index a7b682063..ed22a6fb7 100644 --- a/test/unit/utOpenGEXImportExport.cpp +++ b/test/unit/utOpenGEXImportExport.cpp @@ -40,6 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "AbstractImportExportBase.h" + +#include #include "UnitTestPCH.h" #include @@ -51,11 +53,12 @@ public: bool importerTest() override { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OpenGEX/Example.ogex", 0); + EXPECT_EQ(1u, scene->mNumMeshes); return nullptr != scene; } }; -TEST_F(utOpenGEXImportExport, importLWSFromFileTest) { +TEST_F(utOpenGEXImportExport, importOpenGexFromFileTest) { EXPECT_TRUE(importerTest()); }