Selaa lähdekoodia

Changed some runtime asserts to boost::static_asserts.
Added FindInstances postprocessing step. Not fully tested yet, but seems to work well. Enabled it for the viewer, too.

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

aramis_acg 16 vuotta sitten
vanhempi
commit
4b4526953e

+ 2 - 0
code/AssimpPCH.h

@@ -108,6 +108,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #	include "../include/BoostWorkaround/boost/scoped_array.hpp"
 #	include "../include/BoostWorkaround/boost/format.hpp"
 #	include "../include/BoostWorkaround/boost/foreach.hpp"
+#	include "../include/BoostWorkaround/boost/static_assert.hpp"
 
 #else
 
@@ -115,6 +116,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #	include <boost/scoped_array.hpp>
 #	include <boost/format.hpp>
 #	include <boost/foreach.hpp>
+#	include <boost/static_assert.hpp>
 
 #endif // ! ASSIMP_BUILD_BOOST_WORKAROUND
 #endif // !! ASSIMP_PCH_INCLUDED

+ 254 - 0
code/FindInstancesProcess.cpp

@@ -0,0 +1,254 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2008, 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  FindInstancesProcess.cpp
+ *  @brief Implementation of the aiProcess_FindInstances postprocessing step
+*/
+
+#include "AssimpPCH.h"
+#include "FindInstancesProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindInstancesProcess::FindInstancesProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindInstancesProcess::~FindInstancesProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindInstancesProcess::IsActive( unsigned int pFlags) const
+{
+	return 0 != (pFlags & aiProcess_FindInstances);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compare the bones of two meshes
+bool CompareBones(const aiMesh* orig, const aiMesh* inst)
+{
+	for (unsigned int i = 0; i < orig->mNumBones;++i) {
+		aiBone* aha = orig->mBones[i];
+		aiBone* oha = inst->mBones[i];
+
+		if (aha->mNumWeights   != oha->mNumWeights   ||
+			aha->mOffsetMatrix != oha->mOffsetMatrix ||
+			aha->mNumWeights   != oha->mNumWeights) {
+			return false;
+		}
+
+		// compare weight per weight ---
+		for (unsigned int n = 0; n < aha->mNumWeights;++n) {
+			if  (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId ||
+				(aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) {
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update mesh indices in the node graph
+void UpdateMeshIndices(aiNode* node, unsigned int* lookup)
+{
+	for (unsigned int n = 0; n < node->mNumMeshes;++n)
+		node->mMeshes[n] = lookup[node->mMeshes[n]];
+
+	for (unsigned int n = 0; n < node->mNumChildren;++n)
+		UpdateMeshIndices(node->mChildren[n],lookup);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindInstancesProcess::Execute( aiScene* pScene)
+{
+	DefaultLogger::get()->debug("FindInstancesProcess begin");
+	if (pScene->mNumMeshes) {
+
+		// use a pseudo hash for all meshes in the scene to quickly find 
+		// the ones which are possibly equal. This step is executed early 
+		// in the pipeline, so we could, depending on the file format,
+		// have several thousand small meshes. That's too much for a brute
+		// everyone-against-everyone check involving up to 25 comparisons
+		// each.
+		boost::scoped_array<uint64_t> hashes (new uint64_t[pScene->mNumMeshes]);
+		boost::scoped_array<unsigned int> remapping (new unsigned int[pScene->mNumMeshes]);
+
+		unsigned int numMeshesOut = 0;
+		for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+
+			aiMesh* inst = pScene->mMeshes[i];
+			hashes[i] = GetMeshHash(inst);
+
+			for (int a = i-1; a > 0; --a) {
+				if (hashes[i] == hashes[a])
+				{
+					aiMesh* orig = pScene->mMeshes[a];
+					if (!orig)
+						continue;
+					
+					// check for hash collision .. we needn't check
+					// the vertex format, it *must* match due to the
+					// (brilliant) construction of the hash
+					if (orig->mNumBones       != inst->mNumBones      ||
+						orig->mNumFaces       != inst->mNumFaces      ||
+						orig->mNumVertices    != inst->mNumVertices   ||
+						orig->mMaterialIndex  != inst->mMaterialIndex ||
+						orig->mPrimitiveTypes != inst->mPrimitiveTypes)
+						continue;
+
+					// up to now the meshes are equal. find an appropriate
+					// epsilon to compare position differences against
+					float epsilon = ComputePositionEpsilon(inst);
+					epsilon *= epsilon;
+
+					// now compare vertex positions, normals,
+					// tangents and bitangents using this epsilon.
+					if (orig->HasPositions()) {
+						if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
+							continue;
+					}
+					if (orig->HasNormals()) {
+						if(!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon))
+							continue;
+					}
+					if (orig->HasTangentsAndBitangents()) {
+						if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) ||
+							!CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon))
+							continue;
+					}
+
+					// use a constant epsilon for colors and UV coordinates
+					static const float uvEpsilon = 10e-4f;
+
+					BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
+
+					// as in JIV: manually unrolled as continue wouldn't work as desired in inner loops
+					if (orig->mTextureCoords[0]) {
+						if(!CompareArrays(orig->mTextureCoords[0],inst->mTextureCoords[0],orig->mNumVertices,uvEpsilon))
+							continue;
+						if (orig->mTextureCoords[1]) {
+							if(!CompareArrays(orig->mTextureCoords[1],inst->mTextureCoords[1],orig->mNumVertices,uvEpsilon))
+								continue;
+							if (orig->mTextureCoords[2]) {
+								if(!CompareArrays(orig->mTextureCoords[2],inst->mTextureCoords[2],orig->mNumVertices,uvEpsilon))
+									continue;
+								if (orig->mTextureCoords[3]) {
+									if(!CompareArrays(orig->mTextureCoords[3],inst->mTextureCoords[3],orig->mNumVertices,uvEpsilon))
+										continue;
+								}
+							}
+						}
+					}
+
+					BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
+
+					// and the same nasty stuff for vertex colors ...
+					if (orig->mColors[0]) {
+						if(!CompareArrays(orig->mColors[0],inst->mColors[0],orig->mNumVertices,uvEpsilon))
+							continue;
+						if (orig->mTextureCoords[1]) {
+							if(!CompareArrays(orig->mColors[1],inst->mColors[1],orig->mNumVertices,uvEpsilon))
+								continue;
+							if (orig->mTextureCoords[2]) {
+								if(!CompareArrays(orig->mColors[2],inst->mColors[2],orig->mNumVertices,uvEpsilon))
+									continue;
+								if (orig->mTextureCoords[3]) {
+									if(!CompareArrays(orig->mColors[3],inst->mColors[3],orig->mNumVertices,uvEpsilon))
+										continue;
+								}
+							}
+						}
+					}
+
+					// It seems to be strange, but we really need to check whether the
+					// bones are identical too. Although it's extremely unprobable
+					// that they're not if control reaches here, but we need to deal
+					// with unprobable cases, too.
+					if (!CompareBones(orig,inst))
+						continue;
+
+					// FIXME: Ignore the faces for the moment ... ok!?
+				
+					// We're still here. Or in other words: 'inst' is an instance of 'orig'.
+					// Place a marker in our list that we can easily update mesh indices.
+					remapping[i] = a;
+
+					// Delete the instanced mesh, we don't need it anymore
+					delete inst;
+					pScene->mMeshes[i] = NULL;
+				}
+			}
+
+			// If we didn't find a match for the current mesh: keep it
+			if (pScene->mMeshes[i]) {
+				remapping[i] = numMeshesOut++;
+			}
+		}
+		ai_assert(0 != numMeshesOut);
+		if (numMeshesOut != pScene->mNumMeshes) {
+
+			// Collapse the meshes array by removing all NULL entries
+			for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) {
+				if (pScene->mMeshes[i])
+					pScene->mMeshes[real++] = pScene->mMeshes[i];
+			}
+
+			// And update the nodegraph with our nice lookup table
+			UpdateMeshIndices(pScene->mRootNode,remapping.get());
+
+			// write to log
+			if (!DefaultLogger::isNullLogger()) {
+			
+				char buffer[512];
+				::sprintf(buffer,"FindInstancesProcess finished. Found %i instances",pScene->mNumMeshes-numMeshesOut);
+				DefaultLogger::get()->info(buffer); 
+			}
+			pScene->mNumMeshes = numMeshesOut;
+		}
+		else DefaultLogger::get()->debug("FindInstancesProcess finished. No instanced meshes found"); 
+	}
+}

+ 134 - 0
code/FindInstancesProcess.h

@@ -0,0 +1,134 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, 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  FindInstancesProcess.h
+ *  @brief Declares the aiProcess_FindInstances post-process step
+ */
+#ifndef AI_FINDINSTANCES_H_INC
+#define AI_FINDINSTANCES_H_INC
+
+#include "BaseProcess.h"
+#include "ProcessHelper.h"
+
+class FindInstancesProcessTest;
+namespace Assimp	{
+
+// -------------------------------------------------------------------------------
+/** @brief Get a pseudo(!)-hash representing a mesh.
+ *
+ *  The hash is built from number of vertices, faces, primitive types,
+ *  .... but *not* from the real mesh data. It isn't absolutely unique.
+ *  @param in Input mesh
+ *  @return Hash. 
+ */
+inline uint64_t GetMeshHash(aiMesh* in) 
+{
+	ai_assert(NULL != in);
+
+	// ... get an unique value representing the vertex format of the mesh
+	const unsigned int fhash = GetMeshVFormatUnique(in);
+
+	// and bake it with number of vertices/faces/bones/matidx/ptypes
+	return ((uint64_t)fhash << 32u) | ((
+		(in->mNumBones << 16u) ^  (in->mNumVertices)       ^
+		(in->mNumFaces<<4u)    ^  (in->mMaterialIndex<<15) ^
+		(in->mPrimitiveTypes<<28)) & 0xffffffff );
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Perform a component-wise comparison of two arrays
+ *
+ *  @param first First array
+ *  @param second Second aray
+ *  @param size Size of both arrays
+ *  @param e Epsilon
+ *  @return true if the arrays are identical
+ */
+inline bool CompareArrays(const aiVector3D* first, const aiVector3D* second, 
+	unsigned int size, float e) 
+{
+	for (const aiVector3D* end = first+size; first != end; ++first,++second) {
+		if ( (*first - *second).SquareLength() >= e)
+			return false;
+	}
+	return true;
+}
+
+// and the same for colors ...
+inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second, 
+	unsigned int size, float e) 
+{
+	for (const aiColor4D* end = first+size; first != end; ++first,++second) {
+		if ( GetColorDifference(*first,*second) >= e)
+			return false;
+	}
+	return true;
+}
+
+// ---------------------------------------------------------------------------
+/** @brief A post-processing steps to search for instanced meshes
+*/
+class ASSIMP_API FindInstancesProcess : public BaseProcess
+{
+	friend class Importer;
+	friend class ::FindInstancesProcessTest; 
+
+protected:
+	/** Constructor to be privately used by Importer */
+	FindInstancesProcess();
+
+	/** Destructor, private as well */
+	~FindInstancesProcess();
+
+public:
+	// -------------------------------------------------------------------
+	// Check whether step is active in given flags combination
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	// Execute step on a given scene
+	void Execute( aiScene* pScene);
+
+private:
+
+}; // ! end class FindInstancesProcess
+}  // ! end namespace Assimp
+
+#endif // !! AI_FINDINSTANCES_H_INC

+ 11 - 6
code/Importer.cpp

@@ -196,6 +196,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef AI_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
 #	include "TextureTransform.h"
 #endif
+#ifndef AI_BUILD_NO_FINDINSTANCES_PROCESS
+#	include "FindInstancesProcess.h"
+#endif
 
 using namespace Assimp;
 using namespace Assimp::Intern;
@@ -334,6 +337,14 @@ Importer::Importer()
 	mPostProcessingSteps.push_back( new ValidateDSProcess()); 
 #endif
 
+#if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
+	mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
+#endif
+
+#if (!defined AI_BUILD_NO_FINDINSTANCES_PROCESS)
+	mPostProcessingSteps.push_back( new FindInstancesProcess());
+#endif
+
 
 #if (!defined AI_BUILD_NO_FINDDEGENERATES_PROCESS)
 	mPostProcessingSteps.push_back( new FindDegeneratesProcess());
@@ -353,12 +364,6 @@ Importer::Importer()
 	mPostProcessingSteps.push_back( new TextureTransformStep());
 #endif
 
-
-
-
-#if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
-	mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
-#endif
 #if (!defined AI_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
 	mPostProcessingSteps.push_back( new PretransformVertices());
 #endif

+ 2 - 2
code/JoinVerticesProcess.cpp

@@ -209,7 +209,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 			if( (uv.mBitangent - v.mBitangent).SquareLength() > squareEpsilon)
 				continue;
 			// manually unrolled because continue wouldn't work as desired in an inner loop
-			ai_assert( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
+			BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
 			if( GetColorDifference( uv.mColors[0], v.mColors[0]) > squareEpsilon)
 				continue;
 			if( GetColorDifference( uv.mColors[1], v.mColors[1]) > squareEpsilon)
@@ -219,7 +219,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 			if( GetColorDifference( uv.mColors[3], v.mColors[3]) > squareEpsilon)
 				continue;
 			// texture coord matching manually unrolled as well
-			ai_assert( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
+			BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_TEXTURECOORDS);
 			if( (uv.mTexCoords[0] - v.mTexCoords[0]).SquareLength() > squareEpsilon)
 				continue;
 			if( (uv.mTexCoords[1] - v.mTexCoords[1]).SquareLength() > squareEpsilon)

+ 0 - 14
code/JoinVerticesProcess.h

@@ -95,20 +95,6 @@ protected:
 	 * @param meshIndex Index of the mesh to process
 	 */
 	int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
-
-	// -------------------------------------------------------------------
-	/** Little helper function to calculate the quadratic difference 
-	 * of two colours. 
-	 * @param pColor1 First color
-	 * @param pColor2 second color
-	 * @return Quadratic color difference
-	 */
-	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);
-		return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
-	}
 };
 
 } // end of namespace Assimp

