summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/assimp/code/JoinVerticesProcess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/assimp/code/JoinVerticesProcess.cpp')
-rw-r--r--src/3rdparty/assimp/code/JoinVerticesProcess.cpp684
1 files changed, 343 insertions, 341 deletions
diff --git a/src/3rdparty/assimp/code/JoinVerticesProcess.cpp b/src/3rdparty/assimp/code/JoinVerticesProcess.cpp
index bd75bae3b..9cd34e1d0 100644
--- a/src/3rdparty/assimp/code/JoinVerticesProcess.cpp
+++ b/src/3rdparty/assimp/code/JoinVerticesProcess.cpp
@@ -3,12 +3,12 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2016, 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
+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
@@ -25,16 +25,16 @@ conditions are met:
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
+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
+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
+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
+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.
---------------------------------------------------------------------------
*/
@@ -43,372 +43,374 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* for all imported meshes
*/
-#include "AssimpPCH.h"
+
#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
+ // nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
JoinVerticesProcess::~JoinVerticesProcess()
{
- // nothing to do here
+ // 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;
+ 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
- sprintf(szBuff,"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;
+ 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)
{
- BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_COLOR_SETS == 8);
- BOOST_STATIC_ASSERT( 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.
- BOOST_STATIC_ASSERT(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 comparision succeeds.
- // By this method the non-present attributes are effectively ignored in the comparision.
- 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 upto 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);
-
- 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);
- }
- }
-
- 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;
+ 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