diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index ead8ca1dd..23c19cc58 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -863,6 +863,7 @@ struct Sampler : public Object { }; struct Scene : public Object { + std::string name; std::vector> nodes; Scene() {} diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 2c7e9fc5a..c892e6697 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1400,6 +1400,11 @@ inline void Node::Read(Value &obj, Asset &r) { } inline void Scene::Read(Value &obj, Asset &r) { + if (Value *scene_name = FindString(obj, "name")) { + if (scene_name->IsString()) { + this->name = scene_name->GetString(); + } + } if (Value *array = FindArray(obj, "nodes")) { for (unsigned int i = 0; i < array->Size(); ++i) { if (!(*array)[i].IsUint()) continue; diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b1ba5b67b..738186414 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1386,6 +1386,9 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO // read the asset file glTF2::Asset asset(pIOHandler); asset.Load(pFile, GetExtension(pFile) == "glb"); + if (asset.scene) { + pScene->mName = asset.scene->name; + } // // Copy the data out diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index e5defdeb0..364a75308 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -45,25 +43,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of the FindDegenerates post-process step. */ - - -// internal headers #include "ProcessHelper.h" #include "FindDegenerates.h" + #include +#include + using namespace Assimp; -//remove mesh at position 'index' from the scene -static void removeMesh(aiScene* pScene, unsigned const index); -//correct node indices to meshes and remove references to deleted mesh -static void updateSceneGraph(aiNode* pNode, unsigned const index); +// Correct node indices to meshes and remove references to deleted mesh +static void updateSceneGraph(aiNode* pNode, const std::unordered_map& meshMap); // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -FindDegeneratesProcess::FindDegeneratesProcess() -: mConfigRemoveDegenerates( false ) -, mConfigCheckAreaOfTriangle( false ){ +FindDegeneratesProcess::FindDegeneratesProcess() : + mConfigRemoveDegenerates( false ), + mConfigCheckAreaOfTriangle( false ){ // empty } @@ -91,50 +87,50 @@ void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { // Executes the post processing step on the given imported data. void FindDegeneratesProcess::Execute( aiScene* pScene) { ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin"); - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { - //Do not process point cloud, ExecuteOnMesh works only with faces data + if ( nullptr == pScene) { + return; + } + + std::unordered_map meshMap; + meshMap.reserve(pScene->mNumMeshes); + + const unsigned int originalNumMeshes = pScene->mNumMeshes; + unsigned int targetIndex = 0; + for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { + // Do not process point cloud, ExecuteOnMesh works only with faces data if ((pScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType::aiPrimitiveType_POINT) && ExecuteOnMesh(pScene->mMeshes[i])) { - removeMesh(pScene, i); - --i; //the current i is removed, do not skip the next one + delete pScene->mMeshes[i]; + // Not strictly required, but clean: + pScene->mMeshes[i] = nullptr; + } else { + meshMap[i] = targetIndex; + pScene->mMeshes[targetIndex] = pScene->mMeshes[i]; + ++targetIndex; } } + pScene->mNumMeshes = targetIndex; + + if (meshMap.size() < originalNumMeshes) { + updateSceneGraph(pScene->mRootNode, meshMap); + } + ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); } -static void removeMesh(aiScene* pScene, unsigned const index) { - //we start at index and copy the pointers one position forward - //save the mesh pointer to delete it later - auto delete_me = pScene->mMeshes[index]; - for (unsigned i = index; i < pScene->mNumMeshes - 1; ++i) { - pScene->mMeshes[i] = pScene->mMeshes[i+1]; - } - pScene->mMeshes[pScene->mNumMeshes - 1] = nullptr; - --(pScene->mNumMeshes); - delete delete_me; - - //removing a mesh also requires updating all references to it in the scene graph - updateSceneGraph(pScene->mRootNode, index); -} - -static void updateSceneGraph(aiNode* pNode, unsigned const index) { +static void updateSceneGraph(aiNode* pNode, const std::unordered_map& meshMap) { + unsigned int targetIndex = 0; for (unsigned i = 0; i < pNode->mNumMeshes; ++i) { - if (pNode->mMeshes[i] > index) { - --(pNode->mMeshes[i]); - continue; - } - if (pNode->mMeshes[i] == index) { - for (unsigned j = i; j < pNode->mNumMeshes -1; ++j) { - pNode->mMeshes[j] = pNode->mMeshes[j+1]; - } - --(pNode->mNumMeshes); - --i; - continue; + const unsigned int sourceMeshIndex = pNode->mMeshes[i]; + auto it = meshMap.find(sourceMeshIndex); + if (it != meshMap.end()) { + pNode->mMeshes[targetIndex] = it->second; + ++targetIndex; } } + pNode->mNumMeshes = targetIndex; //recurse to all children for (unsigned i = 0; i < pNode->mNumChildren; ++i) { - updateSceneGraph(pNode->mChildren[i], index); + updateSceneGraph(pNode->mChildren[i], meshMap); } } diff --git a/include/assimp/scene.h b/include/assimp/scene.h index a189f5700..2a9a77b02 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -335,12 +335,15 @@ struct aiScene /** * @brief The global metadata assigned to the scene itself. * - * This data contains global metadata which belongs to the scene like - * unit-conversions, versions, vendors or other model-specific data. This + * This data contains global metadata which belongs to the scene like + * unit-conversions, versions, vendors or other model-specific data. This * can be used to store format-specific metadata as well. */ C_STRUCT aiMetadata* mMetaData; + /** The name of the scene itself. + */ + C_STRUCT aiString mName; #ifdef __cplusplus diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index 88ad9640a..280a5199d 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -40,8 +40,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "UnitTestPCH.h" +#include "../../include/assimp/scene.h" #include "PostProcessing/FindDegenerates.h" +#include + using namespace std; using namespace Assimp; @@ -147,3 +150,59 @@ TEST_F(FindDegeneratesProcessTest, testDegeneratesRemovalWithAreaCheck) { EXPECT_EQ(mMesh->mNumUVComponents[1] - 100, mMesh->mNumFaces); } + +namespace +{ + std::unique_ptr getDegenerateMesh() + { + std::unique_ptr mesh(new aiMesh); + mesh->mNumVertices = 2; + mesh->mVertices = new aiVector3D[2]; + mesh->mVertices[0] = aiVector3D{ 0.0f, 0.0f, 0.0f }; + mesh->mVertices[1] = aiVector3D{ 1.0f, 0.0f, 0.0f }; + mesh->mNumFaces = 1; + mesh->mFaces = new aiFace[1]; + mesh->mFaces[0].mNumIndices = 3; + mesh->mFaces[0].mIndices = new unsigned int[3]; + mesh->mFaces[0].mIndices[0] = 0; + mesh->mFaces[0].mIndices[1] = 1; + mesh->mFaces[0].mIndices[2] = 0; + return mesh; + } +} + +TEST_F(FindDegeneratesProcessTest, meshRemoval) { + mProcess->EnableAreaCheck(true); + mProcess->EnableInstantRemoval(true); + mProcess->ExecuteOnMesh(mMesh); + + std::unique_ptr scene(new aiScene); + scene->mNumMeshes = 5; + scene->mMeshes = new aiMesh*[5]; + + /// Use the mesh which doesn't get completely stripped of faces from the main test. + aiMesh* meshWhichSurvives = mMesh; + mMesh = nullptr; + + scene->mMeshes[0] = getDegenerateMesh().release(); + scene->mMeshes[1] = getDegenerateMesh().release(); + scene->mMeshes[2] = meshWhichSurvives; + scene->mMeshes[3] = getDegenerateMesh().release(); + scene->mMeshes[4] = getDegenerateMesh().release(); + + scene->mRootNode = new aiNode; + scene->mRootNode->mNumMeshes = 5; + scene->mRootNode->mMeshes = new unsigned int[5]; + scene->mRootNode->mMeshes[0] = 0; + scene->mRootNode->mMeshes[1] = 1; + scene->mRootNode->mMeshes[2] = 2; + scene->mRootNode->mMeshes[3] = 3; + scene->mRootNode->mMeshes[4] = 4; + + mProcess->Execute(scene.get()); + + EXPECT_EQ(scene->mNumMeshes, 1); + EXPECT_EQ(scene->mMeshes[0], meshWhichSurvives); + EXPECT_EQ(scene->mRootNode->mNumMeshes, 1); + EXPECT_EQ(scene->mRootNode->mMeshes[0], 0); +}