diff options
| author | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
|---|---|---|
| committer | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
| commit | ca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch) | |
| tree | b06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/external/assimp-4.1.0/code/JoinVerticesProcess.cpp | |
| parent | Forced rename of platform folders in cmake dir. Git didn't pick this up before. (diff) | |
| download | nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip | |
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/external/assimp-4.1.0/code/JoinVerticesProcess.cpp')
| -rw-r--r-- | NvCloth/samples/external/assimp-4.1.0/code/JoinVerticesProcess.cpp | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/NvCloth/samples/external/assimp-4.1.0/code/JoinVerticesProcess.cpp b/NvCloth/samples/external/assimp-4.1.0/code/JoinVerticesProcess.cpp new file mode 100644 index 0000000..17b0ab0 --- /dev/null +++ b/NvCloth/samples/external/assimp-4.1.0/code/JoinVerticesProcess.cpp @@ -0,0 +1,417 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the post processing step to join identical vertices + * for all imported meshes + */ + + +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS + +#include "JoinVerticesProcess.h" +#include "ProcessHelper.h" +#include "Vertex.h" +#include "TinyFormatter.h" +#include <stdio.h> + +using namespace Assimp; +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +JoinVerticesProcess::JoinVerticesProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +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 +{ + return (pFlags & aiProcess_JoinIdenticalVertices) != 0; +} +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void JoinVerticesProcess::Execute( aiScene* pScene) +{ + DefaultLogger::get()->debug("JoinVerticesProcess begin"); + + // get the total number of vertices BEFORE the step is executed + int iNumOldVertices = 0; + if (!DefaultLogger::isNullLogger()) { + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { + iNumOldVertices += pScene->mMeshes[a]->mNumVertices; + } + } + + // execute the step + int iNumVertices = 0; + for( unsigned int a = 0; a < pScene->mNumMeshes; a++) + iNumVertices += ProcessMesh( pScene->mMeshes[a],a); + + // if logging is active, print detailed statistics + if (!DefaultLogger::isNullLogger()) + { + if (iNumOldVertices == iNumVertices) + { + DefaultLogger::get()->debug("JoinVerticesProcess finished "); + } else + { + char szBuff[128]; // should be sufficiently large in every case + ::ai_snprintf(szBuff,128,"JoinVerticesProcess finished | Verts in: %i out: %i | ~%.1f%%", + iNumOldVertices, + iNumVertices, + ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f); + DefaultLogger::get()->info(szBuff); + } + } + + pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; +} + +// ------------------------------------------------------------------------------------------------ +// Unites identical vertices in the given mesh +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"); + + // Return early if we don't have any positions + if (!pMesh->HasPositions() || !pMesh->HasFaces()) { + return 0; + } + + // We'll never have more vertices afterwards. + std::vector<Vertex> uniqueVertices; + uniqueVertices.reserve( pMesh->mNumVertices); + + // For each vertex the index of the vertex it was replaced by. + // Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark + // whether a new vertex was created for the index (true) or if it was replaced by an existing + // unique vertex (false). This saves an additional std::vector<bool> and greatly enhances + // branching performance. + static_assert(AI_MAX_VERTICES == 0x7fffffff, "AI_MAX_VERTICES == 0x7fffffff"); + std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff); + + // 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; + // float posEpsilonSqr; + SpatialSort* vertexFinder = NULL; + SpatialSort _vertexFinder; + + typedef std::pair<SpatialSort,float> SpatPair; + if (shared) { + std::vector<SpatPair >* avf; + shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); + if (avf) { + SpatPair& blubb = (*avf)[meshIndex]; + vertexFinder = &blubb.first; + // posEpsilonSqr = blubb.second; + } + } + if (!vertexFinder) { + // bad, need to compute it. + _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); + vertexFinder = &_vertexFinder; + // posEpsilonSqr = ComputePositionEpsilon(pMesh); + } + + // Squared because we check against squared length of the vector difference + static const float squareEpsilon = epsilon * epsilon; + + // Again, better waste some bytes than a realloc ... + std::vector<unsigned int> verticesFound; + verticesFound.reserve(10); + + // 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); + + // Now check each vertex if it brings something new to the table + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + // 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]; + // Position mismatch is impossible - the vertex finder already discarded all non-matching positions + + // We just test the other attributes even if they're not present in the mesh. + // In this case they're initialized to 0 so the comparison succeeds. + // By this method the non-present attributes are effectively ignored in the comparison. + if( (uv.normal - v.normal).SquareLength() > squareEpsilon) + continue; + if( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon) + continue; + if( (uv.tangent - v.tangent).SquareLength() > squareEpsilon) + continue; + if( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon) + continue; + + // Usually we won't have vertex colors or multiple UVs, so we can skip from here + // Actually this increases runtime performance slightly, at least if branch + // prediction is on our side. + if (complex){ + // manually unrolled because continue wouldn't work as desired in an inner loop, + // also because some compilers seem to fail the task. Colors and UV coords + // are interleaved since the higher entries are most likely to be + // zero and thus useless. By interleaving the arrays, vertices are, + // on average, rejected earlier. + + if( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon) + continue; + + if( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon) + continue; + + if( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon) + continue; + + if( (uv.texcoords[4] - v.texcoords[4]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon) + continue; + + if( (uv.texcoords[5] - v.texcoords[5]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[4], v.colors[4]) > squareEpsilon) + continue; + + if( (uv.texcoords[6] - v.texcoords[6]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[5], v.colors[5]) > squareEpsilon) + continue; + + if( (uv.texcoords[7] - v.texcoords[7]).SquareLength() > squareEpsilon) + continue; + if( GetColorDifference( uv.colors[6], v.colors[6]) > squareEpsilon) + continue; + + if( GetColorDifference( uv.colors[7], v.colors[7]) > squareEpsilon) + 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); + } + } + + if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { + DefaultLogger::get()->debug((Formatter::format(), + "Mesh ",meshIndex, + " (", + (pMesh->mName.length ? pMesh->mName.data : "unnamed"), + ") | Verts in: ",pMesh->mNumVertices, + " out: ", + uniqueVertices.size(), + " | ~", + ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f, + "%" + )); + } + + // replace vertex data with the unique data sets + pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); + + // ---------------------------------------------------------------------------- + // NOTE - we're *not* calling Vertex::SortBack() because it would check for + // presence of every single vertex component once PER VERTEX. And our CPU + // dislikes branches, even if they're easily predictable. + // ---------------------------------------------------------------------------- + + // Position + delete [] pMesh->mVertices; + pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) + pMesh->mVertices[a] = uniqueVertices[a].position; + + // Normals, if present + if( pMesh->mNormals) + { + delete [] pMesh->mNormals; + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mNormals[a] = uniqueVertices[a].normal; + } + } + // Tangents, if present + if( pMesh->mTangents) + { + delete [] pMesh->mTangents; + pMesh->mTangents = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mTangents[a] = uniqueVertices[a].tangent; + } + } + // Bitangents as well + if( pMesh->mBitangents) + { + delete [] pMesh->mBitangents; + pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices]; + for( unsigned int a = 0; a < pMesh->mNumVertices; a++) { + pMesh->mBitangents[a] = uniqueVertices[a].bitangent; + } + } + // Vertex colors + 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++) { + pMesh->mColors[a][b] = uniqueVertices[b].colors[a]; + } + } + // Texture coords + 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++) { + pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a]; + } + } + + // adjust the indices in all faces + 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; + } + } + + // adjust bone vertex weights. + for( int a = 0; a < (int)pMesh->mNumBones; a++) { + aiBone* bone = pMesh->mBones[a]; + std::vector<aiVertexWeight> newWeights; + newWeights.reserve( bone->mNumWeights); + + if ( NULL != bone->mWeights ) { + for ( unsigned int b = 0; b < bone->mNumWeights; b++ ) { + const aiVertexWeight& ow = bone->mWeights[ b ]; + // if the vertex is a unique one, translate it + if ( !( replaceIndex[ ow.mVertexId ] & 0x80000000 ) ) { + aiVertexWeight nw; + nw.mVertexId = replaceIndex[ ow.mVertexId ]; + nw.mWeight = ow.mWeight; + newWeights.push_back( nw ); + } + } + } else { + DefaultLogger::get()->error( "X-Export: aiBone shall contain weights, but pointer to them is NULL." ); + } + + if (newWeights.size() > 0) { + // kill the old and replace them with the translated weights + delete [] bone->mWeights; + bone->mNumWeights = (unsigned int)newWeights.size(); + + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); + } + else { + + /* NOTE: + * + * In the algorithm above we're assuming that there are no vertices + * with a different bone weight setup at the same position. That wouldn't + * make sense, but it is not absolutely impossible. SkeletonMeshBuilder + * for example generates such input data if two skeleton points + * share the same position. Again this doesn't make sense but is + * reality for some model formats (MD5 for example uses these special + * nodes as attachment tags for its weapons). + * + * Then it is possible that a bone has no weights anymore .... as a quick + * workaround, we're just removing these bones. If they're animated, + * model geometry might be modified but at least there's no risk of a crash. + */ + delete bone; + --pMesh->mNumBones; + for (unsigned int n = a; n < pMesh->mNumBones; ++n) { + pMesh->mBones[n] = pMesh->mBones[n+1]; + } + + --a; + DefaultLogger::get()->warn("Removing bone -> no weights remaining"); + } + } + return pMesh->mNumVertices; +} + +#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS |