Bufixes to the ASE loader, added "RemoveRedundantMaterials"-Step. Rewrite debug output.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@74 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2008-07-30 23:00:25 +00:00
parent 76ebdecd7a
commit 8aa56a62c2
22 changed files with 492 additions and 57 deletions

View File

@ -105,7 +105,7 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
void ASEImporter::InternReadFile( void ASEImporter::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{ {
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rt")); boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file // Check whether we can read from the file
if( file.get() == NULL) if( file.get() == NULL)
@ -846,9 +846,9 @@ void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMesh
} }
++iBase; ++iBase;
} }
p_pcOut->mFaces[q].mIndices[0] = iBase-2; p_pcOut->mFaces[q].mIndices[0] = iBase-3;
p_pcOut->mFaces[q].mIndices[1] = iBase-1; p_pcOut->mFaces[q].mIndices[1] = iBase-2;
p_pcOut->mFaces[q].mIndices[2] = iBase; p_pcOut->mFaces[q].mIndices[2] = iBase-1;
} }
} }
// convert texture coordinates // convert texture coordinates
@ -1071,14 +1071,13 @@ void ASEImporter::BuildMaterialIndices()
ai_assert(NULL != pcScene); ai_assert(NULL != pcScene);
// iterate through all materials and check whether we need them // iterate through all materials and check whether we need them
unsigned int iNum = 0;
for (unsigned int iMat = 0; iMat < this->mParser->m_vMaterials.size();++iMat) for (unsigned int iMat = 0; iMat < this->mParser->m_vMaterials.size();++iMat)
{ {
if (this->mParser->m_vMaterials[iMat].bNeed) if (this->mParser->m_vMaterials[iMat].bNeed)
{ {
// convert it to the aiMaterial layout // convert it to the aiMaterial layout
this->ConvertMaterial(this->mParser->m_vMaterials[iMat]); this->ConvertMaterial(this->mParser->m_vMaterials[iMat]);
iNum++; ++pcScene->mNumMaterials;
} }
for (unsigned int iSubMat = 0; iSubMat < this->mParser->m_vMaterials[ for (unsigned int iSubMat = 0; iSubMat < this->mParser->m_vMaterials[
iMat].avSubMaterials.size();++iSubMat) iMat].avSubMaterials.size();++iSubMat)
@ -1087,17 +1086,16 @@ void ASEImporter::BuildMaterialIndices()
{ {
// convert it to the aiMaterial layout // convert it to the aiMaterial layout
this->ConvertMaterial(this->mParser->m_vMaterials[iMat].avSubMaterials[iSubMat]); this->ConvertMaterial(this->mParser->m_vMaterials[iMat].avSubMaterials[iSubMat]);
iNum++; ++pcScene->mNumMaterials;
} }
} }
} }
// allocate the output material array // allocate the output material array
pcScene->mNumMaterials = iNum;
pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials];
Dot3DS::Material** pcIntMaterials = new Dot3DS::Material*[pcScene->mNumMaterials]; Dot3DS::Material** pcIntMaterials = new Dot3DS::Material*[pcScene->mNumMaterials];
iNum = 0; unsigned int iNum = 0;
for (unsigned int iMat = 0; iMat < this->mParser->m_vMaterials.size();++iMat) for (unsigned int iMat = 0; iMat < this->mParser->m_vMaterials.size();++iMat)
{ {
if (this->mParser->m_vMaterials[iMat].bNeed) if (this->mParser->m_vMaterials[iMat].bNeed)

View File

@ -83,7 +83,11 @@ using namespace Assimp::ASE;
{ \ { \
return; \ return; \
} \ } \
else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; \ if(IsLineEnd(*this->m_szFile) && !bLastWasEndLine) \
{ \
++this->iLineNumber; \
bLastWasEndLine = true; \
} else bLastWasEndLine = false; \
++this->m_szFile; ++this->m_szFile;
#define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \ #define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \
@ -102,7 +106,11 @@ using namespace Assimp::ASE;
this->LogError("Encountered unexpected EOL while parsing a " msg \ this->LogError("Encountered unexpected EOL while parsing a " msg \
" chunk (Level " level ")"); \ " chunk (Level " level ")"); \
} \ } \
else if(IsLineEnd(*this->m_szFile))++this->iLineNumber; \ if(IsLineEnd(*this->m_szFile) && !bLastWasEndLine) \
{ \
++this->iLineNumber; \
bLastWasEndLine = true; \
} else bLastWasEndLine = false; \
++this->m_szFile; ++this->m_szFile;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -120,6 +128,7 @@ Parser::Parser (const char* szFile)
this->iLastFrame = 0; this->iLastFrame = 0;
this->iFrameSpeed = 30; // use 30 as default value for this property this->iFrameSpeed = 30; // use 30 as default value for this property
this->iTicksPerFrame = 1; // use 1 as default value for this property this->iTicksPerFrame = 1; // use 1 as default value for this property
this->bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Parser::LogWarning(const char* szWarn) void Parser::LogWarning(const char* szWarn)
@ -177,7 +186,12 @@ bool Parser::SkipToNextToken()
char me = *this->m_szFile; char me = *this->m_szFile;
// increase the line number counter if necessary // increase the line number counter if necessary
if (IsLineEnd(me))++this->iLineNumber; if (IsLineEnd(me) && !bLastWasEndLine)
{
++this->iLineNumber;
bLastWasEndLine = true;
}
else bLastWasEndLine = false;
if ('*' == me || '}' == me || '{' == me)return true; if ('*' == me || '}' == me || '{' == me)return true;
else if ('\0' == me)return false; else if ('\0' == me)return false;

View File

@ -550,6 +550,9 @@ public:
//! Ticks per frame //! Ticks per frame
unsigned int iTicksPerFrame; unsigned int iTicksPerFrame;
//! true if the last character read was an end-line character
bool bLastWasEndLine;
}; };

View File

@ -130,6 +130,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#if (!defined AI_BUILD_NO_IMPROVECACHELOCALITY_PROCESS) #if (!defined AI_BUILD_NO_IMPROVECACHELOCALITY_PROCESS)
# include "ImproveCacheLocality.h" # include "ImproveCacheLocality.h"
#endif #endif
#if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
# include "RemoveRedundantMaterials.h"
#endif
using namespace Assimp; using namespace Assimp;
@ -142,7 +146,8 @@ Importer::Importer() :
{ {
// allocate a default IO handler // allocate a default IO handler
mIOHandler = new DefaultIOSystem; mIOHandler = new DefaultIOSystem;
mIsDefaultHandler = true; mIsDefaultHandler = true;
bExtraVerbose = false; // disable extra verbose mode by default
// add an instance of each worker class here // add an instance of each worker class here
#if (!defined AI_BUILD_NO_X_IMPORTER) #if (!defined AI_BUILD_NO_X_IMPORTER)
@ -182,7 +187,10 @@ Importer::Importer() :
// add an instance of each post processing step here in the order // add an instance of each post processing step here in the order
// of sequence it is executed // of sequence it is executed
#if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS) #if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS)
mPostProcessingSteps.push_back( new ValidateDSProcess()); mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first
#endif
#if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
#endif #endif
#if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS) #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS)
mPostProcessingSteps.push_back( new TriangulateProcess()); mPostProcessingSteps.push_back( new TriangulateProcess());
@ -328,6 +336,14 @@ const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags
// if successful, apply all active post processing steps to the imported data // if successful, apply all active post processing steps to the imported data
if( mScene) if( mScene)
{ {
if (bExtraVerbose)
{
pFlags |= aiProcess_ValidateDataStructure;
// use the MSB to tell the ValidateDS-Step that e're in extra verbose mode
// TODO: temporary solution, clean up later
mScene->mFlags |= 0x80000000;
}
for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++)
{ {
BaseProcess* process = mPostProcessingSteps[a]; BaseProcess* process = mPostProcessingSteps[a];
@ -336,7 +352,21 @@ const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags
process->ExecuteOnScene( this ); process->ExecuteOnScene( this );
} }
if( !mScene)break; // error string has already been set ... if( !mScene)break; // error string has already been set ...
// if the extra verbose mode is active execute the
// VaidateDataStructureStep again after each step
if (bExtraVerbose && a)
{
DefaultLogger::get()->debug("Extra verbose: revalidating data structures");
((ValidateDSProcess*)mPostProcessingSteps[0])->ExecuteOnScene (this);
if( !mScene)
{
DefaultLogger::get()->error("Extra verbose: failed to revalidate data structures");
break; // error string has already been set ...
}
}
} }
if (bExtraVerbose)mScene->mFlags &= ~0x80000000;
} }
// if failed, extract the error string // if failed, extract the error string

View File

@ -63,6 +63,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() ImproveCacheLocalityProcess::ImproveCacheLocalityProcess()
@ -93,14 +97,14 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{ {
this->ProcessMesh( pScene->mMeshes[a]); this->ProcessMesh( pScene->mMeshes[a],a);
} }
DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. "); DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. ");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Improves the cache coherency of a specific mesh // Improves the cache coherency of a specific mesh
void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh) void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
{ {
ai_assert(NULL != pMesh); ai_assert(NULL != pMesh);
@ -162,12 +166,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh)
} }
} }
delete[] piFIFOStack; delete[] piFIFOStack;
float fACMR = (float)iCacheMisses / pMesh->mNumFaces; float fACMR = (float)iCacheMisses / pMesh->mNumFaces;
char szBuff[16];
sprintf(szBuff,"%f",fACMR);
DefaultLogger::get()->debug("Input ACMR: " + std::string(szBuff));
// first we need to build a vertex-triangle adjacency list // first we need to build a vertex-triangle adjacency list
VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true); VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true);
@ -347,11 +346,15 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh)
} }
} }
} }
if (!DefaultLogger::isNullLogger())
{
char szBuff[128]; // should be sufficiently large in every case
float fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
fACMR = (float)iCacheMisses / pMesh->mNumFaces; sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
sprintf(szBuff,"%f",fACMR); ((fACMR - fACMR2) / fACMR) * 100.f);
DefaultLogger::get()->debug("Output ACMR: " + std::string(szBuff)); DefaultLogger::get()->info(szBuff);
}
// sort the output index buffer back to the input array // sort the output index buffer back to the input array
piCSIter = piIBOutput; piCSIter = piIBOutput;

View File

@ -89,8 +89,9 @@ protected:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Executes the postprocessing step on the given mesh /** Executes the postprocessing step on the given mesh
* @param pMesh The mesh to process. * @param pMesh The mesh to process.
* @param meshNum Index of the mesh to process
*/ */
void ProcessMesh( aiMesh* pMesh); void ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
//! Configuration parameter: specifies the size of the cache to //! Configuration parameter: specifies the size of the cache to

View File

@ -53,6 +53,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
JoinVerticesProcess::JoinVerticesProcess() JoinVerticesProcess::JoinVerticesProcess()
@ -80,22 +84,44 @@ void JoinVerticesProcess::Execute( aiScene* pScene)
{ {
DefaultLogger::get()->debug("JoinVerticesProcess begin"); DefaultLogger::get()->debug("JoinVerticesProcess begin");
bool bHas = false; // get the total number of vertices BEFORE the step is executed
int iNumOldVertices = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{ {
if( this->ProcessMesh( pScene->mMeshes[a])) iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
bHas = true; }
// execute the step
int iNumVertices = 0;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
{
iNumVertices += this->ProcessMesh( pScene->mMeshes[a],a);
}
// if logging is active, print detailled statistics
if (!DefaultLogger::isNullLogger())
{
if (iNumOldVertices == iNumVertices)DefaultLogger::get()->debug("JoinVerticesProcess finished ");
else
{
char szBuff[128]; // should be sufficiently large in every case
sprintf(szBuff,"JoinVerticesProcess finished | Verts in: %i out: %i | ~%.1f%%",
iNumOldVertices,
iNumVertices,
((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f);
DefaultLogger::get()->info(szBuff);
}
} }
if (bHas)DefaultLogger::get()->info("JoinVerticesProcess finished. Found vertices to join");
else DefaultLogger::get()->debug("JoinVerticesProcess finished. There was nothing to do.");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unites identical vertices in the given mesh // Unites identical vertices in the given mesh
bool JoinVerticesProcess::ProcessMesh( aiMesh* pMesh) int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
{ {
// helper structure to hold all the data a single vertex can possibly have // helper structure to hold all the data a single vertex can possibly have
typedef struct Vertex vertex; typedef struct Vertex vertex;
if (!pMesh->HasPositions() || !pMesh->HasFaces())
return 0;
struct Vertex struct Vertex
{ {
@ -216,6 +242,17 @@ bool JoinVerticesProcess::ProcessMesh( aiMesh* pMesh)
} }
} }
if (!DefaultLogger::isNullLogger())
{
char szBuff[128]; // should be sufficiently large in every case
sprintf(szBuff,"Mesh %i | Verts in: %i out: %i | ~%.1f%%",
meshIndex,
pMesh->mNumVertices,
uniqueVertices.size(),
((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f);
DefaultLogger::get()->info(szBuff);
}
// replace vertex data with the unique data sets // replace vertex data with the unique data sets
pMesh->mNumVertices = (unsigned int)uniqueVertices.size(); pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
// Position // Position
@ -310,5 +347,5 @@ bool JoinVerticesProcess::ProcessMesh( aiMesh* pMesh)
bone->mWeights = new aiVertexWeight[bone->mNumWeights]; bone->mWeights = new aiVertexWeight[bone->mNumWeights];
memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
} }
return (iOldVerts != pMesh->mNumVertices); return pMesh->mNumVertices;
} }

View File

@ -90,8 +90,9 @@ protected:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Unites identical vertices in the given mesh. /** Unites identical vertices in the given mesh.
* @param pMesh The mesh to process. * @param pMesh The mesh to process.
* @param meshIndex Index of the mesh to process
*/ */
bool ProcessMesh( aiMesh* pMesh); int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Little helper function to calculate the quadratic difference /** Little helper function to calculate the quadratic difference

View File

@ -47,6 +47,67 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// hashing function taken from
// http://www.azillionmonkeys.com/qed/hash.html
// (incremental version of the hashing function)
// (stdint.h should have been been included here)
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
// ------------------------------------------------------------------------------------------------
uint32_t SuperFastHash (const char * data, int len, uint32_t hash = 0) {
uint32_t tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += get16bits (data);
tmp = (get16bits (data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof (uint16_t);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += get16bits (data);
hash ^= hash << 16;
hash ^= data[sizeof (uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += get16bits (data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
aiReturn aiGetMaterialProperty(const aiMaterial* pMat, aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
const char* pKey, const char* pKey,
@ -230,13 +291,30 @@ aiReturn aiGetMaterialString(const aiMaterial* pMat,
return AI_FAILURE; return AI_FAILURE;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
uint32_t MaterialHelper::ComputeHash()
{
uint32_t hash = 1503; // magic start value, choosen to be my birthday :-)
for (unsigned int i = 0; i < this->mNumProperties;++i)
{
aiMaterialProperty* prop;
// NOTE: We need to exclude the material name from the hash
if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey->data,AI_MATKEY_NAME))
{
hash = SuperFastHash(prop->mKey->data,prop->mKey->length,hash);
hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
}
}
return hash;
}
// ------------------------------------------------------------------------------------------------
aiReturn MaterialHelper::RemoveProperty (const char* pKey) aiReturn MaterialHelper::RemoveProperty (const char* pKey)
{ {
ai_assert(NULL != pKey); ai_assert(NULL != pKey);
for (unsigned int i = 0; i < this->mNumProperties;++i) for (unsigned int i = 0; i < this->mNumProperties;++i)
{ {
if (NULL != this->mProperties[i]) if (this->mProperties[i]) // just for safety
{ {
if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey->data, pKey )) if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey->data, pKey ))
{ {
@ -272,7 +350,7 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
unsigned int iOutIndex = 0xFFFFFFFF; unsigned int iOutIndex = 0xFFFFFFFF;
for (unsigned int i = 0; i < this->mNumProperties;++i) for (unsigned int i = 0; i < this->mNumProperties;++i)
{ {
if (NULL != this->mProperties[i]) if (this->mProperties[i])
{ {
if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey->data, pKey )) if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey->data, pKey ))
{ {
@ -304,8 +382,7 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
return AI_SUCCESS; return AI_SUCCESS;
} }
// resize the array ... allocate // resize the array ... allocate storage for 5 other properties
// storage for 5 other properties
if (this->mNumProperties == this->mNumAllocated) if (this->mNumProperties == this->mNumAllocated)
{ {
unsigned int iOld = this->mNumAllocated; unsigned int iOld = this->mNumAllocated;

View File

@ -104,6 +104,17 @@ public:
*/ */
aiReturn RemoveProperty (const char* pKey); aiReturn RemoveProperty (const char* pKey);
// -------------------------------------------------------------------
/** Computes a hash (hopefully unique) from all material properties
* The hash value must be updated after material properties have
* been changed.
*
* \return Unique hash
*/
uint32_t ComputeHash();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Copy the property list of a material /** Copy the property list of a material
* \param pcDest Destination material * \param pcDest Destination material

View File

@ -0,0 +1,155 @@
/*
---------------------------------------------------------------------------
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 Implementation of the "RemoveRedundantMaterials" post processing step
*/
// internal headers
#include "RemoveRedundantMaterials.h"
#include "MaterialSystem.h"
// public ASSIMP headers
#include "../include/DefaultLogger.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
using namespace Assimp;
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
RemoveRedundantMatsProcess::~RemoveRedundantMatsProcess()
{
// nothing to do here
}
// -------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
}
// -------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
{
DefaultLogger::get()->debug("RemoveRedundantMatsProcess begin");
unsigned int iCnt = 0;
if (pScene->mNumMaterials)
{
// TODO: reimplement this algorithm to work in-place
unsigned int* aiMappingTable = new unsigned int[pScene->mNumMaterials];
unsigned int iNewNum = 0;
// iterate through all materials and calculate a hash for them
// store all hashes in a list and so a quick search whether
// we do already have a specific hash. This allows us to
// determine which materials are identical.
uint32_t* aiHashes;
aiHashes = new uint32_t[pScene->mNumMaterials];
for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
{
uint32_t me = aiHashes[i] = ((MaterialHelper*)pScene->mMaterials[i])->ComputeHash();
for (unsigned int a = 0; a < i;++a)
{
if (me == aiHashes[a])
{
++iCnt;
me = 0;
aiMappingTable[i] = aiMappingTable[a];
delete pScene->mMaterials[i];
break;
}
}
if (me)
{
aiMappingTable[i] = iNewNum++;
}
}
if (iCnt)
{
// build an output material list
aiMaterial** ppcMaterials = new aiMaterial*[iNewNum];
::memset(ppcMaterials,0,sizeof(void*)*iNewNum);
for (unsigned int p = 0; p < pScene->mNumMaterials;++p)
{
// generate new names for all modified materials
const unsigned int idx = aiMappingTable[p];
if (!ppcMaterials[idx])
{
aiString sz;
sz.length = ::sprintf(sz.data,"aiMaterial #%i",p);
ppcMaterials[idx] = pScene->mMaterials[p];
((MaterialHelper*)pScene->mMaterials[p])->AddProperty(&sz,AI_MATKEY_NAME);
}
}
// update all material indices
for (unsigned int p = 0; p < pScene->mNumMeshes;++p)
{
aiMesh* mesh = pScene->mMeshes[p];
mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
}
// delete the old material list
delete[] pScene->mMaterials;
pScene->mMaterials = ppcMaterials;
pScene->mNumMaterials = iNewNum;
}
// delete temporary storage
delete[] aiHashes;
delete[] aiMappingTable;
}
if (!iCnt)DefaultLogger::get()->debug("RemoveRedundantMatsProcess finished ");
else
{
char szBuffer[128]; // should be sufficiently large
::sprintf(szBuffer,"RemoveRedundantMatsProcess finished. Found %i redundant materials",iCnt);
DefaultLogger::get()->info(szBuffer);
}
}

View File

@ -0,0 +1,85 @@
/*
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 Defines a post processing step to remove redundant materials */
#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC
#define AI_REMOVEREDUNDANTMATERIALS_H_INC
#include "BaseProcess.h"
#include "../include/aiMesh.h"
class RemoveRedundantMatsTest;
namespace Assimp
{
// ---------------------------------------------------------------------------
/** RemoveRedundantMatsProcess: Class to remove redundant materials
*/
class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
{
friend class Importer;
friend class ::RemoveRedundantMatsTest; // grant the unit test full access to us
protected:
/** Constructor to be privately used by Importer */
RemoveRedundantMatsProcess();
/** Destructor, private as well */
~RemoveRedundantMatsProcess();
public:
// -------------------------------------------------------------------
/** Returns whether the processing step is present in the given flag field.
* @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;
// -------------------------------------------------------------------
/** 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);
};
}; // end of namespace Assimp
#endif // !!AI_REMOVEREDUNDANTMATERIALS_H_INC

View File

@ -293,7 +293,10 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
{ {
this->ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); this->ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
} }
if (abRefList[face.mIndices[a]]) // the MSB flag is temporarily used by the extra verbose
// mode to tell us that the JoinVerticesProcess might have
// been executed already.
if ( !(this->mScene->mFlags & 0x80000000 ) && abRefList[face.mIndices[a]])
{ {
this->ReportError("aiMesh::mVertices[%i] is referenced twice - second " this->ReportError("aiMesh::mVertices[%i] is referenced twice - second "
"time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);

View File

@ -154,7 +154,15 @@ enum aiPostProcessSteps
* basing on the algorithm described in this paper: * basing on the algorithm described in this paper:
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
*/ */
aiProcess_ImproveCacheLocality = 0x1600 aiProcess_ImproveCacheLocality = 0x800,
/** Searches for redundant materials and removes them.
*
* This is especially useful in combination with the PretransformVertices
* and OptimizeGraph steps. Both steps join small meshes, but they
* can't do that if two meshes have different materials.
*/
aiProcess_RemoveRedundantMaterials = 0x1000,
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -198,6 +198,16 @@ public:
inline const aiScene* GetScene() inline const aiScene* GetScene()
{return this->mScene;} {return this->mScene;}
// -------------------------------------------------------------------
/** Enables the "extra verbose" mode. In this mode the data
* structure is validated after each post-process step to make sure
* all steps behave consequently in the same manner when modifying
* data structures.
*/
inline void SetExtraVerbose(bool bDo)
{this->bExtraVerbose = bDo;}
private: private:
/** Empty copy constructor. */ /** Empty copy constructor. */
@ -222,6 +232,9 @@ protected:
/** The error description, if there was one. */ /** The error description, if there was one. */
std::string mErrorString; std::string mErrorString;
/** Used for testing */
bool bExtraVerbose;
}; };
} // End of namespace Assimp } // End of namespace Assimp

