diff --git a/code/PostProcessing/JoinVerticesProcess.cpp b/code/PostProcessing/JoinVerticesProcess.cpp index 184e8d745..745ce1a5e 100644 --- a/code/PostProcessing/JoinVerticesProcess.cpp +++ b/code/PostProcessing/JoinVerticesProcess.cpp @@ -5,8 +5,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, @@ -45,41 +43,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * for all imported meshes */ - #ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS #include "JoinVerticesProcess.h" #include "ProcessHelper.h" #include #include + #include #include +#include using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -JoinVerticesProcess::JoinVerticesProcess() -{ +JoinVerticesProcess::JoinVerticesProcess() { // nothing to do here } // ------------------------------------------------------------------------------------------------ // Destructor, private as well -JoinVerticesProcess::~JoinVerticesProcess() -{ +JoinVerticesProcess::~JoinVerticesProcess() { // nothing to do here } // ------------------------------------------------------------------------------------------------ // Returns whether the processing step is present in the given flag field. -bool JoinVerticesProcess::IsActive( unsigned int pFlags) const -{ +bool JoinVerticesProcess::IsActive( unsigned int pFlags) const { return (pFlags & aiProcess_JoinIdenticalVertices) != 0; } // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. -void JoinVerticesProcess::Execute( aiScene* pScene) -{ +void JoinVerticesProcess::Execute( aiScene* pScene) { ASSIMP_LOG_DEBUG("JoinVerticesProcess begin"); // get the total number of vertices BEFORE the step is executed @@ -92,27 +87,29 @@ void JoinVerticesProcess::Execute( aiScene* pScene) // execute the step int iNumVertices = 0; - for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { iNumVertices += ProcessMesh( pScene->mMeshes[a],a); + } + + pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; // if logging is active, print detailed statistics if (!DefaultLogger::isNullLogger()) { if (iNumOldVertices == iNumVertices) { ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); - } else { - ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, - " out: ", iNumVertices, " | ~", - ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); + return; } + + // Show statistics + ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, + " out: ", iNumVertices, " | ~", + ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); } - - pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; } namespace { -bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) -{ +bool areVerticesEqual(const Vertex &lhs, const Vertex &rhs, bool complex) { // A little helper to find locally close vertices faster. // Try to reuse the lookup table from the last step. const static float epsilon = 1e-5f; @@ -171,8 +168,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { // ---------------------------------------------------------------------------- // Position, if present (check made for aiAnimMesh) - if (pMesh->mVertices) - { + if (pMesh->mVertices) { delete [] pMesh->mVertices; pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { @@ -181,8 +177,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { } // Normals, if present - if (pMesh->mNormals) - { + if (pMesh->mNormals) { delete [] pMesh->mNormals; pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { @@ -190,8 +185,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { } } // Tangents, if present - if (pMesh->mTangents) - { + if (pMesh->mTangents) { delete [] pMesh->mTangents; pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { @@ -199,8 +193,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { } } // Bitangents as well - if (pMesh->mBitangents) - { + if (pMesh->mBitangents) { delete [] pMesh->mBitangents; pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; for (unsigned int a = 0; a < pMesh->mNumVertices; a++) { @@ -208,8 +201,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { } } // Vertex colors - for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) - { + for (unsigned int a = 0; pMesh->HasVertexColors(a); a++) { delete [] pMesh->mColors[a]; pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices]; for( unsigned int b = 0; b < pMesh->mNumVertices; b++) { @@ -217,8 +209,7 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { } } // Texture coords - for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) - { + for (unsigned int a = 0; pMesh->HasTextureCoords(a); a++) { delete [] pMesh->mTextureCoords[a]; pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices]; for (unsigned int b = 0; b < pMesh->mNumVertices; b++) { @@ -226,12 +217,40 @@ void updateXMeshVertices(XMesh *pMesh, std::vector &uniqueVertices) { } } } + } // namespace // ------------------------------------------------------------------------------------------------ // Unites identical vertices in the given mesh -int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) -{ +// combine hashes +inline void hash_combine(std::size_t &) { + // empty +} + +template +inline void hash_combine(std::size_t& seed, const T& v, Rest... rest) { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + hash_combine(seed, rest...); +} +//template specialization for std::hash for Vertex +template<> +struct std::hash { + std::size_t operator()(Vertex const& v) const noexcept { + size_t seed = 0; + hash_combine(seed, v.position.x ,v.position.y,v.position.z); + return seed; + } +}; +//template specialization for std::equal_to for Vertex +template<> +struct std::equal_to { + bool operator()(const Vertex &lhs, const Vertex &rhs) const { + return areVerticesEqual(lhs, rhs, false); + } +}; +// now start the JoinVerticesProcess +int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) { static_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 8, "AI_MAX_NUMBER_OF_COLOR_SETS == 8"); static_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8, "AI_MAX_NUMBER_OF_TEXTURECOORDS == 8"); @@ -245,8 +264,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) // multiple meshes) std::unordered_set usedVertexIndices; usedVertexIndices.reserve(pMesh->mNumVertices); - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { aiFace& face = pMesh->mFaces[a]; for( unsigned int b = 0; b < face.mNumIndices; b++) { usedVertexIndices.insert(face.mIndices[b]); @@ -292,7 +310,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) // Run an optimized code path if we don't have multiple UVs or vertex colors. // This should yield false in more than 99% of all imports ... - const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1); const bool hasAnimMeshes = pMesh->mNumAnimMeshes > 0; // We'll never have more vertices afterwards. @@ -303,72 +320,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) uniqueAnimatedVertices[animMeshIndex].reserve(pMesh->mNumVertices); } } - + // a map that maps a vertix to its new index + std::unordered_map vertex2Index; + // we can not end up with more vertices than we started with + vertex2Index.reserve(pMesh->mNumVertices); // Now check each vertex if it brings something new to the table + int newIndex = 0; for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + // if the vertex is unused Do nothing if (usedVertexIndices.find(a) == usedVertexIndices.end()) { continue; } - // collect the vertex data Vertex v(pMesh,a); - - // collect all vertices that are close enough to the given position - vertexFinder->FindIdenticalPositions( v.position, verticesFound); - unsigned int matchIndex = 0xffffffff; - - // check all unique vertices close to the position if this vertex is already present among them - for( unsigned int b = 0; b < verticesFound.size(); b++) { - const unsigned int vidx = verticesFound[b]; - const unsigned int uidx = replaceIndex[ vidx]; - if( uidx & 0x80000000) - continue; - - const Vertex& uv = uniqueVertices[ uidx]; - - if (!areVerticesEqual(v, uv, complex)) { - continue; - } - - if (hasAnimMeshes) { - // If given vertex is animated, then it has to be preserver 1 to 1 (base mesh and animated mesh require same topology) - // NOTE: not doing this totaly breaks anim meshes as they don't have their own faces (they use pMesh->mFaces) - bool breaksAnimMesh = false; - for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { - const Vertex& animatedUV = uniqueAnimatedVertices[animMeshIndex][ uidx]; - Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); - if (!areVerticesEqual(aniMeshVertex, animatedUV, complex)) { - breaksAnimMesh = true; - break; - } - } - if (breaksAnimMesh) { - continue; - } - } - - // we're still here -> this vertex perfectly matches our given vertex - matchIndex = uidx; - break; - } - - // found a replacement vertex among the uniques? - if( matchIndex != 0xffffffff) - { - // store where to found the matching unique vertex - replaceIndex[a] = matchIndex | 0x80000000; - } - else - { - // no unique vertex matches it up to now -> so add it - replaceIndex[a] = (unsigned int)uniqueVertices.size(); - uniqueVertices.push_back( v); + // is the vertex already in the map? + auto it = vertex2Index.find(v); + // if the vertex is not in the map then it is a new vertex add it. + if (it == vertex2Index.end()) { + // this is a new vertex give it a new index + vertex2Index[v] = newIndex; + //keep track of its index and increment 1 + replaceIndex[a] = newIndex++; + // add the vertex to the unique vertices + uniqueVertices.push_back(v); if (hasAnimMeshes) { for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) { Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a); - uniqueAnimatedVertices[animMeshIndex].push_back(aniMeshVertex); + uniqueAnimatedVertices[animMeshIndex].push_back(v); } } + } else{ + // if the vertex is already there just find the replace index that is appropriate to it + replaceIndex[a] = it->second; } } @@ -394,8 +377,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) } // adjust the indices in all faces - for( unsigned int a = 0; a < pMesh->mNumFaces; a++) - { + for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { aiFace& face = pMesh->mFaces[a]; for( unsigned int b = 0; b < face.mNumIndices; b++) { face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;