vrshoot

diff libs/assimp/JoinVerticesProcess.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/JoinVerticesProcess.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,414 @@
     1.4 +/*
     1.5 +---------------------------------------------------------------------------
     1.6 +Open Asset Import Library (assimp)
     1.7 +---------------------------------------------------------------------------
     1.8 +
     1.9 +Copyright (c) 2006-2012, assimp team
    1.10 +
    1.11 +All rights reserved.
    1.12 +
    1.13 +Redistribution and use of this software in source and binary forms, 
    1.14 +with or without modification, are permitted provided that the following 
    1.15 +conditions are met:
    1.16 +
    1.17 +* Redistributions of source code must retain the above
    1.18 +  copyright notice, this list of conditions and the
    1.19 +  following disclaimer.
    1.20 +
    1.21 +* Redistributions in binary form must reproduce the above
    1.22 +  copyright notice, this list of conditions and the
    1.23 +  following disclaimer in the documentation and/or other
    1.24 +  materials provided with the distribution.
    1.25 +
    1.26 +* Neither the name of the assimp team, nor the names of its
    1.27 +  contributors may be used to endorse or promote products
    1.28 +  derived from this software without specific prior
    1.29 +  written permission of the assimp team.
    1.30 +
    1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.42 +---------------------------------------------------------------------------
    1.43 +*/
    1.44 +
    1.45 +/** @file Implementation of the post processing step to join identical vertices
    1.46 + * for all imported meshes
    1.47 + */
    1.48 +
    1.49 +#include "AssimpPCH.h"
    1.50 +#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
    1.51 +
    1.52 +#include "JoinVerticesProcess.h"
    1.53 +#include "ProcessHelper.h"
    1.54 +#include "Vertex.h"
    1.55 +#include "TinyFormatter.h"
    1.56 +
    1.57 +using namespace Assimp;
    1.58 +// ------------------------------------------------------------------------------------------------
    1.59 +// Constructor to be privately used by Importer
    1.60 +JoinVerticesProcess::JoinVerticesProcess()
    1.61 +{
    1.62 +	// nothing to do here
    1.63 +}
    1.64 +
    1.65 +// ------------------------------------------------------------------------------------------------
    1.66 +// Destructor, private as well
    1.67 +JoinVerticesProcess::~JoinVerticesProcess()
    1.68 +{
    1.69 +	// nothing to do here
    1.70 +}
    1.71 +
    1.72 +// ------------------------------------------------------------------------------------------------
    1.73 +// Returns whether the processing step is present in the given flag field.
    1.74 +bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
    1.75 +{
    1.76 +	return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
    1.77 +}
    1.78 +// ------------------------------------------------------------------------------------------------
    1.79 +// Executes the post processing step on the given imported data.
    1.80 +void JoinVerticesProcess::Execute( aiScene* pScene)
    1.81 +{
    1.82 +	DefaultLogger::get()->debug("JoinVerticesProcess begin");
    1.83 +
    1.84 +	// get the total number of vertices BEFORE the step is executed
    1.85 +	int iNumOldVertices = 0;
    1.86 +	if (!DefaultLogger::isNullLogger()) {
    1.87 +		for( unsigned int a = 0; a < pScene->mNumMeshes; a++)	{
    1.88 +			iNumOldVertices +=	pScene->mMeshes[a]->mNumVertices;
    1.89 +		}
    1.90 +	}
    1.91 +
    1.92 +	// execute the step
    1.93 +	int iNumVertices = 0;
    1.94 +	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
    1.95 +		iNumVertices +=	ProcessMesh( pScene->mMeshes[a],a);
    1.96 +
    1.97 +	// if logging is active, print detailed statistics
    1.98 +	if (!DefaultLogger::isNullLogger())
    1.99 +	{
   1.100 +		if (iNumOldVertices == iNumVertices)
   1.101 +		{
   1.102 +			DefaultLogger::get()->debug("JoinVerticesProcess finished ");
   1.103 +		} else
   1.104 +		{
   1.105 +			char szBuff[128]; // should be sufficiently large in every case
   1.106 +			sprintf(szBuff,"JoinVerticesProcess finished | Verts in: %i out: %i | ~%.1f%%",
   1.107 +				iNumOldVertices,
   1.108 +				iNumVertices,
   1.109 +				((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f);
   1.110 +			DefaultLogger::get()->info(szBuff);
   1.111 +		}
   1.112 +	}
   1.113 +
   1.114 +	pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
   1.115 +}
   1.116 +
   1.117 +// ------------------------------------------------------------------------------------------------
   1.118 +// Unites identical vertices in the given mesh
   1.119 +int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
   1.120 +{
   1.121 +	BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_COLOR_SETS    == 8);
   1.122 +	BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_TEXTURECOORDS == 8);
   1.123 +
   1.124 +	// Return early if we don't have any positions
   1.125 +	if (!pMesh->HasPositions() || !pMesh->HasFaces()) {
   1.126 +		return 0;
   1.127 +	}
   1.128 +
   1.129 +	// We'll never have more vertices afterwards.
   1.130 +	std::vector<Vertex> uniqueVertices;
   1.131 +	uniqueVertices.reserve( pMesh->mNumVertices);
   1.132 +
   1.133 +	// For each vertex the index of the vertex it was replaced by.
   1.134 +	// Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark
   1.135 +	//	whether a new vertex was created for the index (true) or if it was replaced by an existing
   1.136 +	//	unique vertex (false). This saves an additional std::vector<bool> and greatly enhances
   1.137 +	//	branching performance.
   1.138 +	BOOST_STATIC_ASSERT(AI_MAX_VERTICES == 0x7fffffff);
   1.139 +	std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
   1.140 +
   1.141 +	// A little helper to find locally close vertices faster.
   1.142 +	// Try to reuse the lookup table from the last step.
   1.143 +	const static float epsilon = 1e-5f;
   1.144 +	// float posEpsilonSqr;
   1.145 +	SpatialSort* vertexFinder = NULL;
   1.146 +	SpatialSort _vertexFinder;
   1.147 +
   1.148 +	typedef std::pair<SpatialSort,float> SpatPair;
   1.149 +	if (shared)	{
   1.150 +		std::vector<SpatPair >* avf;
   1.151 +		shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
   1.152 +		if (avf)	{
   1.153 +			SpatPair& blubb = (*avf)[meshIndex];
   1.154 +			vertexFinder  = &blubb.first;
   1.155 +			// posEpsilonSqr = blubb.second;
   1.156 +		}
   1.157 +	}
   1.158 +	if (!vertexFinder)	{
   1.159 +		// bad, need to compute it.
   1.160 +		_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
   1.161 +		vertexFinder = &_vertexFinder; 
   1.162 +		// posEpsilonSqr = ComputePositionEpsilon(pMesh);
   1.163 +	}
   1.164 +
   1.165 +	// Squared because we check against squared length of the vector difference
   1.166 +	static const float squareEpsilon = epsilon * epsilon;
   1.167 +
   1.168 +	// Again, better waste some bytes than a realloc ...
   1.169 +	std::vector<unsigned int> verticesFound;
   1.170 +	verticesFound.reserve(10);
   1.171 +
   1.172 +	// Run an optimized code path if we don't have multiple UVs or vertex colors.
   1.173 +	// This should yield false in more than 99% of all imports ...
   1.174 +	const bool complex = ( pMesh->GetNumColorChannels() > 0 || pMesh->GetNumUVChannels() > 1);
   1.175 +
   1.176 +	// Now check each vertex if it brings something new to the table
   1.177 +	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)	{
   1.178 +		// collect the vertex data
   1.179 +		Vertex v(pMesh,a);
   1.180 +
   1.181 +		// collect all vertices that are close enough to the given position
   1.182 +		vertexFinder->FindIdenticalPositions( v.position, verticesFound);
   1.183 +		unsigned int matchIndex = 0xffffffff;
   1.184 +
   1.185 +		// check all unique vertices close to the position if this vertex is already present among them
   1.186 +		for( unsigned int b = 0; b < verticesFound.size(); b++)	{
   1.187 +
   1.188 +			const unsigned int vidx = verticesFound[b];
   1.189 +			const unsigned int uidx = replaceIndex[ vidx];
   1.190 +			if( uidx & 0x80000000)
   1.191 +				continue;
   1.192 +
   1.193 +			const Vertex& uv = uniqueVertices[ uidx];
   1.194 +			// Position mismatch is impossible - the vertex finder already discarded all non-matching positions
   1.195 +
   1.196 +			// We just test the other attributes even if they're not present in the mesh.
   1.197 +			// In this case they're initialized to 0 so the comparision succeeds. 
   1.198 +			// By this method the non-present attributes are effectively ignored in the comparision.
   1.199 +			if( (uv.normal - v.normal).SquareLength() > squareEpsilon)
   1.200 +				continue;
   1.201 +			if( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon)
   1.202 +				continue;
   1.203 +			if( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
   1.204 +				continue;
   1.205 +			if( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon)
   1.206 +				continue;
   1.207 +
   1.208 +			// Usually we won't have vertex colors or multiple UVs, so we can skip from here
   1.209 +			// Actually this increases runtime performance slightly, at least if branch
   1.210 +			// prediction is on our side.
   1.211 +			if (complex){
   1.212 +				// manually unrolled because continue wouldn't work as desired in an inner loop, 
   1.213 +				// also because some compilers seem to fail the task. Colors and UV coords
   1.214 +				// are interleaved since the higher entries are most likely to be
   1.215 +				// zero and thus useless. By interleaving the arrays, vertices are,
   1.216 +				// on average, rejected earlier.
   1.217 +
   1.218 +				if( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon)
   1.219 +					continue;
   1.220 +				if( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon)
   1.221 +					continue;
   1.222 +
   1.223 +				if( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon)
   1.224 +					continue;
   1.225 +				if( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon)
   1.226 +					continue;
   1.227 +
   1.228 +				if( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon)
   1.229 +					continue;
   1.230 +				if( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon)
   1.231 +					continue;
   1.232 +
   1.233 +				if( (uv.texcoords[4] - v.texcoords[4]).SquareLength() > squareEpsilon)
   1.234 +					continue;
   1.235 +				if( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon)
   1.236 +					continue;
   1.237 +
   1.238 +				if( (uv.texcoords[5] - v.texcoords[5]).SquareLength() > squareEpsilon)
   1.239 +					continue;
   1.240 +				if( GetColorDifference( uv.colors[4], v.colors[4]) > squareEpsilon)
   1.241 +					continue;
   1.242 +
   1.243 +				if( (uv.texcoords[6] - v.texcoords[6]).SquareLength() > squareEpsilon)
   1.244 +					continue;
   1.245 +				if( GetColorDifference( uv.colors[5], v.colors[5]) > squareEpsilon)
   1.246 +					continue;
   1.247 +
   1.248 +				if( (uv.texcoords[7] - v.texcoords[7]).SquareLength() > squareEpsilon)
   1.249 +					continue;
   1.250 +				if( GetColorDifference( uv.colors[6], v.colors[6]) > squareEpsilon)
   1.251 +					continue;
   1.252 +				
   1.253 +				if( GetColorDifference( uv.colors[7], v.colors[7]) > squareEpsilon)
   1.254 +					continue;
   1.255 +			}
   1.256 +
   1.257 +			// we're still here -> this vertex perfectly matches our given vertex
   1.258 +			matchIndex = uidx;
   1.259 +			break;
   1.260 +		}
   1.261 +
   1.262 +		// found a replacement vertex among the uniques?
   1.263 +		if( matchIndex != 0xffffffff)
   1.264 +		{
   1.265 +			// store where to found the matching unique vertex
   1.266 +			replaceIndex[a] = matchIndex | 0x80000000;
   1.267 +		}
   1.268 +		else
   1.269 +		{
   1.270 +			// no unique vertex matches it upto now -> so add it
   1.271 +			replaceIndex[a] = (unsigned int)uniqueVertices.size();
   1.272 +			uniqueVertices.push_back( v);
   1.273 +		}
   1.274 +	}
   1.275 +
   1.276 +	if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE)	{
   1.277 +		DefaultLogger::get()->debug((Formatter::format(),
   1.278 +			"Mesh ",meshIndex,
   1.279 +			" (",
   1.280 +			(pMesh->mName.length ? pMesh->mName.data : "unnamed"),
   1.281 +			") | Verts in: ",pMesh->mNumVertices,
   1.282 +			" out: ",
   1.283 +			uniqueVertices.size(),
   1.284 +			" | ~",
   1.285 +			((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f,
   1.286 +			"%"
   1.287 +		));
   1.288 +	}
   1.289 +
   1.290 +	// replace vertex data with the unique data sets
   1.291 +	pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
   1.292 +
   1.293 +	// ----------------------------------------------------------------------------
   1.294 +	// NOTE - we're *not* calling Vertex::SortBack() because it would check for 
   1.295 +	// presence of every single vertex component once PER VERTEX. And our CPU 
   1.296 +	// dislikes branches, even if they're easily predictable.
   1.297 +	// ----------------------------------------------------------------------------
   1.298 +
   1.299 +	// Position
   1.300 +	delete [] pMesh->mVertices;
   1.301 +	pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
   1.302 +	for( unsigned int a = 0; a < pMesh->mNumVertices; a++)
   1.303 +		pMesh->mVertices[a] = uniqueVertices[a].position;
   1.304 +
   1.305 +	// Normals, if present
   1.306 +	if( pMesh->mNormals)
   1.307 +	{
   1.308 +		delete [] pMesh->mNormals;
   1.309 +		pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
   1.310 +		for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
   1.311 +			pMesh->mNormals[a] = uniqueVertices[a].normal;
   1.312 +		}
   1.313 +	}
   1.314 +	// Tangents, if present
   1.315 +	if( pMesh->mTangents)
   1.316 +	{
   1.317 +		delete [] pMesh->mTangents;
   1.318 +		pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
   1.319 +		for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
   1.320 +			pMesh->mTangents[a] = uniqueVertices[a].tangent;
   1.321 +		}
   1.322 +	}
   1.323 +	// Bitangents as well
   1.324 +	if( pMesh->mBitangents)
   1.325 +	{
   1.326 +		delete [] pMesh->mBitangents;
   1.327 +		pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
   1.328 +		for( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
   1.329 +			pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
   1.330 +		}
   1.331 +	}
   1.332 +	// Vertex colors
   1.333 +	for( unsigned int a = 0; pMesh->HasVertexColors(a); a++)
   1.334 +	{
   1.335 +		delete [] pMesh->mColors[a];
   1.336 +		pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
   1.337 +		for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
   1.338 +			pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
   1.339 +		}
   1.340 +	}
   1.341 +	// Texture coords
   1.342 +	for( unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
   1.343 +	{
   1.344 +		delete [] pMesh->mTextureCoords[a];
   1.345 +		pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
   1.346 +		for( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
   1.347 +			pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
   1.348 +		}
   1.349 +	}
   1.350 +
   1.351 +	// adjust the indices in all faces
   1.352 +	for( unsigned int a = 0; a < pMesh->mNumFaces; a++)
   1.353 +	{
   1.354 +		aiFace& face = pMesh->mFaces[a];
   1.355 +		for( unsigned int b = 0; b < face.mNumIndices; b++)	{
   1.356 +			face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
   1.357 +		}
   1.358 +	}
   1.359 +
   1.360 +	// adjust bone vertex weights.
   1.361 +	for( int a = 0; a < (int)pMesh->mNumBones; a++)
   1.362 +	{
   1.363 +		aiBone* bone = pMesh->mBones[a];
   1.364 +		std::vector<aiVertexWeight> newWeights;
   1.365 +		newWeights.reserve( bone->mNumWeights);
   1.366 +
   1.367 +		for( unsigned int b = 0; b < bone->mNumWeights; b++)
   1.368 +		{
   1.369 +			const aiVertexWeight& ow = bone->mWeights[b];
   1.370 +			// if the vertex is a unique one, translate it
   1.371 +			if( !(replaceIndex[ow.mVertexId] & 0x80000000))
   1.372 +			{
   1.373 +				aiVertexWeight nw;
   1.374 +				nw.mVertexId = replaceIndex[ow.mVertexId];
   1.375 +				nw.mWeight = ow.mWeight;
   1.376 +				newWeights.push_back( nw);
   1.377 +			}
   1.378 +		}
   1.379 +
   1.380 +		if (newWeights.size() > 0) {
   1.381 +			// kill the old and replace them with the translated weights
   1.382 +			delete [] bone->mWeights;
   1.383 +			bone->mNumWeights = (unsigned int)newWeights.size();
   1.384 +
   1.385 +			bone->mWeights = new aiVertexWeight[bone->mNumWeights];
   1.386 +			memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
   1.387 +		}
   1.388 +		else {
   1.389 +		
   1.390 +			/*  NOTE:
   1.391 +			 *
   1.392 +			 *  In the algorithm above we're assuming that there are no vertices
   1.393 +			 *  with a different bone weight setup at the same position. That wouldn't
   1.394 +			 *  make sense, but it is not absolutely impossible. SkeletonMeshBuilder
   1.395 +			 *  for example generates such input data if two skeleton points
   1.396 +			 *  share the same position. Again this doesn't make sense but is
   1.397 +			 *  reality for some model formats (MD5 for example uses these special
   1.398 +			 *  nodes as attachment tags for its weapons). 
   1.399 +			 *
   1.400 +			 *  Then it is possible that a bone has no weights anymore .... as a quick
   1.401 +			 *  workaround, we're just removing these bones. If they're animated,
   1.402 +			 *  model geometry might be modified but at least there's no risk of a crash.
   1.403 +			 */
   1.404 +			delete bone;
   1.405 +			--pMesh->mNumBones;
   1.406 +			for (unsigned int n = a; n < pMesh->mNumBones; ++n)  {
   1.407 +				pMesh->mBones[n] = pMesh->mBones[n+1];
   1.408 +			}
   1.409 +
   1.410 +			--a; 
   1.411 +			DefaultLogger::get()->warn("Removing bone -> no weights remaining");
   1.412 +		}
   1.413 +	}
   1.414 +	return pMesh->mNumVertices;
   1.415 +}
   1.416 +
   1.417 +#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS