Removed OptimizeGraph step - actually it isn't really useful, although I got it working very well.

Updated credits.
VC9 workspace updated by hand (no vc9 ...) so be careful.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@300 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2009-01-15 21:08:28 +00:00
parent 6557c28ef7
commit 462bb953c2
12 changed files with 3 additions and 1006 deletions

View File

@ -55,3 +55,6 @@ supplied patches for linux and SCons.
- T. R.
The GUY who performed some of the CSM mocaps.
- Andy Maloney
Contributed fixes for the documentation and the doxygen markup

View File

@ -181,9 +181,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
# include "RemoveRedundantMaterials.h"
#endif
#ifndef AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS
# include "OptimizeGraphProcess.h"
#endif
#ifndef AI_BUILD_NO_FINDINVALIDDATA_PROCESS
# include "FindInvalidDataProcess.h"
#endif
@ -376,10 +373,6 @@ Importer::Importer()
mPostProcessingSteps.push_back( new FindInvalidDataProcess());
#endif
#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
mPostProcessingSteps.push_back( new OptimizeGraphProcess());
#endif
#if (!defined AI_BUILD_NO_FIXINFACINGNORMALS_PROCESS)
mPostProcessingSteps.push_back( new FixInfacingNormalsProcess());
#endif
@ -564,15 +557,6 @@ bool ValidateFlags(unsigned int pFlags)
"aiProcess_GenNormals may not be specified together");
return false;
}
if (pFlags & aiProcess_PreTransformVertices &&
pFlags & aiProcess_OptimizeGraph)
{
DefaultLogger::get()->error("aiProcess_PreTransformVertives and "
"aiProcess_OptimizeGraph may not be specified together");
return false;
}
return true;
}
#endif // ! DEBUG

View File

@ -1,636 +0,0 @@
/*
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.
----------------------------------------------------------------------
*/
/** Implementation of the OptimizeGraphProcess post-processing step*/
#include "AssimpPCH.h"
#include "OptimizeGraphProcess.h"
#include "Hash.h"
using namespace Assimp;
// MSB for type unsigned int
#define AI_OG_UINT_MSB (1u<<((sizeof(unsigned int)*8u)-1u))
#define AI_OG_UINT_MSB_2 (AI_OG_UINT_MSB>>1)
// check whether a node/a mesh is locked
#define AI_OG_IS_NODE_LOCKED(nd) (nd->mNumChildren & AI_OG_UINT_MSB)
#define AI_OG_IS_MESH_LOCKED(ms) (ms->mNumBones & AI_OG_UINT_MSB)
// check whether a node has locked meshes in its list
#define AI_OG_HAS_NODE_LOCKED_MESHES(nd) (nd->mNumChildren & AI_OG_UINT_MSB_2)
// unmask the two upper bits of an unsigned int
#define AI_OG_UNMASK(p) (p & (~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2)))
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
OptimizeGraphProcess::OptimizeGraphProcess()
{
configMinNumFaces = AI_OG_MIN_NUM_FACES;
configJoinInequalTransforms = AI_OG_JOIN_INEQUAL_TRANSFORMS;
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
OptimizeGraphProcess::~OptimizeGraphProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_OptimizeGraph) != 0;
}
// ------------------------------------------------------------------------------------------------
// Setup properties of the step
void OptimizeGraphProcess::SetupProperties(const Importer* pImp)
{
// join nods with inequal transformations?
configJoinInequalTransforms = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS,
AI_OG_JOIN_INEQUAL_TRANSFORMS) != 0 ? true : false;
// minimum face number per node
configMinNumFaces = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_MIN_NUM_FACES,
AI_OG_MIN_NUM_FACES);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
{
ai_assert(NULL != node);
// process animations
for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
{
aiAnimation* pani = pScene->mAnimations[i];
for (unsigned int a = 0; a < pani->mNumChannels;++a)
{
aiNodeAnim* pba = pani->mChannels[a];
if (pba->mNodeName == node->mName)
{
// this node is locked, it is referenced by an animation channel
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
}
// process cameras
for (unsigned int i = 0; i < pScene->mNumCameras;++i)
{
aiCamera* p = pScene->mCameras[i];
if (p->mName == node->mName)
{
// this node is locked, it is referenced by a camera
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
// process lights
for (unsigned int i = 0; i < pScene->mNumLights;++i)
{
aiLight* p = pScene->mLights[i];
if (p->mName == node->mName)
{
// this node is locked, it is referenced by a light
node->mNumChildren |= AI_OG_UINT_MSB;
}
}
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
FindLockedNodes(node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount)
{
ai_assert(NULL != node && NULL != pRefCount);
for (unsigned int i = 0;i < node->mNumMeshes;++i)
{
unsigned int m = node->mMeshes[i];
if (pRefCount[m].first)
{
// we have already one reference - lock the first node
// that had a referenced to this mesh too if it has only
// one mesh assigned. If there are multiple meshes,
// the others could still be used for optimizations.
if (pRefCount[m].second)
{
pRefCount[m].second->mNumChildren |= (pRefCount[m].second->mNumMeshes <= 1
? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
pRefCount[m].second = NULL;
}
pScene->mMeshes[m]->mNumBones |= AI_OG_UINT_MSB;
// lock this node
node->mNumChildren |= (node->mNumMeshes <= 1
? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
}
else pRefCount[m].second = node;
++pRefCount[m].first;
}
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
FindLockedMeshes(node->mChildren[i],pRefCount);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::FindLockedMeshes(aiNode* node)
{
ai_assert(NULL != node);
MeshRefCount* pRefCount = new MeshRefCount[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pRefCount[i] = MeshRefCount();
// execute the algorithm
FindLockedMeshes(node,pRefCount);
delete[] pRefCount;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::UnlockNodes(aiNode* node)
{
ai_assert(NULL != node);
node->mNumChildren &= ~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2);
// call all children
for (unsigned int i = 0; i < node->mNumChildren;++i)
UnlockNodes(node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::UnlockMeshes()
{
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mMeshes[i]->mNumBones &= ~AI_OG_UINT_MSB;
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ComputeMeshHashes()
{
mMeshHashes.resize(pScene->mNumMeshes);
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
unsigned int iRet = 0;
aiMesh* pcMesh = pScene->mMeshes[i];
// normals
if (pcMesh->HasNormals())iRet |= 0x1;
// tangents and bitangents
if (pcMesh->HasTangentsAndBitangents())iRet |= 0x2;
// texture coordinates
unsigned int p = 0;
ai_assert(4 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
while (pcMesh->HasTextureCoords(p))
{
iRet |= (0x100 << p++);
// NOTE: meshes with numUVComp != 3 && != 2 aren't handled correctly here
ai_assert(pcMesh->mNumUVComponents[p] == 3 || pcMesh->mNumUVComponents[p] == 2);
if (3 == pcMesh->mNumUVComponents[p])
iRet |= (0x1000 << p++);
}
// vertex colors
p = 0;
ai_assert(4 >= AI_MAX_NUMBER_OF_COLOR_SETS);
while (pcMesh->HasVertexColors(p))iRet |= (0x10000 << p++);
mMeshHashes[i] = iRet;
// material index -store it in the upper 1 1/2 bytes, so
// are able to encode 2^12 material indices.
iRet |= (pcMesh->mMaterialIndex << 20u);
}
}
// ------------------------------------------------------------------------------------------------
inline unsigned int OptimizeGraphProcess::BinarySearch(NodeIndexList& sortedArray,
unsigned int min, unsigned int& index, unsigned int iStart)
{
unsigned int first = iStart,last = (unsigned int)sortedArray.size()-1;
while (first <= last)
{
unsigned int mid = (first + last) / 2;
unsigned int id = sortedArray[mid].second;
if (min > id)
first = mid + 1;
else if (min <= id)
{
last = mid - 1;
if (!mid || min > sortedArray[last].second)
{
index = sortedArray[last].first;
return mid;
}
}
}
return (unsigned int)sortedArray.size();
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode)
{
ai_assert(NULL != pNode);
// find all meshes which are compatible and could therefore be joined.
// we can't join meshes that are locked
std::vector<aiMesh*> apcMeshes(pNode->mNumMeshes);
unsigned int iNumMeshes;
for (unsigned int m = 0, ttt = 0; m < pNode->mNumMeshes;++m)
{
iNumMeshes = 0;
unsigned int nm = pNode->mMeshes[m];
if (0xffffffff == nm || AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nm]))continue;
for (unsigned int q = m+1; q < pNode->mNumMeshes;++q)
{
register unsigned int nq = pNode->mMeshes[q];
// skip locked meshes
if (AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nq]))continue;
// compare the mesh hashes
if (mMeshHashes[nm] == mMeshHashes[nq])
{
apcMeshes[iNumMeshes++] = pScene->mMeshes[nq];
pNode->mMeshes[q] = 0xffffffff;
}
}
aiMesh* out;
if (iNumMeshes > 0)
{
apcMeshes[iNumMeshes++] = pScene->mMeshes[nm];
// JoinMeshes(apcMeshes,out,iNumMeshes);
}
else out = pScene->mMeshes[nm];
pNode->mMeshes[ttt++] = (unsigned int)mOutputMeshes.size();
mOutputMeshes.push_back(out);
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::TransformMeshes(aiNode* quak,aiNode* pNode)
{
for (unsigned int pl = 0; pl < quak->mNumMeshes;++pl)
{
aiMesh* mariusIsHot = pScene->mMeshes[quak->mMeshes[pl]];
aiMatrix4x4 mMatTransform = pNode->mTransformation;
// transformation: first back to the parent's local space,
// later into the local space of the destination child node
mMatTransform.Inverse();
mMatTransform = quak->mTransformation * mMatTransform;
// transform all vertices
for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
mariusIsHot->mVertices[oo] = mMatTransform * mariusIsHot->mVertices[oo];
// transform all normal vectors
if (mariusIsHot->HasNormals())
{
mMatTransform.Inverse().Transpose();
for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
mariusIsHot->mNormals[oo] = mMatTransform * mariusIsHot->mNormals[oo];
}
}
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::ApplyOptimizations(aiNode* node)
{
ai_assert(NULL != node);
unsigned int iJoinedIndex = 0;
// first: node index; second: number of faces in node
NodeIndexList aiBelowTreshold;
aiBelowTreshold.reserve(node->mNumChildren);
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
aiNode* pChild = node->mChildren[i];
if (AI_OG_IS_NODE_LOCKED(pChild) || !pChild->mNumMeshes)continue;
// find out how many faces this node is referencing
unsigned int iFaceCnt = 0;
for (unsigned int a = 0; a < pChild->mNumMeshes;++a)
iFaceCnt += pScene->mMeshes[pChild->mMeshes[a]]->mNumFaces;
// are we below the treshold?
if (iFaceCnt < configMinNumFaces)
{
aiBelowTreshold.push_back(NodeIndexEntry());
NodeIndexEntry& p = aiBelowTreshold.back();
p.first = i;
p.second = iFaceCnt;
p.pNode = pChild;
}
}
if (!aiBelowTreshold.empty())
{
// some typedefs for the data structures we'll need
typedef std::pair<unsigned int, unsigned int> JoinListEntry;
std::vector<JoinListEntry> aiJoinList(aiBelowTreshold.size());
std::vector<unsigned int> aiTempList(aiBelowTreshold.size());
unsigned int iNumJoins, iNumTemp;
// sort the list by size
std::sort(aiBelowTreshold.begin(),aiBelowTreshold.end());
unsigned int iStart = 0;
for (NodeIndexList::const_iterator it = aiBelowTreshold.begin(),end = aiBelowTreshold.end();
it != end; /*++it */++iStart)
{
aiNode* pNode = node->mChildren[(*it).first];
// get the hash of the mesh
const unsigned int iMeshVFormat = mMeshHashes[pNode->mMeshes[0]];
// we search for a node with more faces than this ... find
// the one that fits best and continue until we've reached
// treshold size.
int iDiff = configMinNumFaces-(*it).second;
for (;;)
{
// do a binary search and start the iteration there
unsigned int index;
unsigned int start = BinarySearch(aiBelowTreshold,iDiff,index,iStart);
if (index == (*it).first)start++;
if (start >= aiBelowTreshold.size())
{
// there is no node with enough faces. take the first
start = 0;
}
// todo: implement algorithm to find the best possible combination ...
iNumTemp = 0;
while( start < aiBelowTreshold.size())
{
// check whether the node has akready been processed before
const NodeIndexEntry& entry = aiBelowTreshold[start];
if (!entry.pNode)continue;
const aiNode* pip = node->mChildren[entry.first];
if (configJoinInequalTransforms )
{
// we need to check whether this node has locked meshes
// in this case we can't add it here - the meshes will
// be transformed from one to another coordinate space
if (!AI_OG_HAS_NODE_LOCKED_MESHES(pip) || pip->mTransformation == pNode->mTransformation)
aiTempList[iNumTemp++] = start;
}
else if (node->mChildren[entry.first]->mTransformation == pNode->mTransformation)
{
aiTempList[iNumTemp++] = start;
break;
}
++start;
}
if (iNumTemp)
{
// search for a node which has a mesh with
// - the same material index
// - the same vertex layout
unsigned int d = iNumJoins = 0;
for (unsigned int m = 0; m < iNumTemp;++m)
{
register unsigned int mn = aiTempList[m];
aiNode* pip = aiBelowTreshold[mn].pNode;
for (unsigned int tt = 0; tt < pip->mNumMeshes;++tt)
{
register unsigned int mm = pip->mMeshes[tt];
if (mMeshHashes [ mm ] == iMeshVFormat)
{
d = mn;
goto break_out;
}
}
}
break_out:
aiJoinList[iNumJoins++] = JoinListEntry( aiBelowTreshold[d].first, d );
iDiff -= aiBelowTreshold[d].second;
}
// did we reach the target treshold?
if (iDiff <= 0)break;
}
// did we found any nodes to be joined with *this* one?
if (iNumJoins)
{
unsigned int iNumTotalChilds = pNode->mNumChildren;
unsigned int iNumTotalMeshes = pNode->mNumMeshes;
std::vector<JoinListEntry>::const_iterator wend = aiJoinList.begin()+iNumJoins;
// get output array bounds
for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
wit != wend;++wit )
{
aiNode*& quak = node->mChildren[(*wit).first];
iNumTotalChilds += AI_OG_UNMASK( quak->mNumChildren );
iNumTotalMeshes += quak->mNumMeshes;
}
// build the output child list
if (iNumTotalChilds != pNode->mNumChildren)
{
aiNode** ppc = pNode->mChildren;
delete[] pNode->mChildren;
pNode->mChildren = new aiNode*[iNumTotalChilds];
::memcpy(pNode->mChildren,ppc, sizeof(void*)* AI_OG_UNMASK( pNode->mNumChildren ));
for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
wit != wend;++wit )
{
aiNode*& quak = node->mChildren[(*wit).first];
::memcpy(pNode->mChildren+pNode->mNumChildren,
quak->mChildren, sizeof(void*)*quak->mNumChildren);
pNode->mNumChildren += AI_OG_UNMASK( quak->mNumChildren );
}
}
// build the output mesh list
unsigned int* ppc = pNode->mMeshes;
delete[] pNode->mMeshes;
pNode->mMeshes = new unsigned int[iNumTotalMeshes];
::memcpy(pNode->mMeshes,ppc, sizeof(void*)*pNode->mNumMeshes);
for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
wit != wend;++wit )
{
aiNode*& quak = node->mChildren[(*wit).first];
::memcpy(pNode->mMeshes+pNode->mNumMeshes,
quak->mMeshes, sizeof(unsigned int)*quak->mNumMeshes);
// if the node has a transformation matrix that is not equal to ours,
// we'll need to transform all vertices of the mesh into our
// local coordinate space.
if (configJoinInequalTransforms && quak->mTransformation != pNode->mTransformation)
TransformMeshes(quak,pNode);
pNode->mNumMeshes += quak->mNumMeshes;
// remove the joined nodes from all lists.
aiBelowTreshold[(*wit).second].pNode = NULL;
if ((*wit).second == iStart+1)++iStart;
}
// now generate an output name for the joined nodes
if (1 == iNumTotalChilds)
{
pNode->mName.length = ::sprintf( pNode->mName.data, "<Joined_%i_%i>",
iJoinedIndex++,iNumJoins+1);
}
}
// now optimize the meshes in this node
ApplyNodeMeshesOptimization(pNode);
// note - this has been optimized away. The search in the binary
// list starts with iStart, which is incremented each iteration
++it; // = aiBelowTreshold.erase(it);
}
}
// call all children recursively
for (unsigned int i = 0; i < node->mNumChildren;++i)
ApplyOptimizations(node->mChildren[i]);
}
// ------------------------------------------------------------------------------------------------
void OptimizeGraphProcess::BuildOutputMeshList()
{
// all meshes should have been deleted before if they are
// not contained in the new mesh list
if (pScene->mNumMeshes < mOutputMeshes.size())
{
delete[] pScene->mMeshes;
pScene->mMeshes = new aiMesh*[mOutputMeshes.size()];
}
pScene->mNumMeshes = (unsigned int)mOutputMeshes.size();
::memcpy(pScene->mMeshes,&mOutputMeshes[0],pScene->mNumMeshes*sizeof(void*));
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void OptimizeGraphProcess::Execute( aiScene* pScene)
{
throw new ImportErrorException("This step is disabled in this beta");
this->pScene = pScene;
/*
a) the term "mesh node" stands for a node with numMeshes > 0
b) the term "animation node" stands for a node with numMeshes == 0,
regardless whether the node is referenced by animation channels,
lights or cameras
Algorithm:
1. Compute hashes for all meshes that we're able to check whether
two meshes are compatible.
3. Find out which nodes may not be moved, so to speak are "locked" - a
locked node will never be joined with neighbors.
- A node lock is indicated by a set MSB in the aiNode::mNumChildren member
4. Find out which meshes are locked - they are referenced by
more than one node. They will never be joined. Mark all
nodes referencing such a mesh as "locked", too.
- A mesh lock is indicated by a set MSB in the aiMesh::mNumBones member
5. For each unlocked node count the face numbers of all assigned meshes
- if it is below the pre-defined treshold add the node to a list.
For each node in the list - try to find enough joinable nodes to
have enough faces all together.
Two nodes are joined if:
- none of them is locked
- (optional) their world matrices are identical
- nodes whose meshes share the same material indices are prefered
Two meshes in one node are joined if:
- their material indices are identical
- none of them is locked
- they share the same vertex format
6. Build the final mesh list
7. For all meshes and all nodes - remove locks.
*/
throw new ImportErrorException("OG step is still undeer development and not yet finished");
// STEP 1
ComputeMeshHashes();
// STEP 2
FindLockedNodes(pScene->mRootNode);
// STEP 3
FindLockedMeshes(pScene->mRootNode);
// STEP 4
ApplyOptimizations(pScene->mRootNode);
// STEP 5
BuildOutputMeshList();
// STEP 6
UnlockNodes(pScene->mRootNode);
UnlockMeshes();
}

View File

@ -1,275 +0,0 @@
/*
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.
----------------------------------------------------------------------
*/
/** Defines a post processing step to refactor the output node graph to
be more compact */
#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC
#define AI_OPTIMIZEGRAPHPROCESS_H_INC
#include "BaseProcess.h"
struct aiMesh;
struct aiNode;
struct aiBone;
class OptimizeGraphProcessTest;
namespace Assimp {
// NOTE: If you change these limits, don't forget to change the
// corresponding values in all Assimp ports
// **********************************************************
// Java: ConfigProperty.java,
// ConfigProperty.OG_MIN_NUM_FACES
// ConfigProperty.JOIN_INEQUAL_TRANSFORMS
// **********************************************************
#if (!defined AI_OG_MAX_DEPTH)
# define AI_OG_MAX_DEPTH 0x4
#endif // !! AI_LMW_MAX_WEIGHTS
#if (!defined AI_OG_MIN_NUM_FACES)
# define AI_OG_MIN_NUM_FACES 0xffffffff
#endif // !! AI_LMW_MAX_WEIGHTS
#if (!defined AI_OG_JOIN_INEQUAL_TRANSFORMS)
# define AI_OG_JOIN_INEQUAL_TRANSFORMS false
#endif // !! AI_LMW_MAX_WEIGHTS
// ---------------------------------------------------------------------------
struct NodeIndexEntry : public std::pair<unsigned int, unsigned int>
{
// binary operator < for use with std::sort
bool operator< (const NodeIndexEntry& other)
{
return second < other.second;
}
// pointer to the original node
aiNode* pNode;
};
typedef std::vector<NodeIndexEntry> NodeIndexList;
// ---------------------------------------------------------------------------
/** This post processing step reformats the output node graph to be more
* compact. There are several options, e.g. maximum hierachy depth or
* minimum mesh size. Some files store every face as a new mesh, so
* some Assimp steps (such as FixInfacingNormals or SmoothNormals) don't
* work properly on these meshes. This step joins such small meshes and
* nodes. Animations are kept during the step.
* @note Use the PretransformVertices step to remove the node graph
* completely (and all animations, too).
*/
class ASSIMP_API OptimizeGraphProcess : public BaseProcess
{
friend class Importer;
friend class ::OptimizeGraphProcessTest;
protected:
/** Constructor to be privately used by Importer */
OptimizeGraphProcess();
/** Destructor, private as well */
~OptimizeGraphProcess();
typedef std::pair< unsigned int, aiNode* > MeshRefCount;
typedef unsigned int MeshHash;
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);
// set the configMinNumfaces property
inline void SetMinNumFaces(unsigned int n)
{
configMinNumFaces = n;
}
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);
// -------------------------------------------------------------------
/** Removes animation nodes from the tree.
* @param node Current node
* @param out Receives a list of replacement nodes for *this* node -
* if *this* node should be kept, it must be added to the list.
*/
void RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out);
// -------------------------------------------------------------------
/** Finds and marks all locked nodes in the tree.
* A node is locked if it is referenced by animations.
* @param node ROot node to start with
* @note A locked node has the MSB set in its mNumChildren member
*/
void FindLockedNodes(aiNode* node);
// -------------------------------------------------------------------
/** Searches for locked meshes. A mesh is locked if it is referenced
* by more than one node in the hierarchy.
* @param node Root node to start with
*/
void FindLockedMeshes(aiNode* node);
void FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount);
// -------------------------------------------------------------------
/** Unlocks all nodes in the output tree.
* @param node Root node to start with
*/
void UnlockNodes(aiNode* node);
// -------------------------------------------------------------------
/** Unlocks all meshes in the output tree.
*/
void UnlockMeshes();
// -------------------------------------------------------------------
/** Apply the final optimization algorithm to the tree.
* See the Execute-method for a detailled description of the algorithm.
* @param node Root node to start with
*/
void ApplyOptimizations(aiNode* node);
// -------------------------------------------------------------------
/** Binary search for the first element that is >= min.
* @param sortedArray Input array
* @param min Treshold
*/
unsigned int BinarySearch(NodeIndexList& sortedArray,
unsigned int min, unsigned int& index, unsigned int iStart);
// -------------------------------------------------------------------
/** Compute stable hashes for all meshes and fill mMeshHashes
* with the results.
*/
void ComputeMeshHashes();
// -------------------------------------------------------------------
/** Optimizes the meshes in a single node aftr the joining process.
* @param pNode Node to be optimized. Childs noded won't be processed
* automatically.
*/
void ApplyNodeMeshesOptimization(aiNode* pNode);
// -------------------------------------------------------------------
/** Build the output mesh list.
*/
void BuildOutputMeshList();
// -------------------------------------------------------------------
/** Transform meshes from one coordinate space into another.
* @param quak Input space. All meshes referenced by this node -
* assuming none of them is locked - are transformed in the
* local coordinate space of pNode
* @param pNode Destination coordinate space
*/
void TransformMeshes(aiNode* quak,aiNode* pNode);
private:
/** Configuration option: specifies the minimum number of faces
a node should have. The steps tries to join meshes with less
vertices that are on the same hierarchy level.
If this value is set to a very large value (e.g. 0xffffffff)
all meshes on the same hierarchy level are joined - if they
aren't animation nodes and if they have the same world matrices
*/
unsigned int configMinNumFaces;
/** Configuration option: specifies whether nodes with inequal
world matrices are joined if they are on the same hierarchy
level and if it seems to make sense.
*/
bool configJoinInequalTransforms;
/** Working data */
aiScene* pScene;
/** List of hash identifiers for all meshes.
The hashes are build from both the meshes vertex format
and the material indices. Bones are not taken into account.
*/
std::vector<MeshHash> mMeshHashes;
/** List of output meshes.
*/
std::vector<aiMesh*> mOutputMeshes;
};
} // end of namespace Assimp
#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC

View File

@ -39,7 +39,6 @@ SOURCES = AssimpPCH.cpp \
ObjFileImporter.cpp \
ObjFileMtlImporter.cpp \
ObjFileParser.cpp \
OptimizeGraphProcess.cpp \
PlyLoader.cpp \
PlyParser.cpp \
PretransformVertices.cpp \

View File

@ -39,7 +39,6 @@ SOURCES = AssimpPCH.cpp \
ObjFileImporter.cpp \
ObjFileMtlImporter.cpp \
ObjFileParser.cpp \
OptimizeGraphProcess.cpp \
PlyLoader.cpp \
PlyParser.cpp \
PretransformVertices.cpp \

View File

@ -195,22 +195,6 @@ enum aiPostProcessSteps
*/
aiProcess_FixInfacingNormals = 0x2000,
/** This step performs some optimizations on the node graph.
*
* It is incompatible to the PreTransformVertices-Step. Some configuration
* options exist, see aiConfig.h for more details.
* Generally, two actions are available:<br>
* 1. Remove animation nodes and data from the scene. This allows other
* steps for further optimizations.<br>
* 2. Combine very small meshes to larger ones. Only if the meshes
* are used by the same node or by nodes on the same hierarchy (with
* equal local transformations). Unlike PreTransformVertices, the
* OptimizeGraph-step doesn't transform vertices from one space
* another (at least with the default configuration).<br>
*
* It is recommended to have this step run with the default configuration.
*/
aiProcess_OptimizeGraph = 0x4000,
/** This step splits meshes with more than one primitive type in
* homogeneous submeshes.

View File

@ -1,2 +0,0 @@
#include "UnitTestPCH.h"

View File

@ -1,35 +0,0 @@
#ifndef TESTOG_H
#define TESTOG_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <aiScene.h>
#include <OptimizeGraphProcess.h>
using namespace std;
using namespace Assimp;
class OptimizeGraphProcessTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (OptimizeGraphProcessTest);
CPPUNIT_TEST (testProcess);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testProcess (void);
private:
OptimizeGraphProcess* piProcess;
aiScene* pcMesh;
};
#endif

View File

@ -1387,14 +1387,6 @@
RelativePath="..\..\test\unit\utMaterialSystem.h"
>
</File>
<File
RelativePath="..\..\test\unit\utOptimizeGraph.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utOptimizeGraph.h"
>
</File>
<File
RelativePath="..\..\test\unit\utPretransformVertices.cpp"
>

View File

@ -2086,14 +2086,6 @@
RelativePath="..\..\code\LimitBoneWeightsProcess.h"
>
</File>
<File
RelativePath="..\..\code\OptimizeGraphProcess.cpp"
>
</File>
<File
RelativePath="..\..\code\OptimizeGraphProcess.h"
>
</File>
<File
RelativePath="..\..\code\PretransformVertices.cpp"
>

View File

@ -2083,14 +2083,6 @@
RelativePath="..\..\code\LimitBoneWeightsProcess.h"
>
</File>
<File
RelativePath="..\..\code\OptimizeGraphProcess.cpp"
>
</File>
<File
RelativePath="..\..\code\OptimizeGraphProcess.h"
>
</File>
<File
RelativePath="..\..\code\PretransformVertices.cpp"
>