+ 2 - 26
code/PretransformVertices.cpp

@@ -44,7 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "AssimpPCH.h"
 #include "PretransformVertices.h"
-
+#include "ProcessHelper.h"
 
 using namespace Assimp;
 
@@ -95,32 +95,8 @@ unsigned int GetMeshVFormat(aiMesh* pcMesh)
 	if (pcMesh->mBones)
 		return (unsigned int)(unsigned long)pcMesh->mBones;
 
-	ai_assert(NULL != pcMesh->mVertices);
-
-	// 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;
 
-	// texture coordinates
-	unsigned int p = 0;
-	ai_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
-	while (pcMesh->HasTextureCoords(p))
-	{
-		iRet |= (0x100 << p);
-		if (3 == pcMesh->mNumUVComponents[p])
-			iRet |= (0x10000 << p);
-
-		++p;
-	}
-	// vertex colors
-	p = 0;
-	ai_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
-	while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
+	const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
 
 	// store the value for later use
 	pcMesh->mBones = (aiBone**)(unsigned long)iRet;

+ 68 - 16
code/ProcessHelper.h

@@ -48,11 +48,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 
-typedef std::pair< unsigned int,float > PerVertexWeight;
-typedef std::vector< PerVertexWeight > VertexWeightTable;
+// some aliases to make the whole stuff easier to read
+typedef std::pair	< unsigned int,float > PerVertexWeight;
+typedef std::vector	< PerVertexWeight    > VertexWeightTable;
+
+// -------------------------------------------------------------------------------
+/** Little helper function to calculate the quadratic difference 
+ * of two colours. 
+ * @param pColor1 First color
+ * @param pColor2 second color
+ * @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);
+
+	return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
+}
 
-// ------------------------------------------------------------------------------------------------
-// compute a good epsilon value for position comparisons
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a mesh
 inline float ComputePositionEpsilon(const aiMesh* pMesh)
 {
 	const float epsilon = 1e-5f;
@@ -71,20 +87,54 @@ inline float ComputePositionEpsilon(const aiMesh* pMesh)
 	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;
+
+	BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
+	BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
+
+	// 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;
+}
+
+// -------------------------------------------------------------------------------
 // Compute a per-vertex bone weight table
-// NOTE: delete result with operator delete[] ...
+// please .... delete result with operator delete[] ...
 inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
 {
-	if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) return NULL;
+	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)
-		{
-			aiVertexWeight& weight = bone->mWeights[a];
+		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));
 		}
@@ -93,7 +143,7 @@ inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
 }
 
 
-// ------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
 // Get a string for a given aiTextureType
 inline const char* TextureTypeToString(aiTextureType in)
 {
@@ -120,7 +170,7 @@ inline const char* TextureTypeToString(aiTextureType in)
 	}
 }
 
-// ------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
 // Get a string for a given aiTextureMapping
 inline const char* MappingTypeToString(aiTextureMapping in)
 {
@@ -143,7 +193,9 @@ inline const char* MappingTypeToString(aiTextureMapping in)
 	}
 }
 
-// ------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
+// Utility postprocess step to share the spatial sort tree between
+// all steps which use it to speedup its computations.
 class ComputeSpatialSortProcess : public BaseProcess
 {
 	bool IsActive( unsigned int pFlags) const
@@ -159,8 +211,7 @@ class ComputeSpatialSortProcess : public BaseProcess
 		std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes); 
 		std::vector<_Type>::iterator it = p->begin();
 
-		for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it)
-		{
+		for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it)	{
 			aiMesh* mesh = pScene->mMeshes[i];
 			_Type& blubb = *it;
 			blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D));
@@ -171,7 +222,8 @@ class ComputeSpatialSortProcess : public BaseProcess
 	}
 };
 
-// ------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
+// ... and the same again to cleanup the whole stuff
 class DestroySpatialSortProcess : public BaseProcess
 {
 	bool IsActive( unsigned int pFlags) const

+ 0 - 5
include/BoostWorkaround/boost/scoped_array.hpp

@@ -38,11 +38,6 @@ public:
 		return ptr;
 	}
 
-	inline operator T*()
-	{
-		return ptr;
-	}
-
 	inline T* operator-> ()
 	{
 		return ptr;

+ 20 - 0
include/BoostWorkaround/boost/static_assert.hpp

@@ -0,0 +1,20 @@
+
+#ifndef AI_BOOST_STATIC_ASSERT_INCLUDED
+#define AI_BOOST_STATIC_ASSERT_INCLUDED
+
+#ifndef BOOST_STATIC_ASSERT
+
+namespace boost {
+	namespace detail {
+
+		template <bool b>  class static_assertion_failure;
+		template <>        class static_assertion_failure<true> {};
+	}
+}
+
+
+#define BOOST_STATIC_ASSERT(eval) \
+{boost::detail::static_assertion_failure<(eval)> assert_dummy;assert_dummy;}
+
+#endif
+#endif // !! AI_BOOST_STATIC_ASSERT_INCLUDED

+ 11 - 1
include/aiPostProcess.h

@@ -243,8 +243,18 @@ enum aiPostProcessSteps
 	 * coordinates and generates a new, transformed, UV channel for it.
 	 * Most applications won't support UV transformations, so you will
 	 * probably want to specify this step in every case.
+
+	 * todo ... rewrite doc
+	*/
+	aiProcess_TransformUVCoords = 0x80000,
+
+
+	/** This step searches for duplicate meshes and replaces duplicates
+	 *  with references to the first mesh.
+	 *
+	 * todo ... add more doc 
 	*/
