소스 검색

+ integrate Debone-Process into Assimp. This step was contributed by mick-p, see [3262561] (https://sourceforge.net/tracker/?func=detail&aid=3262561&group_id=226462&atid=1067634)
- refactor ProcessHelper.h. Some functions now reside in ProcessHelper.cpp, which is new.

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

aramis_acg 14 년 전
부모
커밋
a6e0a5075f
9개의 변경된 파일1111개의 추가작업 그리고 220개의 파일을 삭제
  1. 4 0
      CREDITS
  2. 7 1
      code/CMakeLists.txt
  3. 466 0
      code/DeboneProcess.cpp
  4. 137 0
      code/DeboneProcess.h
  5. 6 6
      code/Importer.cpp
  6. 410 0
      code/ProcessHelper.cpp
  7. 42 212
      code/ProcessHelper.h
  8. 23 0
      include/aiConfig.h
  9. 16 1
      include/aiPostProcess.h

+ 4 - 0
CREDITS

@@ -115,3 +115,7 @@ Contributed AssimpDelphi (/port/AssimpDelphi).
 
 - rdb 
 Contributes a bundle of fixes and improvments for the bsp-importer.
+
+- Mick P
+For contributing the De-bone postprocessing step and filing various bug reports.
+

+ 7 - 1
code/CMakeLists.txt

@@ -87,7 +87,6 @@ SOURCE_GROUP( Common FILES
 	BaseProcess.cpp
 	BaseProcess.h
 	ByteSwap.h
-	ProcessHelper.h
 	DefaultProgressHandler.h
 	DefaultIOStream.cpp
 	DefaultIOStream.h
@@ -362,6 +361,10 @@ SOURCE_GROUP( PostProcessing FILES
 	OptimizeGraph.h
 	OptimizeMeshes.cpp
 	OptimizeMeshes.h
+	DeboneProcess.cpp
+	DeboneProcess.h
+	ProcessHelper.h
+	ProcessHelper.cp
 )
 
 SOURCE_GROUP( Q3D FILES
@@ -608,6 +611,7 @@ ADD_LIBRARY( assimp SHARED
 	PretransformVertices.cpp
 	PretransformVertices.h
 	ProcessHelper.h
+	ProcessHelper.cpp
 	Q3DLoader.cpp
 	Q3DLoader.h
 	Q3BSPFileData.h
@@ -734,6 +738,8 @@ ADD_LIBRARY( assimp SHARED
 	Profiler.h
 	NDOLoader.cpp
 	NDOLoader.h
+	DeboneProcess.cpp
+	DeboneProcess.h
 	ColladaExporter.h
 	ColladaExporter.cpp
 

+ 466 - 0
code/DeboneProcess.cpp

@@ -0,0 +1,466 @@
+									 /*
+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 DeboneProcess.cpp
+/** Implementation of the DeboneProcess post processing step */
+
+#include "AssimpPCH.h"
+
+// internal headers of the post-processing framework
+#include "ProcessHelper.h"
+#include "DeboneProcess.h"
+
+#include <limits>
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DeboneProcess::DeboneProcess()
+{
+	mNumBones = 0;
+	mNumBonesCanDoWithout = 0;
+
+	mThreshold = AI_DEBONE_THRESHOLD;
+	mAllOrNone = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+DeboneProcess::~DeboneProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool DeboneProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_Debone) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DeboneProcess::SetupProperties(const Importer* pImp)
+{	
+	// get the current value of the property
+	mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false;
+	mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DeboneProcess::Execute( aiScene* pScene)
+{
+	DefaultLogger::get()->debug("DeboneProcess begin");
+
+	if(!pScene->mNumMeshes) {
+		return;
+	}
+
+	std::vector<bool> splitList(pScene->mNumMeshes); 
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+		splitList[a] = ConsiderMesh( pScene->mMeshes[a] );
+	}
+
+	int numSplits = 0; 
+
+	if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones))	{
+		for(unsigned int a = 0; a < pScene->mNumMeshes; a++)	{
+			if(splitList[a]) {
+				numSplits++;
+			}
+		}
+	}
+
+	if(numSplits)	{
+		// 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(unsigned int a=0;a<pScene->mNumMeshes;a++)
+		{
+			aiMesh* srcMesh = pScene->mMeshes[a];
+
+			std::vector<std::pair<aiMesh*,const aiBone*> > newMeshes;
+
+			if(splitList[a]) { 
+				SplitMesh(srcMesh,newMeshes);
+			}
+
+			// mesh was split
+			if(!newMeshes.empty())	{				
+				unsigned int out = 0, in = srcMesh->mNumBones;				
+
+				// store new meshes and indices of the new meshes
+				for(unsigned int b=0;b<newMeshes.size();b++)	{						
+					const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0;
+
+					aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0;
+					std::pair<unsigned int,aiNode*> push_pair(meshes.size(),theNode);
+
+					mSubMeshIndices[a].push_back(push_pair);
+					meshes.push_back(newMeshes[b].first);
+
+					out+=newMeshes[b].first->mNumBones;
+				}
+						   
+				if(!DefaultLogger::isNullLogger()) {
+					char buffer[1024];
+					::sprintf(buffer,"Removed %i bones. Input bones: %i. Output bones: %i",in-out,in,out);
+					DefaultLogger::get()->info(buffer);
+				}
+
+				// 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(std::pair<unsigned int,aiNode*>(meshes.size(),0));
+				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("DeboneProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts bones total/removable in a given mesh.
+bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
+{
+	if(!pMesh->HasBones()) {
+		return false;
+	}
+
+	bool split = false;
+
+	//interstitial faces not permitted
+	bool isInterstitialRequired = false;
+
+	std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
+	std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
+
+	const unsigned int cUnowned = UINT_MAX;
+	const unsigned int cCoowned = UINT_MAX-1;
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++)	{
+		for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++)	{
+			float w = pMesh->mBones[i]->mWeights[j].mWeight;
+
+			if(w==0.0f) {
+				continue;
+			}
+
+			unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
+			if(w>=mThreshold)	{
+
+				if(vertexBones[vid]!=cUnowned)	{
+					if(vertexBones[vid]==i) //double entry
+					{
+						DefaultLogger::get()->warn("Encountered double entry in bone weights");					
+					}
+					else //TODO: track attraction in order to break tie
+					{
+						vertexBones[vid] = cCoowned;
+					}
+				}
+				else vertexBones[vid] = i;			
+			}
+
+			if(!isBoneNecessary[i]) {
+				isBoneNecessary[i] = w<mThreshold;
+			}
+		}
+
+		if(!isBoneNecessary[i])  {
+			isInterstitialRequired = true;
+		}
+	}
+
+	if(isInterstitialRequired) {
+		for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+			unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
+
+			for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
+				unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
+
+				if(v!=w)	{
+					if(v<pMesh->mNumBones) isBoneNecessary[v] = true; 
+					if(w<pMesh->mNumBones) isBoneNecessary[w] = true; 
+				}
+			}
+		}
+	}
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++)	{
+		if(!isBoneNecessary[i])	{
+			mNumBonesCanDoWithout++; 
+			split = true;
+		}
+		
+		mNumBones++;
+	}
+	return split;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Splits the given mesh by bone count.
+void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector<std::pair<aiMesh*,const aiBone*>>& poNewMeshes) const
+{
+	// same deal here as ConsiderMesh basically
+
+	std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
+	std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
+
+	const unsigned int cUnowned = UINT_MAX;
+	const unsigned int cCoowned = UINT_MAX-1;
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++)	{
+		for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++)	{
+			float w = pMesh->mBones[i]->mWeights[j].mWeight;
+
+			if(w==0.0f) {
+				continue;
+			}
+
+			unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
+
+			if(w>=mThreshold) {
+				if(vertexBones[vid]!=cUnowned)  {
+					if(vertexBones[vid]==i) //double entry
+					{
+						//DefaultLogger::get()->warn("Encountered double entry in bone weights");					
+					}
+					else //TODO: track attraction in order to break tie
+					{
+						vertexBones[vid] = cCoowned;
+					}
+				}
+				else vertexBones[vid] = i;			
+			}
+
+			if(!isBoneNecessary[i]) {
+				isBoneNecessary[i] = w<mThreshold;
+			}
+		}
+	}
+
+	unsigned int nFacesUnowned = 0;
+
+	std::vector<unsigned int> faceBones(pMesh->mNumFaces,UINT_MAX);
+	std::vector<unsigned int> facesPerBone(pMesh->mNumBones,0);
+
+	for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+		unsigned int nInterstitial = 1;
+
+		unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
+
+		for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
+			unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
+
+			if(v!=w)	{
+				if(v<pMesh->mNumBones) isBoneNecessary[v] = true; 
+				if(w<pMesh->mNumBones) isBoneNecessary[w] = true; 
+			}
+			else nInterstitial++;
+		}
+
+		if(v<pMesh->mNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices)	{				
+			faceBones[i] = v; //primitive belongs to bone #v
+			facesPerBone[v]++;
+		}
+		else nFacesUnowned++; 
+	}
+
+	// invalidate any "cojoined" faces
+	for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+		if(faceBones[i]<pMesh->mNumBones&&isBoneNecessary[faceBones[i]]) 
+		{
+			ai_assert(facesPerBone[faceBones[i]]>0);
+			facesPerBone[faceBones[i]]--; 
+			
+			nFacesUnowned++; 
+			faceBones[i] = cUnowned; 
+		}
+	}
+
+	if(nFacesUnowned) {	 	
+		std::vector<unsigned int> subFaces;
+
+		for(unsigned int i=0;i<pMesh->mNumFaces;i++)	{
+			if(faceBones[i]==cUnowned) {
+				subFaces.push_back(i);
+			}
+		}
+
+		aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0);
+		std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,0);
+
+		poNewMeshes.push_back(push_pair);
+	}
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++) {			
+
+		if(!isBoneNecessary[i]&&facesPerBone[i]>0)	{				
+			std::vector<unsigned int> subFaces;
+
+			for(unsigned int j=0;j<pMesh->mNumFaces;j++)	{
+				if(faceBones[j]==i) {
+					subFaces.push_back(j);
+				}
+			}
+
+			unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES;
+			aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f);
+
+			//Lifted from PretransformVertices.cpp
+			ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix);
+			std::pair<aiMesh*,const aiBone*> push_pair(subMesh,pMesh->mBones[i]);
+
+			poNewMeshes.push_back(push_pair);		
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively updates the node's mesh list to account for the changed mesh list
+void DeboneProcess::UpdateNode(aiNode* pNode) const
+{
+	// rebuild the node's mesh index list
+	
+	std::vector<unsigned int> newMeshList;
+
+	// this will require two passes
+
+	unsigned int m = pNode->mNumMeshes, n = mSubMeshIndices.size();
+	
+	// first pass, look for meshes which have not moved
+
+	for(unsigned int a=0;a<m;a++)	{
+
+		unsigned int srcIndex = pNode->mMeshes[a];
+		const std::vector<std::pair<unsigned int,aiNode*>> &subMeshes = mSubMeshIndices[srcIndex];
+		unsigned int nSubmeshes = subMeshes.size();
+
+		for(unsigned int b=0;b<nSubmeshes;b++) {
+			if(!subMeshes[b].second) {
+				newMeshList.push_back(subMeshes[b].first);
+			}
+		}
+	}
+
+	// second pass, collect deboned meshes 
+
+	for(unsigned int a=0;a<n;a++)
+	{
+		const std::vector<std::pair<unsigned int,aiNode*>> &subMeshes = mSubMeshIndices[a];
+		unsigned int nSubmeshes = subMeshes.size();
+
+		for(unsigned int b=0;b<nSubmeshes;b++) {
+			if(subMeshes[b].second == pNode)	{
+				newMeshList.push_back(subMeshes[b].first);
+			}
+		}
+	}
+
+	if( pNode->mNumMeshes > 0 )	{
+		delete [] pNode->mMeshes; pNode->mMeshes = NULL;
+	}
+
+	pNode->mNumMeshes = newMeshList.size();
+
+	if(pNode->mNumMeshes)	{
+		pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+		std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
+	}
+
+	// do that also recursively for all children
+	for( unsigned int a = 0; a < pNode->mNumChildren; ++a )	{
+		UpdateNode( pNode->mChildren[a]);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the node transformation to a mesh
+void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const
+{
+	// Check whether we need to transform the coordinates at all
+	if (!mat.IsIdentity()) {
+		
+		if (mesh->HasPositions()) {
+			for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+				mesh->mVertices[i] = mat * mesh->mVertices[i];
+			}
+		}
+		if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+			aiMatrix4x4 mWorldIT = mat;
+			mWorldIT.Inverse().Transpose();
+
+			// TODO: implement Inverse() for aiMatrix3x3
+			aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+			if (mesh->HasNormals()) {
+				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+					mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
+				}
+			}
+			if (mesh->HasTangentsAndBitangents()) {
+				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+					mesh->mTangents[i]   = (m * mesh->mTangents[i]).Normalize();
+					mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
+				}
+			}
+		}
+	}
+}

+ 137 - 0
code/DeboneProcess.h

@@ -0,0 +1,137 @@
+				   /*
+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.
+
+----------------------------------------------------------------------
+*/
+
+/** Defines a post processing step to limit the number of bones affecting a single vertex. */
+#ifndef AI_DEBONEPROCESS_H_INC
+#define AI_DEBONEPROCESS_H_INC
+
+#include <vector>
+#include <utility>
+#include "BaseProcess.h"
+
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+class DeboneTest;
+
+namespace Assimp
+{
+
+#if (!defined AI_DEBONE_THRESHOLD)
+#	define AI_DEBONE_THRESHOLD	1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------------------------------------------------------------------------
+/** This post processing step removes bones nearly losslessly or according to 
+* a configured threshold. In order to remove the bone, the primitives affected by
+* the bone are split from the mesh. The split off (new) mesh is boneless. At any 
+* point in time, bones without affect upon a given mesh are to be removed.
+*/
+class ASSIMP_API DeboneProcess : public BaseProcess
+{
+	friend class Importer;
+	friend class ::DeboneTest;
+
+protected:
+	/** Constructor to be privately used by Importer */
+	DeboneProcess();
+
+	/** Destructor, private as well */
+	~DeboneProcess();
+
+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.
+	*/
+	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);
+
+	// -------------------------------------------------------------------
+	/** Counts bones total/removable in a given mesh.
+	* @param pMesh The mesh to process.
+	*/
+	bool ConsiderMesh( const aiMesh* pMesh);
+
+	/// 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<std::pair<aiMesh*,const aiBone*>>& poNewMeshes) const;
+
+	/// Recursively updates the node's mesh list to account for the changed mesh list
+	void UpdateNode(aiNode* pNode) const;
+
+	// -------------------------------------------------------------------
+	// Apply transformation to a mesh 
+	void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const;
+
+public:
+	/** Number of bones present in the scene. */
+	unsigned int mNumBones;
+	unsigned int mNumBonesCanDoWithout;
+
+	float mThreshold;
+	bool mAllOrNone;
+
+	/// Per mesh index: Array of indices of the new submeshes.
+	std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_DEBONEPROCESS_H_INC

+ 6 - 6
code/Importer.cpp

@@ -178,9 +178,6 @@ using namespace Assimp::Formatter;
 #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
 #	include "BlenderLoader.h"
 #endif
-//#ifndef ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER
-//#	include "SomLoader.h"
-//#endif
 #ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
 #	include "Q3BSPFileImporter.h"
 #endif
@@ -260,6 +257,9 @@ using namespace Assimp::Formatter;
 #ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
 #	include "SplitByBoneCountProcess.h"
 #endif
+#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
+#	include "DeboneProcess.h"
+#endif
 
 using namespace Assimp;
 using namespace Assimp::Intern;
@@ -426,9 +426,6 @@ Importer::Importer()
 #if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
 	pimpl->mImporter.push_back( new BlenderImporter());
 #endif
-//#if (!defined ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER)
-//	pimpl->mImporter.push_back( new SomImporter());
-//#endif
 #if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
 	pimpl->mImporter.push_back( new Q3BSPFileImporter() );
 #endif
@@ -522,6 +519,9 @@ Importer::Importer()
 #if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
 	pimpl->mPostProcessingSteps.push_back( new FlipWindingOrderProcess());
 #endif
+#if (!defined ASSIMP_BUILD_DEBONE_PROCESS)
+	pimpl->mPostProcessingSteps.push_back( new DeboneProcess());
+#endif
 #if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
 	pimpl->mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
 #endif

+ 410 - 0
code/ProcessHelper.cpp

@@ -0,0 +1,410 @@
+/*
+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 ProcessHelper.cpp
+/** Implement shared utility functions for postprocessing steps */
+
+#include "AssimpPCH.h"
+#include "ProcessHelper.h"
+
+
+#include <limits>
+
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
+{
+	const char* s = in.c_str();
+	while (*s) {
+		SkipSpacesAndLineEnd(&s);
+		if (*s == '\'') {
+			const char* base = ++s;
+			while (*s != '\'') {
+				++s;
+				if (*s == '\0') {
+					DefaultLogger::get()->error("ConvertListToString: String list is ill-formatted");
+					return;
+				}
+			}
+			out.push_back(std::string(base,(size_t)(s-base)));
+			++s;
+		}
+		else {
+			out.push_back(GetNextToken(s));
+		}
+	}
+}
+
+// -------------------------------------------------------------------------------
+void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, 
+	const aiMatrix4x4& m)
+{
+	min = aiVector3D (10e10f,  10e10f, 10e10f);
+	max = aiVector3D (-10e10f,-10e10f,-10e10f);
+	for (unsigned int i = 0;i < mesh->mNumVertices;++i)
+	{
+		const aiVector3D v = m * mesh->mVertices[i];
+		min = std::min(v,min);
+		max = std::max(v,max);
+	}
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
+{
+	ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
+	out = min + (max-min)*0.5f;
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
+	aiVector3D& max, const aiMatrix4x4& m)
+{
+	FindAABBTransformed(mesh,min,max,m);
+	out = min + (max-min)*0.5f;
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
+{
+	aiVector3D min,max;
+	FindMeshCenter(mesh,out,min,max);
+}
+
+// -------------------------------------------------------------------------------
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
+	const aiMatrix4x4& m)
+{
+	aiVector3D min,max;
+	FindMeshCenterTransformed(mesh,out,min,max,m);
+}
+
+// -------------------------------------------------------------------------------
+float ComputePositionEpsilon(const aiMesh* pMesh)
+{
+	const float epsilon = 1e-4f;
+
+	// calculate the position bounds so we have a reliable epsilon to check position differences against 
+	aiVector3D minVec, maxVec;
+	ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
+	return (maxVec - minVec).Length() * epsilon;
+}
+
+// -------------------------------------------------------------------------------
+float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
+{
+	const float epsilon = 1e-4f;
+
+	// calculate the position bounds so we have a reliable epsilon to check position differences against 
+	aiVector3D minVec, maxVec, mi, ma;
+	MinMaxChooser<aiVector3D>()(minVec,maxVec);
+
+	for (size_t a = 0; a < num; ++a) {
+		const aiMesh* pMesh = pMeshes[a];
+		ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
+
+		minVec = std::min(minVec,mi);
+		maxVec = std::max(maxVec,ma);
+	}
+	return (maxVec - minVec).Length() * epsilon;
+}
+
+
+// -------------------------------------------------------------------------------
+unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh)
+{
+	ai_assert(NULL != pcMesh);
+
+	// FIX: the hash may never be 0. Otherwise a comparison against
+	// nullptr could be successful
+	unsigned int iRet = 1;
+
+	// normals
+	if (pcMesh->HasNormals())iRet |= 0x2;
+	// tangents and bitangents
+	if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
+
+#ifdef BOOST_STATIC_ASSERT
+	BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
+	BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
+#endif
+
+	// texture coordinates
+	unsigned int p = 0;
+	while (pcMesh->HasTextureCoords(p))
+	{
+		iRet |= (0x100 << p);
+		if (3 == pcMesh->mNumUVComponents[p])
+			iRet |= (0x10000 << p);
+
+		++p;
+	}
+	// vertex colors
+	p = 0;
+	while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
+	return iRet;
+}
+
+// -------------------------------------------------------------------------------
+VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh)
+{
+	if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
+		return NULL;
+	}
+
+	VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
+	for (unsigned int i = 0; i < pMesh->mNumBones;++i)	{
+
+		aiBone* bone = pMesh->mBones[i];
+		for (unsigned int a = 0; a < bone->mNumWeights;++a)	{
+			const aiVertexWeight& weight = bone->mWeights[a];
+			avPerVertexWeights[weight.mVertexId].push_back( std::pair<unsigned int,float>(i,weight.mWeight) );
+		}
+	}
+	return avPerVertexWeights;
+}
+
+
+// -------------------------------------------------------------------------------
+const char* TextureTypeToString(aiTextureType in)
+{
+	switch (in)
+	{
+	case aiTextureType_NONE:
+		return "n/a";
+	case aiTextureType_DIFFUSE:
+		return "Diffuse";
+	case aiTextureType_SPECULAR:
+		return "Specular";
+	case aiTextureType_AMBIENT:
+		return "Ambient";
+	case aiTextureType_EMISSIVE:
+		return "Emissive";
+	case aiTextureType_OPACITY:
+		return "Opacity";
+	case aiTextureType_NORMALS:
+		return "Normals";
+	case aiTextureType_HEIGHT:
+		return "Height";
+	case aiTextureType_SHININESS:
+		return "Shininess";
+	case aiTextureType_DISPLACEMENT:
+		return "Displacement";
+	case aiTextureType_LIGHTMAP:
+		return "Lightmap";
+	case aiTextureType_REFLECTION:
+		return "Reflection";
+	case aiTextureType_UNKNOWN:
+		return "Unknown";
+	}
+   
+	ai_assert(false);
+    return  "BUG";          
+}
+
+// -------------------------------------------------------------------------------
+const char* MappingTypeToString(aiTextureMapping in)
+{
+	switch (in)
+	{
+	case aiTextureMapping_UV:
+		return "UV";
+	case aiTextureMapping_BOX:
+		return "Box";
+	case aiTextureMapping_SPHERE:
+		return "Sphere";
+	case aiTextureMapping_CYLINDER:
+		return "Cylinder";
+	case aiTextureMapping_PLANE:
+		return "Plane";
+	case aiTextureMapping_OTHER:
+		return "Other";
+	}
+
+	ai_assert(false);
+    return  "BUG";   
+}
+
+
+// -------------------------------------------------------------------------------
+aiMesh* MakeSubmesh(const aiMesh *pMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags)
+{		
+	aiMesh *oMesh = new aiMesh(); 
+	std::vector<unsigned int> vMap(pMesh->mNumVertices,UINT_MAX);
+
+	size_t numSubVerts = 0; 
+	size_t numSubFaces = subMeshFaces.size();
+
+	for(unsigned int i=0;i<numSubFaces;i++)	{
+		const aiFace &f = pMesh->mFaces[subMeshFaces[i]];
+
+		for(unsigned int j=0;j<f.mNumIndices;j++)	{
+			if(vMap[f.mIndices[j]]==UINT_MAX)	{
+				vMap[f.mIndices[j]] = numSubVerts++;
+			}
+		}		
+	} 
+
+	oMesh->mName = pMesh->mName;
+		
+	oMesh->mMaterialIndex = pMesh->mMaterialIndex;
+	oMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
+	
+	// create all the arrays for this mesh if the old mesh contained them
+		
+	oMesh->mNumFaces = subMeshFaces.size();
+	oMesh->mNumVertices = numSubVerts;
+	oMesh->mVertices = new aiVector3D[numSubVerts];
+	if( pMesh->HasNormals() ) {
+		oMesh->mNormals = new aiVector3D[numSubVerts];
+	}
+
+	if( pMesh->HasTangentsAndBitangents() )	{
+		oMesh->mTangents = new aiVector3D[numSubVerts];
+		oMesh->mBitangents = new aiVector3D[numSubVerts];
+	}
+
+	for( size_t a = 0;  pMesh->HasTextureCoords( a) ; ++a )	{
+		oMesh->mTextureCoords[a] = new aiVector3D[numSubVerts];
+		oMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
+	}
+
+	for( size_t a = 0; pMesh->HasVertexColors( a); ++a )	{
+		oMesh->mColors[a] = new aiColor4D[numSubVerts];
+	}
+
+	// and copy over the data, generating faces with linear indices along the way
+	oMesh->mFaces = new aiFace[numSubFaces];
+	
+	for(unsigned int a = 0; a < numSubFaces; ++a )	{
+
+		const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
+		aiFace& dstFace = oMesh->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 )	{
+			dstFace.mIndices[b] = vMap[srcFace.mIndices[b]];
+		}
+	}
+
+	for(unsigned int srcIndex = 0; srcIndex < pMesh->mNumVertices; ++srcIndex ) {
+		unsigned int nvi = vMap[srcIndex]; 
+		if(nvi==UINT_MAX) {
+			continue;
+		}
+
+		oMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
+		if( pMesh->HasNormals() ) {
+			oMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
+		}
+		
+		if( pMesh->HasTangentsAndBitangents() )	{
+			oMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
+			oMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
+		}
+		for( size_t c = 0, cc = pMesh->GetNumUVChannels(); c < cc; ++c )	{
+				oMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
+		}
+		for( size_t c = 0, cc = pMesh->GetNumColorChannels(); c < cc; ++c )	{
+			oMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
+		}
+	}
+
+	if(~subFlags&AI_SUBMESH_FLAGS_SANS_BONES)	{			
+		std::vector<unsigned int> subBones(pMesh->mNumBones,0);
+
+		for(unsigned int a=0;a<pMesh->mNumBones;++a)	{
+			const aiBone* bone = pMesh->mBones[a];
+
+			for(unsigned int b=0;b<bone->mNumWeights;b++)	{
+				unsigned int v = vMap[bone->mWeights[b].mVertexId];
+
+				if(v!=UINT_MAX) {
+					subBones[a]++;
+				}
+			}
+		}
+
+		for(unsigned int a=0;a<pMesh->mNumBones;++a)	{
+			if(subBones[a]>0) {
+				oMesh->mNumBones++;
+			}
+		}
+
+		if(oMesh->mNumBones) {
+			oMesh->mBones = new aiBone*[oMesh->mNumBones]();
+			unsigned int nbParanoia = oMesh->mNumBones;
+
+			oMesh->mNumBones = 0; //rewind
+
+			for(unsigned int a=0;a<pMesh->mNumBones;++a)	{
+				if(subBones[a]==0) {
+					continue; 
+				}
+				aiBone *newBone = new aiBone;
+				oMesh->mBones[oMesh->mNumBones++] = newBone;
+
+				const aiBone* bone = pMesh->mBones[a];
+
+				newBone->mName = bone->mName;
+				newBone->mOffsetMatrix = bone->mOffsetMatrix;
+				newBone->mWeights = new aiVertexWeight[subBones[a]];
+
+				for(unsigned int b=0;b<bone->mNumWeights;b++)	{
+					const unsigned int v = vMap[bone->mWeights[b].mVertexId];
+
+					if(v!=UINT_MAX)	{	
+						aiVertexWeight w(v,bone->mWeights[b].mWeight);
+						newBone->mWeights[newBone->mNumWeights++] = w;
+					}
+				}
+			}
+
+			ai_assert(nbParanoia==oMesh->mNumBones);
+		}
+	}					 
+
+	return oMesh;
+}
+
+} // namespace Assimp

+ 42 - 212
code/ProcessHelper.h

@@ -191,34 +191,6 @@ inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
 	}
 }
 
-// -------------------------------------------------------------------------------
-/** @brief Extract single strings from a list of identifiers
- *  @param in Input string list. 
- *  @param out Receives a list of clean output strings
- *  @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST
- */
-inline void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
-{
-	const char* s = in.c_str();
-	while (*s) {
-		SkipSpacesAndLineEnd(&s);
-		if (*s == '\'') {
-			const char* base = ++s;
-			while (*s != '\'') {
-				++s;
-				if (*s == '\0') {
-					DefaultLogger::get()->error("ConvertListToString: String list is ill-formatted");
-					return;
-				}
-			}
-			out.push_back(std::string(base,(size_t)(s-base)));
-			++s;
-		}
-		else {
-			out.push_back(GetNextToken(s));
-		}
-	}
-}
 
 // -------------------------------------------------------------------------------
 /** @brief Compute the newell normal of a polygon regardless of its shape
@@ -271,55 +243,37 @@ inline void NewellNormal (aiVector3D& out, int num, float* x, float* y, float* z
 	out = aiVector3D(sum_yz,sum_zx,sum_xy);
 }
 
-#if 0
-// -------------------------------------------------------------------------------
-/** @brief Compute newell normal of a polgon regardless of its shape
- *
- *  @param out Receives the output normal
- *  @param data Input vertices
- *  @param idx Index buffer
- *  @param num Number of indices
- */
-inline void NewellNormal (aiVector3D& out, const aiVector3D* data, unsigned int* idx, unsigned int num )
-{
-	// TODO: intended to be used in GenNormals. 
-}
-#endif
+
 
 // -------------------------------------------------------------------------------
 /** Little helper function to calculate the quadratic difference 
  * of two colours. 
  * @param pColor1 First color
  * @param pColor2 second color
- * @return Quadratic color difference
- */
+ * @return Quadratic color difference */
 inline float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2) 
 {
-	const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, 
-		pColor1.b - pColor2.b, pColor1.a - pColor2.a);
-
+	const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g, pColor1.b - pColor2.b, pColor1.a - pColor2.a);
 	return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
 }
 
+
+// -------------------------------------------------------------------------------
+/** @brief Extract single strings from a list of identifiers
+ *  @param in Input string list. 
+ *  @param out Receives a list of clean output strings
+ *  @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST */
+void ConvertListToStrings(const std::string& in, std::list<std::string>& out);
+
+
 // -------------------------------------------------------------------------------
 /** @brief Compute the AABB of a mesh after applying a given transform
  *  @param mesh Input mesh
  *  @param[out] min Receives minimum transformed vertex
  *  @param[out] max Receives maximum transformed vertex
- *  @param m Transformation matrix to be applied
- */
-inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, 
-	const aiMatrix4x4& m)
-{
-	min = aiVector3D (10e10f,  10e10f, 10e10f);
-	max = aiVector3D (-10e10f,-10e10f,-10e10f);
-	for (unsigned int i = 0;i < mesh->mNumVertices;++i)
-	{
-		const aiVector3D v = m * mesh->mVertices[i];
-		min = std::min(v,min);
-		max = std::max(v,max);
-	}
-}
+ *  @param m Transformation matrix to be applied */
+void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max, const aiMatrix4x4& m);
+
 
 // -------------------------------------------------------------------------------
 /** @brief Helper function to determine the 'real' center of a mesh
@@ -328,191 +282,65 @@ inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D
  *  @param mesh Input mesh
  *  @param[out] min Minimum vertex of the mesh
  *  @param[out] max maximum vertex of the mesh
- *  @param[out] out Center point
- */
-inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
-{
-	ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
-	out = min + (max-min)*0.5f;
-}
+ *  @param[out] out Center point */
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max);
+
 
 // -------------------------------------------------------------------------------
 // Helper function to determine the 'real' center of a mesh after applying a given transform
-inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
-	aiVector3D& max, const aiMatrix4x4& m)
-{
-	FindAABBTransformed(mesh,min,max,m);
-	out = min + (max-min)*0.5f;
-}
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,aiVector3D& max, const aiMatrix4x4& m);
+
 
 // -------------------------------------------------------------------------------
 // Helper function to determine the 'real' center of a mesh
-inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
-{
-	aiVector3D min,max;
-	FindMeshCenter(mesh,out,min,max);
-}
+void FindMeshCenter (aiMesh* mesh, aiVector3D& out);
+
 
 // -------------------------------------------------------------------------------
 // Helper function to determine the 'real' center of a mesh after applying a given transform
-inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
-	const aiMatrix4x4& m)
-{
-	aiVector3D min,max;
-	FindMeshCenterTransformed(mesh,out,min,max,m);
-}
+void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,const aiMatrix4x4& m);
+
 
 // -------------------------------------------------------------------------------
 // Compute a good epsilon value for position comparisons on a mesh
-inline float ComputePositionEpsilon(const aiMesh* pMesh)
-{
-	const float epsilon = 1e-4f;
+float ComputePositionEpsilon(const aiMesh* pMesh);
 
-	// calculate the position bounds so we have a reliable epsilon to check position differences against 
-	aiVector3D minVec, maxVec;
-	ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
-	return (maxVec - minVec).Length() * epsilon;
-}
 
 // -------------------------------------------------------------------------------
 // Compute a good epsilon value for position comparisons on a array of meshes
-inline float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
-{
-	const float epsilon = 1e-4f;
-
-	// calculate the position bounds so we have a reliable epsilon to check position differences against 
-	aiVector3D minVec, maxVec, mi, ma;
-	MinMaxChooser<aiVector3D>()(minVec,maxVec);
-
-	for (size_t a = 0; a < num; ++a) {
-		const aiMesh* pMesh = pMeshes[a];
-		ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
+float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num);
 
-		minVec = std::min(minVec,mi);
-		maxVec = std::max(maxVec,ma);
-	}
-	return (maxVec - minVec).Length() * epsilon;
-}
 
 // -------------------------------------------------------------------------------
 // Compute an unique value for the vertex format of a mesh
-inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
-{
-	ai_assert(NULL != pcMesh);
-
-	// FIX: the hash may never be 0. Otherwise a comparison against
-	// nullptr could be successful
-	unsigned int iRet = 1;
-
-	// normals
-	if (pcMesh->HasNormals())iRet |= 0x2;
-	// tangents and bitangents
-	if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
+unsigned int GetMeshVFormatUnique(const aiMesh* pcMesh);
 
-#ifdef BOOST_STATIC_ASSERT
-	BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
-	BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
-#endif
-
-	// texture coordinates
-	unsigned int p = 0;
-	while (pcMesh->HasTextureCoords(p))
-	{
-		iRet |= (0x100 << p);
-		if (3 == pcMesh->mNumUVComponents[p])
-			iRet |= (0x10000 << p);
-
-		++p;
-	}
-	// vertex colors
-	p = 0;
-	while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
-	return iRet;
-}
 
