Parcourir la source

- added a new post processing step to split up meshes into submeshes with a limited number of bones.
- fixed some wording details

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@864 67173fc5-114c-0410-ac8e-9d2fd5bffc1f

ulfjorensen il y a 14 ans
Parent
commit
a9e96e2f9b

+ 6 - 0
code/Importer.cpp

@@ -257,6 +257,9 @@ using namespace Assimp::Formatter;
 #ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
 #	include "OptimizeGraph.h"
 #endif
+#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
+#	include "SplitByBoneCountProcess.h"
+#endif
 
 using namespace Assimp;
 using namespace Assimp::Intern;
@@ -478,6 +481,9 @@ Importer::Importer()
 #if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS)
 	pimpl->mPostProcessingSteps.push_back( new FixInfacingNormalsProcess());
 #endif
+#if (!defined ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS)
+	pimpl->mPostProcessingSteps.push_back( new SplitByBoneCountProcess());
+#endif
 #if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
 	pimpl->mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Triangle());
 #endif

+ 393 - 0
code/SplitByBoneCountProcess.cpp

@@ -0,0 +1,393 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 SplitByBoneCountProcess.cpp 
+/// Implementation of the SplitByBoneCount postprocessing step
+
+#include "AssimpPCH.h"
+
+// internal headers of the post-processing framework
+#include "SplitByBoneCountProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor
+SplitByBoneCountProcess::SplitByBoneCountProcess()
+{
+	// set default, might be overriden by importer config
+	mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+SplitByBoneCountProcess::~SplitByBoneCountProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag.
+bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
+{
+	return !!(pFlags & aiProcess_SplitByBoneCount);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Updates internal properties
+void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
+{
+	// ein andermal.
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitByBoneCountProcess::Execute( aiScene* pScene)
+{
+	DefaultLogger::get()->debug("SplitByBoneCountProcess begin");
+
+	// early out 
+	bool isNecessary = false;
+	for( size_t a = 0; a < pScene->mNumMeshes; ++a)
+		if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
+			isNecessary = true;
+
+	if( !isNecessary )
+	{
+		DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess early-out: no meshes with more than %d bones.") % mMaxBoneCount));
+		return;
+	}
+
+	// we need to do something. Let's go.
+	mSubMeshIndices.clear();
+	mSubMeshIndices.resize( pScene->mNumMeshes);
+
+	// build a new array of meshes for the scene
+	std::vector<aiMesh*> meshes;
+
+	for( size_t a = 0; a < pScene->mNumMeshes; ++a)
+	{
+		aiMesh* srcMesh = pScene->mMeshes[a];
+
+		std::vector<aiMesh*> newMeshes;
+		SplitMesh( pScene->mMeshes[a], newMeshes);
+
+		// mesh was split
+		if( !newMeshes.empty() )
+		{
+			// store new meshes and indices of the new meshes
+			for( size_t b = 0; b < newMeshes.size(); ++b)
+			{
+				mSubMeshIndices[a].push_back( meshes.size());
+				meshes.push_back( newMeshes[b]);
+			}
+
+			// and destroy the source mesh. It should be completely contained inside the new submeshes
+			delete srcMesh;
+		}
+		else
+		{
+			// Mesh is kept unchanged - store it's new place in the mesh array
+			mSubMeshIndices[a].push_back( meshes.size());
+			meshes.push_back( srcMesh);
+		}
+	}
+
+	// rebuild the scene's mesh array
+	pScene->mNumMeshes = meshes.size();
+	delete [] pScene->mMeshes;
+	pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+	std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
+
+	// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
+	UpdateNode( pScene->mRootNode);
+
+	DefaultLogger::get()->debug( boost::str( boost::format( "SplitByBoneCountProcess end: split %d meshes into %d submeshes.") % mSubMeshIndices.size() % meshes.size()));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Splits the given mesh by bone count.
+void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
+{
+	// skip if not necessary
+	if( pMesh->mNumBones <= mMaxBoneCount )
+		return;
+
+	// necessary optimisation: build a list of all affecting bones for each vertex
+	// TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
+	typedef std::pair<size_t, float> BoneWeight;
+	std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
+	for( size_t a = 0; a < pMesh->mNumBones; ++a)
+	{
+		const aiBone* bone = pMesh->mBones[a];
+		for( size_t b = 0; b < bone->mNumWeights; ++b)
+			vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
+	}
+
+	size_t numFacesHandled = 0;
+	std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
+	while( numFacesHandled < pMesh->mNumFaces )
+	{
+		// which bones are used in the current submesh
+		size_t numBones = 0;
+		std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
+		// indices of the faces which are going to go into this submesh
+		std::vector<size_t> subMeshFaces;
+		subMeshFaces.reserve( pMesh->mNumFaces);
+		// accumulated vertex count of all the faces in this submesh
+		size_t numSubMeshVertices = 0;
+
+		// add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
+		for( size_t a = 0; a < pMesh->mNumFaces; ++a)
+		{
+			// skip if the face is already stored in a submesh
+			if( isFaceHandled[a] )
+				continue;
+
+			const aiFace& face = pMesh->mFaces[a];
+			// check every vertex if its bones would still fit into the current submesh
+			bool fitsInCurrentSubmesh = true;
+			for( size_t b = 0; b < face.mNumIndices; ++b )
+			{
+				const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
+				for( size_t c = 0; c < vb.size(); ++c)
+				{
+					// if the bone is already used in this submesh, it's ok
+					if( isBoneUsed[ vb[c].first ] )
+						continue;
+
+					// if it's not used, yet, we would need to add it. That only works
+					// if we're still under the bone count limit
+					if( numBones >= mMaxBoneCount )
+					{
+						fitsInCurrentSubmesh = false;
+						break;
+					} else
+					{
+						numBones++;
+						isBoneUsed[ vb[c].first ] = true;
+					}
+				}
+			}
+
+			if( !fitsInCurrentSubmesh )
+				continue;
+
+			// store the face index and the vertex count
+			subMeshFaces.push_back( a);
+			numSubMeshVertices += face.mNumIndices;
+
+			// remember that this face is handled
+			isFaceHandled[a] = true;
+			numFacesHandled++;
+		}
+
+		// create a new mesh to hold this subset of the source mesh
+		aiMesh* newMesh = new aiMesh;
+		if( pMesh->mName.length > 0 )
+			newMesh->mName.Set( boost::str( boost::format( "%s_sub%d") % pMesh->mName.data % poNewMeshes.size()));
+		newMesh->mMaterialIndex = pMesh->mMaterialIndex;
+		newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
+		poNewMeshes.push_back( newMesh);
+
+		// create all the arrays for this mesh if the old mesh contained them
+		newMesh->mNumVertices = numSubMeshVertices;
+		newMesh->mNumFaces = subMeshFaces.size();
+		newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
+		if( pMesh->HasNormals() )
+			newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
+		if( pMesh->HasTangentsAndBitangents() )
+		{
+			newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
+			newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
+		}
+		for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
+		{
+			if( pMesh->HasTextureCoords( a) )
+				newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
+			newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
+		}
+		for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
+		{
+			if( pMesh->HasVertexColors( a) )
+				newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
+		}
+
+		// and copy over the data, generating faces with linear indices along the way
+		newMesh->mFaces = new aiFace[subMeshFaces.size()];
+		size_t nvi = 0; // next vertex index
+		std::vector<size_t> previousVertexIndices( numSubMeshVertices, SIZE_MAX); // per new vertex: its index in the source mesh
+		for( size_t a = 0; a < subMeshFaces.size(); ++a )
+		{
+			const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
+			aiFace& dstFace = newMesh->mFaces[a];
+			dstFace.mNumIndices = srcFace.mNumIndices;
+			dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
+
+			// accumulate linearly all the vertices of the source face
+			for( size_t b = 0; b < dstFace.mNumIndices; ++b )
+			{
+				size_t srcIndex = srcFace.mIndices[b];
+				dstFace.mIndices[b] = nvi;
+				previousVertexIndices[nvi] = srcIndex;
+
+				newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
+				if( pMesh->HasNormals() )
+					newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
+				if( pMesh->HasTangentsAndBitangents() )
+				{
+					newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
+					newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
+				}
+				for( size_t c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
+				{
+					if( pMesh->HasTextureCoords( c) )
+						newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
+				}
+				for( size_t c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
+				{
+					if( pMesh->HasVertexColors( c) )
+						newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
+				}
+
+				nvi++;
+			}
+		}
+
+		ai_assert( nvi == numSubMeshVertices );
+
+		// Create the bones for the new submesh: first create the bone array
+		newMesh->mNumBones = 0;
+		newMesh->mBones = new aiBone*[numBones];
+
+		std::vector<size_t> mappedBoneIndex( pMesh->mNumBones, SIZE_MAX);
+		for( size_t a = 0; a < pMesh->mNumBones; ++a )
+		{
+			if( !isBoneUsed[a] )
+				continue;
+
+			// create the new bone
+			const aiBone* srcBone = pMesh->mBones[a];
+			aiBone* dstBone = new aiBone;
+			mappedBoneIndex[a] = newMesh->mNumBones;
+			newMesh->mBones[newMesh->mNumBones++] = dstBone;
+			dstBone->mName = srcBone->mName;
+			dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
+			dstBone->mNumWeights = 0;
+		}
+
+		ai_assert( newMesh->mNumBones == numBones );
+
+		// iterate over all new vertices and count which bones affected its old vertex in the source mesh
+		for( size_t a = 0; a < numSubMeshVertices; ++a )
+		{
+			size_t oldIndex = previousVertexIndices[a];
+			const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
+
+			for( size_t b = 0; b < bonesOnThisVertex.size(); ++b )
+			{
+				size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
+				if( newBoneIndex != SIZE_MAX )
+					newMesh->mBones[newBoneIndex]->mNumWeights++;
+			}
+		}
+
+		// allocate all bone weight arrays accordingly
+		for( size_t a = 0; a < newMesh->mNumBones; ++a )
+		{
+			aiBone* bone = newMesh->mBones[a];
+			ai_assert( bone->mNumWeights > 0 );
+			bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+			bone->mNumWeights = 0; // for counting up in the next step
+		}
+
+		// now copy all the bone vertex weights for all the vertices which made it into the new submesh
+		for( size_t a = 0; a < numSubMeshVertices; ++a)
+		{
+			// find the source vertex for it in the source mesh
+			size_t previousIndex = previousVertexIndices[a];
+			// these bones were affecting it
+			const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
+			// all of the bones affecting it should be present in the new submesh, or else
+			// the face it comprises shouldn't be present
+			for( size_t b = 0; b < bonesOnThisVertex.size(); ++b)
+			{
+				size_t newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
+				ai_assert( newBoneIndex != SIZE_MAX );
+				aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
+				newMesh->mBones[newBoneIndex]->mNumWeights++;
+
+				dstWeight->mVertexId = a;
+				dstWeight->mWeight = bonesOnThisVertex[b].second;
+			}
+		}
+
+		// I have the strange feeling that this will break apart at some point in time...
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively updates the node's mesh list to account for the changed mesh list
+void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
+{
+	// rebuild the node's mesh index list
+	if( pNode->mNumMeshes > 0 )
+	{
+		std::vector<size_t> newMeshList;
+		for( size_t a = 0; a < pNode->mNumMeshes; ++a)
+		{
+			size_t srcIndex = pNode->mMeshes[a];
+			const std::vector<size_t>& replaceMeshes = mSubMeshIndices[srcIndex];
+			newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
+		}
+
+		delete pNode->mMeshes;
+		pNode->mNumMeshes = newMeshList.size();
+		pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+		std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
+	}
+
+	// do that also recursively for all children
+	for( size_t a = 0; a < pNode->mNumChildren; ++a )
+	{
+		UpdateNode( pNode->mChildren[a]);
+	}
+}

+ 126 - 0
code/SplitByBoneCountProcess.h

@@ -0,0 +1,126 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 SplitByBoneCountProcess.h 
+/// Defines a post processing step to split meshes with many bones into submeshes
+#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC
+#define AI_SPLITBYBONECOUNTPROCESS_H_INC
+
+#include <vector>
+#include "BaseProcess.h"
+
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+namespace Assimp
+{
+
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: ConfigProperty.java, 
+//  ConfigProperty.DEFAULT_VERTEX_SPLIT_LIMIT
+//  ConfigProperty.DEFAULT_TRIANGLE_SPLIT_LIMIT
+// **********************************************************
+
+// default limit for bone count 
+#if (!defined AI_SBBC_DEFAULT_MAX_BONES)
+#	define AI_SBBC_DEFAULT_MAX_BONES		60
+#endif
+
+/** Postprocessing filter to split meshes with many bones into submeshes
+ * so that each submesh has a certain max bone count.
+ *
+ * Applied BEFORE the JoinVertices-Step occurs.
+ * Returns NON-UNIQUE vertices, splits by bone count.
+*/
+class ASSIMP_API SplitByBoneCountProcess : public BaseProcess
+{
+	friend class Importer;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	SplitByBoneCountProcess();
+
+	/** Destructor, private as well */
+	~SplitByBoneCountProcess();
+
+public:
+	/** Returns whether the processing step is present in the given flag.
+	* @param pFlags The processing flags the importer was called with. A
+ 	*   bitwise combination of #aiPostProcessSteps.
+	* @return true if the process is present in this flag fields, 
+ 	*   false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	/** Called prior to ExecuteOnScene().
+	* The function is a request to the process to update its configuration
+	* basing on the Importer's configuration property list.
+	*/
+	virtual void SetupProperties(const Importer* pImp);
+
+protected:
+	/** Executes the post processing step on the given imported data.
+	* At the moment a process is not supposed to fail.
+	* @param pScene The imported data to work at.
+	*/
+	void Execute( aiScene* pScene);
+
+	/// Splits the given mesh by bone count.
+	/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
+	/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
+	void SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const;
+
+	/// Recursively updates the node's mesh list to account for the changed mesh list
+	void UpdateNode( aiNode* pNode) const;
+
+public:
+	/// Max bone count. Splitting occurs if a mesh has more than that number of bones.
+	size_t mMaxBoneCount;
+
+	/// Per mesh index: Array of indices of the new submeshes.
+	std::vector< std::vector<size_t> > mSubMeshIndices;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

+ 3 - 2
include/aiMesh.h

@@ -516,8 +516,7 @@ struct aiMesh
 	* point or line primitives are undefined and set to qNaN.  See
 	* the #mNormals member for a detailled discussion of qNaNs.
 	* @note If the mesh contains tangents, it automatically also 
-	* contains bitangents (the bitangent is just the cross product of
-	* tangent and normal vectors). 
+	* contains bitangents.
 	*/
 	C_STRUCT aiVector3D* mTangents;
 
@@ -626,6 +625,8 @@ struct aiMesh
 			mColors[a] = NULL;
 		mNumBones = 0; mBones = NULL;
 		mMaterialIndex = 0;
+		mNumAnimMeshes = 0;
+		mAnimMeshes = NULL;
 	}
 
 	//! Deletes all storage allocated for the mesh

+ 7 - 1
include/aiPostProcess.h

@@ -494,7 +494,13 @@ enum aiPostProcessSteps
 	 *  x1
 	 * @endcode
 	*/
-	aiProcess_FlipWindingOrder  = 0x1000000
+	aiProcess_FlipWindingOrder  = 0x1000000,
+
+	// -------------------------------------------------------------------------
+	/** <hr>This step splits meshes with many bones into submeshes so that each
+	 * submesh has fewer or as many bones as a given limit. 
+    */
+	aiProcess_SplitByBoneCount  = 0x2000000
 
 	// aiProcess_GenEntityMeshes = 0x100000,
 	// aiProcess_OptimizeAnimations = 0x200000

+ 1 - 1
include/aiTypes.h

@@ -261,7 +261,7 @@ struct aiString
 	}
 
 	/** Constructor from std::string */
-	aiString(const std::string& pString) : 
+	explicit aiString(const std::string& pString) : 
 		length(pString.length()) 
 	{
 		length = length>=MAXLEN?MAXLEN-1:length;

+ 1 - 0
tools/assimp_view/assimp_view.cpp

@@ -89,6 +89,7 @@ unsigned int ppsteps = aiProcess_CalcTangentSpace | // calculate tangents and bi
 		aiProcess_FindInstances            | // search for instanced meshes and remove them by references to one master
 		aiProcess_LimitBoneWeights         | // limit bone weights to 4 per vertex
 		aiProcess_OptimizeMeshes		   | // join small meshes, if possible;
+		aiProcess_SplitByBoneCount         | // split meshes with too many bones. Necessary for our (limited) hardware skinning shader
 		0;
 
 unsigned int ppstepsdefault = ppsteps;

+ 8 - 0
workspaces/vc9/assimp.vcproj

@@ -2090,6 +2090,14 @@
 					RelativePath="..\..\code\SortByPTypeProcess.h"
 					>
 				</File>
+				<File
+					RelativePath="..\..\code\SplitByBoneCountProcess.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\code\SplitByBoneCountProcess.h"
+					>
+				</File>
 				<File
 					RelativePath="..\..\code\SplitLargeMeshes.cpp"
 					>