| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909 |
- /*
- 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 <vector>
- #include <list>
- #include "OptimizeGraphProcess.h"
- #include "Hash.h"
- #include "../include/aiPostProcess.h"
- #include "../include/aiScene.h"
- #include "../include/aiAssert.h"
- #include "../include/assimp.hpp"
- #include "../include/DefaultLogger.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()
- {
- configRemoveAnimations = AI_OG_REMOVE_ANIMATIONS;
- 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)
- {
- // remove animation nodes?
- configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_REMOVE_ANIMATIONS,
- AI_OG_REMOVE_ANIMATIONS) != 0 ? true : false;
- // join nods with inequal transformations?
- configRemoveAnimations = 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);
- }
- // ------------------------------------------------------------------------------------------------
- aiNode* OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node)
- {
- ai_assert(NULL != node);
- std::vector<aiNode*> out;
- RemoveAnimationNodes(node,out);
- if (out.empty())
- throw new ImportErrorException("OptimizeGraphProcess: no nodes are remaining.");
- if (1 == out.size())
- return out[0];
- aiNode* p = new aiNode();
- p->mName.Set("<dummy_root>");
- p->mNumChildren = (unsigned int)out.size();
- p->mChildren = new aiNode*[p->mNumChildren];
- ::memcpy(p->mChildren,&out[0],p->mNumChildren*sizeof(void*));
- return p;
- }
- // ------------------------------------------------------------------------------------------------
- void OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out)
- {
- ai_assert(NULL != node);
- // if this is an animation node: shift all children on this layer
- if (!node->mNumMeshes)
- {
- unsigned int old = (unsigned int)out.size();
- for (unsigned int i = 0; i < node->mNumChildren;++i)
- {
- RemoveAnimationNodes(node->mChildren[i],out);
- }
- // update the transformations of all shifted childs
- std::vector<aiNode*>::iterator it2 = out.end(),it = out.begin()+old;
- for (; it != it2; ++it)
- (*it)->mTransformation = node->mTransformation * (*it)->mTransformation;
-
- delete[] node->mChildren;node->mChildren = NULL;
- delete node;
- }
- else
- {
- // *this* node remains on this layer, and the children, too
- out.push_back(node);
- std::vector<aiNode*> outNew;
- for (unsigned int i = 0; i < node->mNumChildren;++i)
- {
- RemoveAnimationNodes(node->mChildren[i],outNew);
- }
- if (outNew.size() > node->mNumChildren)
- {
- delete[] node->mChildren;
- node->mChildren = new aiNode*[outNew.size()];
- }
- node->mNumChildren = (unsigned int)outNew.size();
- ::memcpy(node->mChildren,&outNew[0],node->mNumChildren*sizeof(void*));
- }
- }
- // ------------------------------------------------------------------------------------------------
- void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
- {
- ai_assert(NULL != node);
- for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
- {
- aiAnimation* pani = pScene->mAnimations[i];
- for (unsigned int a = 0; a < pani->mNumBones;++a)
- {
- aiBoneAnim* pba = pani->mBones[a];
- if (pba->mBoneName == node->mName)
- {
- // this node is locked
- 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::BuildUniqueBoneList(
- std::vector<aiMesh*>::const_iterator it,
- std::vector<aiMesh*>::const_iterator end,
- std::list<BoneWithHash>& asBones)
- {
- unsigned int iOffset = 0;
- for (; it != end;++it)
- {
- for (unsigned int l = 0; l < (*it)->mNumBones;++l)
- {
- aiBone* p = (*it)->mBones[l];
- uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
- std::list<BoneWithHash>::iterator it2 = asBones.begin();
- std::list<BoneWithHash>::iterator end2 = asBones.end();
- for (;it2 != end2;++it2)
- {
- if ((*it2).first == itml)
- {
- (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
- break;
- }
- }
- if (end2 == it2)
- {
- // need to begin a new bone entry
- asBones.push_back(BoneWithHash());
- BoneWithHash& btz = asBones.back();
- // setup members
- btz.first = itml;
- btz.second = &p->mName;
- btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
- }
- }
- iOffset += (*it)->mNumVertices;
- }
- }
- // ------------------------------------------------------------------------------------------------
- void OptimizeGraphProcess::JoinBones(
- std::vector<aiMesh*>::const_iterator it,
- std::vector<aiMesh*>::const_iterator end,
- aiMesh* out)
- {
- ai_assert(NULL != out);
- // find we need to build an unique list of all bones.
- // we work with hashes to make the comparisons MUCH faster,
- // at least if we have many bones.
- std::list<BoneWithHash> asBones;
- BuildUniqueBoneList(it,end,asBones);
-
- // now create the output bones
- out->mBones = new aiBone*[asBones.size()];
- for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),
- end = asBones.end(); it != end;++it)
- {
- aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
- pc->mName = aiString( *((*it).second ));
- // get an itrator to the end of the list
- std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
- // loop through all bones to be joined for this bone
- for (std::vector< BoneSrcIndex >::const_iterator
- wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
- {
- pc->mNumWeights += (*wmit).first->mNumWeights;
- // NOTE: different offset matrices for bones with equal names
- // are - at the moment - not handled correctly.
- if (wmit != (*it).pSrcBones.begin() &&
- pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
- {
- DefaultLogger::get()->warn("Bones with equal names but different "
- "offset matrices can't be joined at the moment. If this causes "
- "problems, deactivate the OptimizeGraph-Step");
- continue;
- }
- pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
- }
- // allocate the vertex weight array
- aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
- // and copy the final weights - adjust the vertex IDs by the
- // face index offset of the coresponding mesh.
- for (std::vector< BoneSrcIndex >::const_iterator
- wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
- {
- aiBone* pip = (*wmit).first;
- for (unsigned int mp = 0; mp < pip->mNumWeights;++mp)
- {
- aiVertexWeight& vf = aiVertexWeight(pip->mWeights[mp]);
- vf.mVertexId += (*wmit).second;
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- void OptimizeGraphProcess::JoinMeshes(std::vector<aiMesh*>& meshList,
- aiMesh*& out, unsigned int max)
- {
- ai_assert(NULL != out && 0 != max);
- out->mMaterialIndex = meshList[0]->mMaterialIndex;
- // allocate the output mesh
- out = new aiMesh();
- std::vector<aiMesh*>::const_iterator end = meshList.begin()+max;
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- out->mNumVertices += (*it)->mNumVertices;
- out->mNumFaces += (*it)->mNumFaces;
- out->mNumBones += AI_OG_UNMASK((*it)->mNumBones);
- }
- if (out->mNumVertices) // just for safety
- {
- aiVector3D* pv2;
- // copy vertex positions
- if (meshList[0]->HasPositions())
- {
- pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
- pv2 += (*it)->mNumVertices;
- }
- }
- // copy normals
- if (meshList[0]->HasNormals())
- {
- pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
- pv2 += (*it)->mNumVertices;
- }
- }
- // copy tangents and bitangents
- if (meshList[0]->HasTangentsAndBitangents())
- {
- pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
- aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D));
- ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
- pv2 += (*it)->mNumVertices;
- pv2b += (*it)->mNumVertices;
- }
- }
- // copy texture coordinates
- unsigned int n = 0;
- while (meshList[0]->HasTextureCoords(n))
- {
- out->mNumUVComponents[n] = meshList[0]->mNumUVComponents[n];
- pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
- pv2 += (*it)->mNumVertices;
- }
- ++n;
- }
- // copy vertex colors
- n = 0;
- while (meshList[0]->HasVertexColors(n))
- {
- aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
- pv2 += (*it)->mNumVertices;
- }
- ++n;
- }
- }
- if (out->mNumFaces) // just for safety
- {
- // copy faces
- out->mFaces = new aiFace[out->mNumFaces];
- aiFace* pf2 = out->mFaces;
- unsigned int ofs = 0;
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- {
- for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2)
- {
- aiFace& face = (*it)->mFaces[m];
- pf2->mNumIndices = face.mNumIndices;
- pf2->mIndices = face.mIndices;
- if (ofs)
- {
- // add the offset to the vertex
- for (unsigned int q = 0; q < face.mNumIndices; ++q)
- face.mIndices[q] += ofs;
- }
- ofs += (*it)->mNumVertices;
- face.mIndices = NULL;
- }
- }
- }
- // bones - as this is quite lengthy, I moved the code to a separate function
- if (out->mNumBones)JoinBones(meshList.begin(),end,out);
- // delete all source meshes
- for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
- delete *it;
- }
- // ------------------------------------------------------------------------------------------------
- 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 ä = 0; ä < quak->mNumMeshes;++ä)
- {
- aiMesh* mariusIsHot = pScene->mMeshes[quak->mMeshes[ä]];
- 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)
- {
- 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.
- Algorithm:
- 1. Compute hashes for all meshes that we're able to check whether
- two meshes are compatible.
- 2. Remove animation nodes if we have been configured to do so
- 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
- if (configRemoveAnimations)
- pScene->mRootNode = RemoveAnimationNodes(pScene->mRootNode);
- // STEP 3
- else FindLockedNodes(pScene->mRootNode);
- // STEP 4
- FindLockedMeshes(pScene->mRootNode);
- // STEP 5
- ApplyOptimizations(pScene->mRootNode);
- // STEP 6
- BuildOutputMeshList();
- // STEP 7
- UnlockNodes(pScene->mRootNode);
- UnlockMeshes();
- }
|