+// defs for ComputeVertexBoneWeightTable()
 typedef std::pair <unsigned int,float> PerVertexWeight;
 typedef std::vector	<PerVertexWeight> VertexWeightTable;
 
 // -------------------------------------------------------------------------------
 // Compute a per-vertex bone weight table
-// please .... delete result with operator delete[] ...
-inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
-{
-	if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones)
-		return NULL;
+VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh);
 
-	VertexWeightTable* avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
-	for (unsigned int i = 0; i < pMesh->mNumBones;++i)
-	{
-		aiBone* bone = pMesh->mBones[i];
-		for (unsigned int a = 0; a < bone->mNumWeights;++a)	{
-			const aiVertexWeight& weight = bone->mWeights[a];
-			avPerVertexWeights[weight.mVertexId].push_back( 
-				std::pair<unsigned int,float>(i,weight.mWeight));
-		}
-	}
-	return avPerVertexWeights;
-}
 
 // -------------------------------------------------------------------------------
 // Get a string for a given aiTextureType
-inline const char* TextureTypeToString(aiTextureType in)
-{
-	switch (in)
-	{
-	case aiTextureType_NONE:
-		return "n/a";
-	case aiTextureType_DIFFUSE:
-		return "Diffuse";
-	case aiTextureType_SPECULAR:
-		return "Specular";
-	case aiTextureType_AMBIENT:
-		return "Ambient";
-	case aiTextureType_EMISSIVE:
-		return "Emissive";
-	case aiTextureType_OPACITY:
-		return "Opacity";
-	case aiTextureType_NORMALS:
-		return "Normals";
-	case aiTextureType_HEIGHT:
-		return "Height";
-	case aiTextureType_SHININESS:
-		return "Shininess";
-	case aiTextureType_DISPLACEMENT:
-		return "Displacement";
-	case aiTextureType_LIGHTMAP:
-		return "Lightmap";
-	case aiTextureType_REFLECTION:
-		return "Reflection";
-	case aiTextureType_UNKNOWN:
-		return "Unknown";
-    default:
-        return  "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";          
-	}
-}
+const char* TextureTypeToString(aiTextureType in);
+
 
 // -------------------------------------------------------------------------------
 // Get a string for a given aiTextureMapping