View File

@ -133,7 +133,7 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter)
aiProcess_GenSmoothNormals | // generate smooth normal vectors if not existing aiProcess_GenSmoothNormals | // generate smooth normal vectors if not existing
aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space
aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes
aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality); // validate the output data structure aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials); // validate the output data structure
// get the end time of zje operation, calculate delta t // get the end time of zje operation, calculate delta t
double fEnd = (double)timeGetTime(); double fEnd = (double)timeGetTime();

View File

@ -675,14 +675,6 @@
RelativePath="..\..\test\unit\Main.cpp" RelativePath="..\..\test\unit\Main.cpp"
> >
</File> </File>
<File
RelativePath="..\..\test\unit\ut3DSLoader.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utASELoader.cpp"
>
</File>
<File <File
RelativePath="..\..\test\unit\utFixInfacingNormals.cpp" RelativePath="..\..\test\unit\utFixInfacingNormals.cpp"
> >
@ -711,14 +703,6 @@
RelativePath="..\..\test\unit\utMaterialSystem.cpp" RelativePath="..\..\test\unit\utMaterialSystem.cpp"
> >
</File> </File>
<File
RelativePath="..\..\test\unit\utMDLLoader.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utOBJLoader.cpp"
>
</File>
<File <File
RelativePath="..\..\test\unit\utPretransformVertices.cpp" RelativePath="..\..\test\unit\utPretransformVertices.cpp"
> >

View File

@ -654,6 +654,10 @@
RelativePath="..\..\include\assimp.hpp" RelativePath="..\..\include\assimp.hpp"
> >
</File> </File>
<File
RelativePath="..\..\include\DefaultLogger.h"
>
</File>
<File <File
RelativePath="..\..\include\IOStream.h" RelativePath="..\..\include\IOStream.h"
> >
@ -762,6 +766,10 @@
RelativePath="..\..\code\RemoveComments.h" RelativePath="..\..\code\RemoveComments.h"
> >
</File> </File>
<File
RelativePath="..\..\code\RemoveRedundantMaterials.h"
>
</File>
<File <File
RelativePath="..\..\code\SpatialSort.h" RelativePath="..\..\code\SpatialSort.h"
> >
@ -1090,6 +1098,10 @@
RelativePath="..\..\code\RemoveComments.cpp" RelativePath="..\..\code\RemoveComments.cpp"
> >
</File> </File>
<File
RelativePath="..\..\code\RemoveRedundantMaterials.cpp"
>
</File>
<File <File
RelativePath="..\..\code\SpatialSort.cpp" RelativePath="..\..\code\SpatialSort.cpp"
> >