-	aiProcess_TransformUVCoords = 0x80000
+	aiProcess_FindInstances = 0x100000
 };
 
 

+ 1 - 0
tools/assimp_view/assimp_view.cpp

@@ -146,6 +146,7 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter)
 		aiProcess_FindInvalidData          | // detect invalid model data, such as invalid normal vectors
 		aiProcess_GenUVCoords              | // convert spherical, cylindrical, box and planar mapping to proper UVs
 		aiProcess_TransformUVCoords        | // preprocess UV transformations (scaling, translation ...)
+		aiProcess_FindInstances            | // search for instanced meshes and remove them by references to one master
 //		aiProcess_PreTransformVertices	   |
 		0);
 

+ 12 - 0
workspaces/vc8/assimp.vcproj

@@ -1254,6 +1254,10 @@
 					RelativePath="..\..\include\BoostWorkaround\boost\scoped_ptr.hpp"
 					>
 				</File>
+				<File
+					RelativePath="..\..\include\BoostWorkaround\boost\static_assert.hpp"
+					>
+				</File>
 				<Filter
 					Name="random"
 					>
@@ -2046,6 +2050,14 @@
 					RelativePath="..\..\code\FindDegenerates.h"
 					>
 				</File>
+				<File
+					RelativePath="..\..\code\FindInstancesProcess.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\code\FindInstancesProcess.h"
+					>
+				</File>
 				<File
 					RelativePath="..\..\code\FindInvalidDataProcess.cpp"
 					>

+ 12 - 0
workspaces/vc9/assimp.vcproj

@@ -1253,6 +1253,10 @@
 					RelativePath="..\..\include\BoostWorkaround\boost\scoped_ptr.hpp"
 					>
 				</File>
+				<File
+					RelativePath="..\..\include\BoostWorkaround\boost\static_assert.hpp"
+					>
+				</File>
 				<Filter
 					Name="random"
 					>
@@ -2045,6 +2049,14 @@
 					RelativePath="..\..\code\FindDegenerates.h"
 					>
 				</File>
+				<File
+					RelativePath="..\..\code\FindInstancesProcess.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\code\FindInstancesProcess.h"
+					>
+				</File>
 				<File
 					RelativePath="..\..\code\FindInvalidDataProcess.cpp"
 					>