-inline const char* MappingTypeToString(aiTextureMapping in)
-{
-	switch (in)
-	{
-	case aiTextureMapping_UV:
-		return "UV";
-	case aiTextureMapping_BOX:
-		return "Box";
-	case aiTextureMapping_SPHERE:
-		return "Sphere";
-	case aiTextureMapping_CYLINDER:
-		return "Cylinder";
-	case aiTextureMapping_PLANE:
-		return "Plane";
-	case aiTextureMapping_OTHER:
-		return "Other";
-    default:
-        return  "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";    
-	}
-}
+const char* MappingTypeToString(aiTextureMapping in);
+
+
+// flags for MakeSubmesh()
+#define AI_SUBMESH_FLAGS_SANS_BONES	0x1
+
+// -------------------------------------------------------------------------------
+// Split a mesh given a list of faces to be contained in the sub mesh
+aiMesh* MakeSubmesh(const aiMesh *superMesh, const std::vector<unsigned int> &subMeshFaces, unsigned int subFlags);
 
 // -------------------------------------------------------------------------------
 // Utility postprocess step to share the spatial sort tree between
@@ -560,5 +388,7 @@ class DestroySpatialSortProcess : public BaseProcess
 	}
 };
 
+
+
 } // ! namespace Assimp
 #endif // !! AI_PROCESS_HELPER_H_INCLUDED

+ 23 - 0
include/aiConfig.h

@@ -288,6 +288,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #	define AI_LMW_MAX_WEIGHTS	0x4
 #endif // !! AI_LMW_MAX_WEIGHTS
 
+// ---------------------------------------------------------------------------
+/** @brief Set the deboning threshold higher to remove more bones
+ *
+ * This is used by the #aiProcess_Debone PostProcess-Step.
+ * @note The default value is AI_DEBONE_THRESHOLD
+ * Property type: float.*/
+#define AI_CONFIG_PP_DB_THRESHOLD \
+	"PP_DB_THRESHOLD"
+
+// default value for AI_CONFIG_PP_LBW_MAX_WEIGHTS
+#if (!defined AI_DEBONE_THRESHOLD)
+#	define AI_DEBONE_THRESHOLD	1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------------------------------------------------------------------------
+/** @brief Require all bones qualify for deboning before removing any
+ *
+ * This is used by the #aiProcess_Debone PostProcess-Step.
+ * @note The default value is 0
+ * Property type: bool.*/
+#define AI_CONFIG_PP_DB_ALL_OR_NONE \
+	"PP_DB_ALL_OR_NONE"
+
 /** @brief Default value for the #AI_CONFIG_PP_ICL_PTCACHE_SIZE property
  */
 #ifndef PP_ICL_PTCACHE_SIZE

+ 16 - 1
include/aiPostProcess.h

@@ -500,7 +500,21 @@ enum aiPostProcessSteps
 	/** <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_SplitByBoneCount  = 0x2000000,
+
+	// -------------------------------------------------------------------------
+	/** <hr>This step removes bones losslessly or according to some threshold.
+	 *  In some cases (i.e. format that require it) exporters are forced to
+	 *  assign dummy bone weights to otherwise static meshes assigned to
+	 *  animated meshes. Since full, weight-based skinning is expensive but
+	 *  animating nodes is extremely cheap, this step is offered to cleanup
+	 *  the data in that regard. 
+	 *																
+	 *  Use <tt>#AI_CONFIG_PP_DB_THRESHOLD</tt> to control this. 
+	 *  Use <tt>#AI_CONFIG_PP_DB_ALL_OR_NONE</tt> if you want bones removed if and 
+	 *	only if all bones within the scene qualify for removal.
+    */
+	aiProcess_Debone  = 0x4000000
 
 	// aiProcess_GenEntityMeshes = 0x100000,
 	// aiProcess_OptimizeAnimations = 0x200000
@@ -600,6 +614,7 @@ enum aiPostProcessSteps
 	aiProcess_FindInstances                  |  \
 	aiProcess_ValidateDataStructure          |  \
 	aiProcess_OptimizeMeshes                 |  \
+	aiProcess_Debone						 |  \
 	0 )