vrshoot

diff libs/assimp/SplitByBoneCountProcess.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/SplitByBoneCountProcess.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,403 @@
     1.4 +/*
     1.5 +Open Asset Import Library (assimp)
     1.6 +----------------------------------------------------------------------
     1.7 +
     1.8 +Copyright (c) 2006-2012, assimp team
     1.9 +All rights reserved.
    1.10 +
    1.11 +Redistribution and use of this software in source and binary forms, 
    1.12 +with or without modification, are permitted provided that the 
    1.13 +following conditions are met:
    1.14 +
    1.15 +* Redistributions of source code must retain the above
    1.16 +  copyright notice, this list of conditions and the
    1.17 +  following disclaimer.
    1.18 +
    1.19 +* Redistributions in binary form must reproduce the above
    1.20 +  copyright notice, this list of conditions and the
    1.21 +  following disclaimer in the documentation and/or other
    1.22 +  materials provided with the distribution.
    1.23 +
    1.24 +* Neither the name of the assimp team, nor the names of its
    1.25 +  contributors may be used to endorse or promote products
    1.26 +  derived from this software without specific prior
    1.27 +  written permission of the assimp team.
    1.28 +
    1.29 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.30 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.31 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.32 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.33 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.34 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.35 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.36 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.37 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.38 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.39 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.40 +
    1.41 +----------------------------------------------------------------------
    1.42 +*/
    1.43 +
    1.44 +
    1.45 +/// @file SplitByBoneCountProcess.cpp 
    1.46 +/// Implementation of the SplitByBoneCount postprocessing step
    1.47 +
    1.48 +#include "AssimpPCH.h"
    1.49 +
    1.50 +// internal headers of the post-processing framework
    1.51 +#include "SplitByBoneCountProcess.h"
    1.52 +
    1.53 +#include <limits>
    1.54 +
    1.55 +using namespace Assimp;
    1.56 +
    1.57 +// ------------------------------------------------------------------------------------------------
    1.58 +// Constructor
    1.59 +SplitByBoneCountProcess::SplitByBoneCountProcess()
    1.60 +{
    1.61 +	// set default, might be overriden by importer config
    1.62 +	mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
    1.63 +}
    1.64 +
    1.65 +// ------------------------------------------------------------------------------------------------
    1.66 +// Destructor
    1.67 +SplitByBoneCountProcess::~SplitByBoneCountProcess()
    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.
    1.74 +bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
    1.75 +{
    1.76 +	return !!(pFlags & aiProcess_SplitByBoneCount);
    1.77 +}
    1.78 +
    1.79 +// ------------------------------------------------------------------------------------------------
    1.80 +// Updates internal properties
    1.81 +void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
    1.82 +{
    1.83 +	mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
    1.84 +}
    1.85 +
    1.86 +// ------------------------------------------------------------------------------------------------
    1.87 +// Executes the post processing step on the given imported data.
    1.88 +void SplitByBoneCountProcess::Execute( aiScene* pScene)
    1.89 +{
    1.90 +	DefaultLogger::get()->debug("SplitByBoneCountProcess begin");
    1.91 +
    1.92 +	// early out 
    1.93 +	bool isNecessary = false;
    1.94 +	for( size_t a = 0; a < pScene->mNumMeshes; ++a)
    1.95 +		if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
    1.96 +			isNecessary = true;
    1.97 +
    1.98 +	if( !isNecessary )
    1.99 +	{
   1.100 +		DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess early-out: no meshes with more than %d bones.") % mMaxBoneCount));
   1.101 +		return;
   1.102 +	}
   1.103 +
   1.104 +	// we need to do something. Let's go.
   1.105 +	mSubMeshIndices.clear();
   1.106 +	mSubMeshIndices.resize( pScene->mNumMeshes);
   1.107 +
   1.108 +	// build a new array of meshes for the scene
   1.109 +	std::vector<aiMesh*> meshes;
   1.110 +
   1.111 +	for( size_t a = 0; a < pScene->mNumMeshes; ++a)
   1.112 +	{
   1.113 +		aiMesh* srcMesh = pScene->mMeshes[a];
   1.114 +
   1.115 +		std::vector<aiMesh*> newMeshes;
   1.116 +		SplitMesh( pScene->mMeshes[a], newMeshes);
   1.117 +
   1.118 +		// mesh was split
   1.119 +		if( !newMeshes.empty() )
   1.120 +		{
   1.121 +			// store new meshes and indices of the new meshes
   1.122 +			for( size_t b = 0; b < newMeshes.size(); ++b)
   1.123 +			{
   1.124 +				mSubMeshIndices[a].push_back( meshes.size());
   1.125 +				meshes.push_back( newMeshes[b]);
   1.126 +			}
   1.127 +
   1.128 +			// and destroy the source mesh. It should be completely contained inside the new submeshes
   1.129 +			delete srcMesh;
   1.130 +		}
   1.131 +		else
   1.132 +		{
   1.133 +			// Mesh is kept unchanged - store it's new place in the mesh array
   1.134 +			mSubMeshIndices[a].push_back( meshes.size());
   1.135 +			meshes.push_back( srcMesh);
   1.136 +		}
   1.137 +	}
   1.138 +
   1.139 +	// rebuild the scene's mesh array
   1.140 +	pScene->mNumMeshes = meshes.size();
   1.141 +	delete [] pScene->mMeshes;
   1.142 +	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
   1.143 +	std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
   1.144 +
   1.145 +	// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
   1.146 +	UpdateNode( pScene->mRootNode);
   1.147 +
   1.148 +	DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess end: split %d meshes into %d submeshes.") % mSubMeshIndices.size() % meshes.size()));
   1.149 +}
   1.150 +
   1.151 +// ------------------------------------------------------------------------------------------------
   1.152 +// Splits the given mesh by bone count.
   1.153 +void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
   1.154 +{
   1.155 +	// skip if not necessary
   1.156 +	if( pMesh->mNumBones <= mMaxBoneCount )
   1.157 +		return;
   1.158 +
   1.159 +	// necessary optimisation: build a list of all affecting bones for each vertex
   1.160 +	// TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
   1.161 +	typedef std::pair<size_t, float> BoneWeight;
   1.162 +	std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
   1.163 +	for( size_t a = 0; a < pMesh->mNumBones; ++a)
   1.164 +	{
   1.165 +		const aiBone* bone = pMesh->mBones[a];
   1.166 +		for( size_t b = 0; b < bone->mNumWeights; ++b)
   1.167 +			vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
   1.168 +	}
   1.169 +
   1.170 +	size_t numFacesHandled = 0;
   1.171 +	std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
   1.172 +	while( numFacesHandled < pMesh->mNumFaces )
   1.173 +	{
   1.174 +		// which bones are used in the current submesh
   1.175 +		size_t numBones = 0;
   1.176 +		std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
   1.177 +		// indices of the faces which are going to go into this submesh
   1.178 +		std::vector<size_t> subMeshFaces;
   1.179 +		subMeshFaces.reserve( pMesh->mNumFaces);
   1.180 +		// accumulated vertex count of all the faces in this submesh
   1.181 +		size_t numSubMeshVertices = 0;
   1.182 +		// a small local array of new bones for the current face. State of all used bones for that face
   1.183 +		// can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
   1.184 +		std::vector<size_t> newBonesAtCurrentFace;
   1.185 +
   1.186 +		// add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
   1.187 +		for( size_t a = 0; a < pMesh->mNumFaces; ++a)
   1.188 +		{
   1.189 +			// skip if the face is already stored in a submesh
   1.190 +			if( isFaceHandled[a] )
   1.191 +				continue;
   1.192 +
   1.193 +			const aiFace& face = pMesh->mFaces[a];
   1.194 +			// check every vertex if its bones would still fit into the current submesh
   1.195 +			for( size_t b = 0; b < face.mNumIndices; ++b )
   1.196 +			{
   1.197 +				const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
   1.198 +				for( size_t c = 0; c < vb.size(); ++c)
   1.199 +				{
   1.200 +					size_t boneIndex = vb[c].first;
   1.201 +					// if the bone is already used in this submesh, it's ok
   1.202 +					if( isBoneUsed[boneIndex] )
   1.203 +						continue;
   1.204 +
   1.205 +					// if it's not used, yet, we would need to add it. Store its bone index
   1.206 +					if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() )
   1.207 +						newBonesAtCurrentFace.push_back( boneIndex);
   1.208 +				}
   1.209 +			}
   1.210 +
   1.211 +			// leave out the face if the new bones required for this face don't fit the bone count limit anymore
   1.212 +			if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
   1.213 +				continue;
   1.214 +
   1.215 +			// mark all new bones as necessary
   1.216 +			while( !newBonesAtCurrentFace.empty() )
   1.217 +			{
   1.218 +				size_t newIndex = newBonesAtCurrentFace.back();
   1.219 +				newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear()
   1.220 +				if( isBoneUsed[newIndex] ) 
   1.221 +					continue;
   1.222 +
   1.223 +				isBoneUsed[newIndex] = true;
   1.224 +				numBones++;
   1.225 +			}
   1.226 +
   1.227 +			// store the face index and the vertex count
   1.228 +			subMeshFaces.push_back( a);
   1.229 +			numSubMeshVertices += face.mNumIndices;
   1.230 +
   1.231 +			// remember that this face is handled
   1.232 +			isFaceHandled[a] = true;
   1.233 +			numFacesHandled++;
   1.234 +		}
   1.235 +
   1.236 +		// create a new mesh to hold this subset of the source mesh
   1.237 +		aiMesh* newMesh = new aiMesh;
   1.238 +		if( pMesh->mName.length > 0 )
   1.239 +			newMesh->mName.Set( boost::str( boost::format( "%s_sub%d") % pMesh->mName.data % poNewMeshes.size()));
   1.240 +		newMesh->mMaterialIndex = pMesh->mMaterialIndex;
   1.241 +		newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
   1.242 +		poNewMeshes.push_back( newMesh);
   1.243 +
   1.244 +		// create all the arrays for this mesh if the old mesh contained them
   1.245 +		newMesh->mNumVertices = numSubMeshVertices;
   1.246 +		newMesh->mNumFaces = subMeshFaces.size();
   1.247 +		newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
   1.248 +		if( pMesh->HasNormals() )
   1.249 +			newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
   1.250 +		if( pMesh->HasTangentsAndBitangents() )
   1.251 +		{
   1.252 +			newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
   1.253 +			newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
   1.254 +		}
   1.255 +		for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
   1.256 +		{
   1.257 +			if( pMesh->HasTextureCoords( a) )
   1.258 +				newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
   1.259 +			newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
   1.260 +		}
   1.261 +		for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
   1.262 +		{
   1.263 +			if( pMesh->HasVertexColors( a) )
   1.264 +				newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
   1.265 +		}
   1.266 +
   1.267 +		// and copy over the data, generating faces with linear indices along the way
   1.268 +		newMesh->mFaces = new aiFace[subMeshFaces.size()];
   1.269 +		size_t nvi = 0; // next vertex index
   1.270 +		std::vector<size_t> previousVertexIndices( numSubMeshVertices, std::numeric_limits<size_t>::max()); // per new vertex: its index in the source mesh
   1.271 +		for( size_t a = 0; a < subMeshFaces.size(); ++a )
   1.272 +		{
   1.273 +			const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
   1.274 +			aiFace& dstFace = newMesh->mFaces[a];
   1.275 +			dstFace.mNumIndices = srcFace.mNumIndices;
   1.276 +			dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
   1.277 +
   1.278 +			// accumulate linearly all the vertices of the source face
   1.279 +			for( size_t b = 0; b < dstFace.mNumIndices; ++b )
   1.280 +			{
   1.281 +				size_t srcIndex = srcFace.mIndices[b];
   1.282 +				dstFace.mIndices[b] = nvi;
   1.283 +				previousVertexIndices[nvi] = srcIndex;
   1.284 +
   1.285 +				newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
   1.286 +				if( pMesh->HasNormals() )
   1.287 +					newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
   1.288 +				if( pMesh->HasTangentsAndBitangents() )
   1.289 +				{
   1.290 +					newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
   1.291 +					newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
   1.292 +				}
   1.293 +				for( size_t c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
   1.294 +				{
   1.295 +					if( pMesh->HasTextureCoords( c) )
   1.296 +						newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
   1.297 +				}
   1.298 +				for( size_t c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
   1.299 +				{
   1.300 +					if( pMesh->HasVertexColors( c) )
   1.301 +						newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
   1.302 +				}
   1.303 +
   1.304 +				nvi++;
   1.305 +			}
   1.306 +		}
   1.307 +
   1.308 +		ai_assert( nvi == numSubMeshVertices );
   1.309 +
   1.310 +		// Create the bones for the new submesh: first create the bone array
   1.311 +		newMesh->mNumBones = 0;
   1.312 +		newMesh->mBones = new aiBone*[numBones];
   1.313 +
   1.314 +		std::vector<size_t> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<size_t>::max());
   1.315 +		for( size_t a = 0; a < pMesh->mNumBones; ++a )
   1.316 +		{
   1.317 +			if( !isBoneUsed[a] )
   1.318 +				continue;
   1.319 +
   1.320 +			// create the new bone
   1.321 +			const aiBone* srcBone = pMesh->mBones[a];
   1.322 +			aiBone* dstBone = new aiBone;
   1.323 +			mappedBoneIndex[a] = newMesh->mNumBones;
   1.324 +			newMesh->mBones[newMesh->mNumBones++] = dstBone;
   1.325 +			dstBone->mName = srcBone->mName;
   1.326 +			dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
   1.327 +			dstBone->mNumWeights = 0;
   1.328 +		}
   1.329 +
   1.330 +		ai_assert( newMesh->mNumBones == numBones );
   1.331 +
   1.332 +		// iterate over all new vertices and count which bones affected its old vertex in the source mesh
   1.333 +		for( size_t a = 0; a < numSubMeshVertices; ++a )
   1.334 +		{
   1.335 +			size_t oldIndex = previousVertexIndices[a];
   1.336 +			const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
   1.337 +
   1.338 +			for( size_t b = 0; b < bonesOnThisVertex.size(); ++b )
   1.339 +			{
   1.340 +				size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
   1.341 +				if( newBoneIndex != std::numeric_limits<size_t>::max() )
   1.342 +					newMesh->mBones[newBoneIndex]->mNumWeights++;
   1.343 +			}
   1.344 +		}
   1.345 +
   1.346 +		// allocate all bone weight arrays accordingly
   1.347 +		for( size_t a = 0; a < newMesh->mNumBones; ++a )
   1.348 +		{
   1.349 +			aiBone* bone = newMesh->mBones[a];
   1.350 +			ai_assert( bone->mNumWeights > 0 );
   1.351 +			bone->mWeights = new aiVertexWeight[bone->mNumWeights];
   1.352 +			bone->mNumWeights = 0; // for counting up in the next step
   1.353 +		}
   1.354 +
   1.355 +		// now copy all the bone vertex weights for all the vertices which made it into the new submesh
   1.356 +		for( size_t a = 0; a < numSubMeshVertices; ++a)
   1.357 +		{
   1.358 +			// find the source vertex for it in the source mesh
   1.359 +			size_t previousIndex = previousVertexIndices[a];
   1.360 +			// these bones were affecting it
   1.361 +			const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
   1.362 +			// all of the bones affecting it should be present in the new submesh, or else
   1.363 +			// the face it comprises shouldn't be present
   1.364 +			for( size_t b = 0; b < bonesOnThisVertex.size(); ++b)
   1.365 +			{
   1.366 +				size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
   1.367 +				ai_assert( newBoneIndex != std::numeric_limits<size_t>::max() );
   1.368 +				aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
   1.369 +				newMesh->mBones[newBoneIndex]->mNumWeights++;
   1.370 +
   1.371 +				dstWeight->mVertexId = a;
   1.372 +				dstWeight->mWeight = bonesOnThisVertex[b].second;
   1.373 +			}
   1.374 +		}
   1.375 +
   1.376 +		// I have the strange feeling that this will break apart at some point in time...
   1.377 +	}
   1.378 +}
   1.379 +
   1.380 +// ------------------------------------------------------------------------------------------------
   1.381 +// Recursively updates the node's mesh list to account for the changed mesh list
   1.382 +void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
   1.383 +{
   1.384 +	// rebuild the node's mesh index list
   1.385 +	if( pNode->mNumMeshes > 0 )
   1.386 +	{
   1.387 +		std::vector<size_t> newMeshList;
   1.388 +		for( size_t a = 0; a < pNode->mNumMeshes; ++a)
   1.389 +		{
   1.390 +			size_t srcIndex = pNode->mMeshes[a];
   1.391 +			const std::vector<size_t>& replaceMeshes = mSubMeshIndices[srcIndex];
   1.392 +			newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
   1.393 +		}
   1.394 +
   1.395 +		delete pNode->mMeshes;
   1.396 +		pNode->mNumMeshes = newMeshList.size();
   1.397 +		pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
   1.398 +		std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
   1.399 +	}
   1.400 +
   1.401 +	// do that also recursively for all children
   1.402 +	for( size_t a = 0; a < pNode->mNumChildren; ++a )
   1.403 +	{
   1.404 +		UpdateNode( pNode->mChildren[a]);
   1.405 +	}
   1.406 +}