OptimizeGraphProcess.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909
  1. /*
  2. Open Asset Import Library (ASSIMP)
  3. ----------------------------------------------------------------------
  4. Copyright (c) 2006-2008, ASSIMP Development Team
  5. All rights reserved.
  6. Redistribution and use of this software in source and binary forms,
  7. with or without modification, are permitted provided that the
  8. following conditions are met:
  9. * Redistributions of source code must retain the above
  10. copyright notice, this list of conditions and the
  11. following disclaimer.
  12. * Redistributions in binary form must reproduce the above
  13. copyright notice, this list of conditions and the
  14. following disclaimer in the documentation and/or other
  15. materials provided with the distribution.
  16. * Neither the name of the ASSIMP team, nor the names of its
  17. contributors may be used to endorse or promote products
  18. derived from this software without specific prior
  19. written permission of the ASSIMP Development Team.
  20. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. ----------------------------------------------------------------------
  32. */
  33. /** Implementation of the OptimizeGraphProcess post-processing step*/
  34. #include <vector>
  35. #include <list>
  36. #include "OptimizeGraphProcess.h"
  37. #include "Hash.h"
  38. #include "../include/aiPostProcess.h"
  39. #include "../include/aiScene.h"
  40. #include "../include/aiAssert.h"
  41. #include "../include/assimp.hpp"
  42. #include "../include/DefaultLogger.h"
  43. using namespace Assimp;
  44. // MSB for type unsigned int
  45. #define AI_OG_UINT_MSB (1u<<((sizeof(unsigned int)*8u)-1u))
  46. #define AI_OG_UINT_MSB_2 (AI_OG_UINT_MSB>>1)
  47. // check whether a node/a mesh is locked
  48. #define AI_OG_IS_NODE_LOCKED(nd) (nd->mNumChildren & AI_OG_UINT_MSB)
  49. #define AI_OG_IS_MESH_LOCKED(ms) (ms->mNumBones & AI_OG_UINT_MSB)
  50. // check whether a node has locked meshes in its list
  51. #define AI_OG_HAS_NODE_LOCKED_MESHES(nd) (nd->mNumChildren & AI_OG_UINT_MSB_2)
  52. // unmask the two upper bits of an unsigned int
  53. #define AI_OG_UNMASK(p) (p & (~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2)))
  54. // ------------------------------------------------------------------------------------------------
  55. // Constructor to be privately used by Importer
  56. OptimizeGraphProcess::OptimizeGraphProcess()
  57. {
  58. configRemoveAnimations = AI_OG_REMOVE_ANIMATIONS;
  59. configMinNumFaces = AI_OG_MIN_NUM_FACES;
  60. configJoinInequalTransforms = AI_OG_JOIN_INEQUAL_TRANSFORMS;
  61. }
  62. // ------------------------------------------------------------------------------------------------
  63. // Destructor, private as well
  64. OptimizeGraphProcess::~OptimizeGraphProcess()
  65. {
  66. // nothing to do here
  67. }
  68. // ------------------------------------------------------------------------------------------------
  69. // Returns whether the processing step is present in the given flag field.
  70. bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const
  71. {
  72. return (pFlags & aiProcess_OptimizeGraph) != 0;
  73. }
  74. // ------------------------------------------------------------------------------------------------
  75. // Setup properties of the step
  76. void OptimizeGraphProcess::SetupProperties(const Importer* pImp)
  77. {
  78. // remove animation nodes?
  79. configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_REMOVE_ANIMATIONS,
  80. AI_OG_REMOVE_ANIMATIONS) != 0 ? true : false;
  81. // join nods with inequal transformations?
  82. configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS,
  83. AI_OG_JOIN_INEQUAL_TRANSFORMS) != 0 ? true : false;
  84. // minimum face number per node
  85. configMinNumFaces = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_MIN_NUM_FACES,
  86. AI_OG_MIN_NUM_FACES);
  87. }
  88. // ------------------------------------------------------------------------------------------------
  89. aiNode* OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node)
  90. {
  91. ai_assert(NULL != node);
  92. std::vector<aiNode*> out;
  93. RemoveAnimationNodes(node,out);
  94. if (out.empty())
  95. throw new ImportErrorException("OptimizeGraphProcess: no nodes are remaining.");
  96. if (1 == out.size())
  97. return out[0];
  98. aiNode* p = new aiNode();
  99. p->mName.Set("<dummy_root>");
  100. p->mNumChildren = (unsigned int)out.size();
  101. p->mChildren = new aiNode*[p->mNumChildren];
  102. ::memcpy(p->mChildren,&out[0],p->mNumChildren*sizeof(void*));
  103. return p;
  104. }
  105. // ------------------------------------------------------------------------------------------------
  106. void OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node,std::vector<aiNode*>& out)
  107. {
  108. ai_assert(NULL != node);
  109. // if this is an animation node: shift all children on this layer
  110. if (!node->mNumMeshes)
  111. {
  112. unsigned int old = (unsigned int)out.size();
  113. for (unsigned int i = 0; i < node->mNumChildren;++i)
  114. {
  115. RemoveAnimationNodes(node->mChildren[i],out);
  116. }
  117. // update the transformations of all shifted childs
  118. std::vector<aiNode*>::iterator it2 = out.end(),it = out.begin()+old;
  119. for (; it != it2; ++it)
  120. (*it)->mTransformation = node->mTransformation * (*it)->mTransformation;
  121. delete[] node->mChildren;node->mChildren = NULL;
  122. delete node;
  123. }
  124. else
  125. {
  126. // *this* node remains on this layer, and the children, too
  127. out.push_back(node);
  128. std::vector<aiNode*> outNew;
  129. for (unsigned int i = 0; i < node->mNumChildren;++i)
  130. {
  131. RemoveAnimationNodes(node->mChildren[i],outNew);
  132. }
  133. if (outNew.size() > node->mNumChildren)
  134. {
  135. delete[] node->mChildren;
  136. node->mChildren = new aiNode*[outNew.size()];
  137. }
  138. node->mNumChildren = (unsigned int)outNew.size();
  139. ::memcpy(node->mChildren,&outNew[0],node->mNumChildren*sizeof(void*));
  140. }
  141. }
  142. // ------------------------------------------------------------------------------------------------
  143. void OptimizeGraphProcess::FindLockedNodes(aiNode* node)
  144. {
  145. ai_assert(NULL != node);
  146. for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
  147. {
  148. aiAnimation* pani = pScene->mAnimations[i];
  149. for (unsigned int a = 0; a < pani->mNumBones;++a)
  150. {
  151. aiBoneAnim* pba = pani->mBones[a];
  152. if (pba->mBoneName == node->mName)
  153. {
  154. // this node is locked
  155. node->mNumChildren |= AI_OG_UINT_MSB;
  156. }
  157. }
  158. }
  159. // call all children
  160. for (unsigned int i = 0; i < node->mNumChildren;++i)
  161. FindLockedNodes(node->mChildren[i]);
  162. }
  163. // ------------------------------------------------------------------------------------------------
  164. void OptimizeGraphProcess::FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount)
  165. {
  166. ai_assert(NULL != node && NULL != pRefCount);
  167. for (unsigned int i = 0;i < node->mNumMeshes;++i)
  168. {
  169. unsigned int m = node->mMeshes[i];
  170. if (pRefCount[m].first)
  171. {
  172. // we have already one reference - lock the first node
  173. // that had a referenced to this mesh too if it has only
  174. // one mesh assigned. If there are multiple meshes,
  175. // the others could still be used for optimizations.
  176. if (pRefCount[m].second)
  177. {
  178. pRefCount[m].second->mNumChildren |= (pRefCount[m].second->mNumMeshes <= 1
  179. ? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
  180. pRefCount[m].second = NULL;
  181. }
  182. pScene->mMeshes[m]->mNumBones |= AI_OG_UINT_MSB;
  183. // lock this node
  184. node->mNumChildren |= (node->mNumMeshes <= 1
  185. ? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2);
  186. }
  187. else pRefCount[m].second = node;
  188. ++pRefCount[m].first;
  189. }
  190. // call all children
  191. for (unsigned int i = 0; i < node->mNumChildren;++i)
  192. FindLockedMeshes(node->mChildren[i],pRefCount);
  193. }
  194. // ------------------------------------------------------------------------------------------------
  195. void OptimizeGraphProcess::FindLockedMeshes(aiNode* node)
  196. {
  197. ai_assert(NULL != node);
  198. MeshRefCount* pRefCount = new MeshRefCount[pScene->mNumMeshes];
  199. for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
  200. pRefCount[i] = MeshRefCount();
  201. // execute the algorithm
  202. FindLockedMeshes(node,pRefCount);
  203. delete[] pRefCount;
  204. }
  205. // ------------------------------------------------------------------------------------------------
  206. void OptimizeGraphProcess::UnlockNodes(aiNode* node)
  207. {
  208. ai_assert(NULL != node);
  209. node->mNumChildren &= ~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2);
  210. // call all children
  211. for (unsigned int i = 0; i < node->mNumChildren;++i)
  212. UnlockNodes(node->mChildren[i]);
  213. }
  214. // ------------------------------------------------------------------------------------------------
  215. void OptimizeGraphProcess::UnlockMeshes()
  216. {
  217. for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
  218. pScene->mMeshes[i]->mNumBones &= ~AI_OG_UINT_MSB;
  219. }
  220. // ------------------------------------------------------------------------------------------------
  221. void OptimizeGraphProcess::ComputeMeshHashes()
  222. {
  223. mMeshHashes.resize(pScene->mNumMeshes);
  224. for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
  225. {
  226. unsigned int iRet = 0;
  227. aiMesh* pcMesh = pScene->mMeshes[i];
  228. // normals
  229. if (pcMesh->HasNormals())iRet |= 0x1;
  230. // tangents and bitangents
  231. if (pcMesh->HasTangentsAndBitangents())iRet |= 0x2;
  232. // texture coordinates
  233. unsigned int p = 0;
  234. ai_assert(4 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
  235. while (pcMesh->HasTextureCoords(p))
  236. {
  237. iRet |= (0x100 << p++);
  238. // NOTE: meshes with numUVComp != 3 && != 2 aren't handled correctly here
  239. ai_assert(pcMesh->mNumUVComponents[p] == 3 || pcMesh->mNumUVComponents[p] == 2);
  240. if (3 == pcMesh->mNumUVComponents[p])
  241. iRet |= (0x1000 << p++);
  242. }
  243. // vertex colors
  244. p = 0;
  245. ai_assert(4 >= AI_MAX_NUMBER_OF_COLOR_SETS);
  246. while (pcMesh->HasVertexColors(p))iRet |= (0x10000 << p++);
  247. mMeshHashes[i] = iRet;
  248. // material index -store it in the upper 1 1/2 bytes, so
  249. // are able to encode 2^12 material indices.
  250. iRet |= (pcMesh->mMaterialIndex << 20u);
  251. }
  252. }
  253. // ------------------------------------------------------------------------------------------------
  254. inline unsigned int OptimizeGraphProcess::BinarySearch(NodeIndexList& sortedArray,
  255. unsigned int min, unsigned int& index, unsigned int iStart)
  256. {
  257. unsigned int first = iStart,last = (unsigned int)sortedArray.size()-1;
  258. while (first <= last)
  259. {
  260. unsigned int mid = (first + last) / 2;
  261. unsigned int id = sortedArray[mid].second;
  262. if (min > id)
  263. first = mid + 1;
  264. else if (min <= id)
  265. {
  266. last = mid - 1;
  267. if (!mid || min > sortedArray[last].second)
  268. {
  269. index = sortedArray[last].first;
  270. return mid;
  271. }
  272. }
  273. }
  274. return (unsigned int)sortedArray.size();
  275. }
  276. // ------------------------------------------------------------------------------------------------
  277. void OptimizeGraphProcess::BuildUniqueBoneList(
  278. std::vector<aiMesh*>::const_iterator it,
  279. std::vector<aiMesh*>::const_iterator end,
  280. std::list<BoneWithHash>& asBones)
  281. {
  282. unsigned int iOffset = 0;
  283. for (; it != end;++it)
  284. {
  285. for (unsigned int l = 0; l < (*it)->mNumBones;++l)
  286. {
  287. aiBone* p = (*it)->mBones[l];
  288. uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
  289. std::list<BoneWithHash>::iterator it2 = asBones.begin();
  290. std::list<BoneWithHash>::iterator end2 = asBones.end();
  291. for (;it2 != end2;++it2)
  292. {
  293. if ((*it2).first == itml)
  294. {
  295. (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
  296. break;
  297. }
  298. }
  299. if (end2 == it2)
  300. {
  301. // need to begin a new bone entry
  302. asBones.push_back(BoneWithHash());
  303. BoneWithHash& btz = asBones.back();
  304. // setup members
  305. btz.first = itml;
  306. btz.second = &p->mName;
  307. btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
  308. }
  309. }
  310. iOffset += (*it)->mNumVertices;
  311. }
  312. }
  313. // ------------------------------------------------------------------------------------------------
  314. void OptimizeGraphProcess::JoinBones(
  315. std::vector<aiMesh*>::const_iterator it,
  316. std::vector<aiMesh*>::const_iterator end,
  317. aiMesh* out)
  318. {
  319. ai_assert(NULL != out);
  320. // find we need to build an unique list of all bones.
  321. // we work with hashes to make the comparisons MUCH faster,
  322. // at least if we have many bones.
  323. std::list<BoneWithHash> asBones;
  324. BuildUniqueBoneList(it,end,asBones);
  325. // now create the output bones
  326. out->mBones = new aiBone*[asBones.size()];
  327. for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),
  328. end = asBones.end(); it != end;++it)
  329. {
  330. aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
  331. pc->mName = aiString( *((*it).second ));
  332. // get an itrator to the end of the list
  333. std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
  334. // loop through all bones to be joined for this bone
  335. for (std::vector< BoneSrcIndex >::const_iterator
  336. wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
  337. {
  338. pc->mNumWeights += (*wmit).first->mNumWeights;
  339. // NOTE: different offset matrices for bones with equal names
  340. // are - at the moment - not handled correctly.
  341. if (wmit != (*it).pSrcBones.begin() &&
  342. pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix)
  343. {
  344. DefaultLogger::get()->warn("Bones with equal names but different "
  345. "offset matrices can't be joined at the moment. If this causes "
  346. "problems, deactivate the OptimizeGraph-Step");
  347. continue;
  348. }
  349. pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
  350. }
  351. // allocate the vertex weight array
  352. aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
  353. // and copy the final weights - adjust the vertex IDs by the
  354. // face index offset of the coresponding mesh.
  355. for (std::vector< BoneSrcIndex >::const_iterator
  356. wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit)
  357. {
  358. aiBone* pip = (*wmit).first;
  359. for (unsigned int mp = 0; mp < pip->mNumWeights;++mp)
  360. {
  361. aiVertexWeight& vf = aiVertexWeight(pip->mWeights[mp]);
  362. vf.mVertexId += (*wmit).second;
  363. }
  364. }
  365. }
  366. }
  367. // ------------------------------------------------------------------------------------------------
  368. void OptimizeGraphProcess::JoinMeshes(std::vector<aiMesh*>& meshList,
  369. aiMesh*& out, unsigned int max)
  370. {
  371. ai_assert(NULL != out && 0 != max);
  372. out->mMaterialIndex = meshList[0]->mMaterialIndex;
  373. // allocate the output mesh
  374. out = new aiMesh();
  375. std::vector<aiMesh*>::const_iterator end = meshList.begin()+max;
  376. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  377. {
  378. out->mNumVertices += (*it)->mNumVertices;
  379. out->mNumFaces += (*it)->mNumFaces;
  380. out->mNumBones += AI_OG_UNMASK((*it)->mNumBones);
  381. }
  382. if (out->mNumVertices) // just for safety
  383. {
  384. aiVector3D* pv2;
  385. // copy vertex positions
  386. if (meshList[0]->HasPositions())
  387. {
  388. pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
  389. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  390. {
  391. ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
  392. pv2 += (*it)->mNumVertices;
  393. }
  394. }
  395. // copy normals
  396. if (meshList[0]->HasNormals())
  397. {
  398. pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
  399. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  400. {
  401. ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
  402. pv2 += (*it)->mNumVertices;
  403. }
  404. }
  405. // copy tangents and bitangents
  406. if (meshList[0]->HasTangentsAndBitangents())
  407. {
  408. pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
  409. aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
  410. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  411. {
  412. ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D));
  413. ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
  414. pv2 += (*it)->mNumVertices;
  415. pv2b += (*it)->mNumVertices;
  416. }
  417. }
  418. // copy texture coordinates
  419. unsigned int n = 0;
  420. while (meshList[0]->HasTextureCoords(n))
  421. {
  422. out->mNumUVComponents[n] = meshList[0]->mNumUVComponents[n];
  423. pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
  424. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  425. {
  426. ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
  427. pv2 += (*it)->mNumVertices;
  428. }
  429. ++n;
  430. }
  431. // copy vertex colors
  432. n = 0;
  433. while (meshList[0]->HasVertexColors(n))
  434. {
  435. aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
  436. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  437. {
  438. ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
  439. pv2 += (*it)->mNumVertices;
  440. }
  441. ++n;
  442. }
  443. }
  444. if (out->mNumFaces) // just for safety
  445. {
  446. // copy faces
  447. out->mFaces = new aiFace[out->mNumFaces];
  448. aiFace* pf2 = out->mFaces;
  449. unsigned int ofs = 0;
  450. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  451. {
  452. for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2)
  453. {
  454. aiFace& face = (*it)->mFaces[m];
  455. pf2->mNumIndices = face.mNumIndices;
  456. pf2->mIndices = face.mIndices;
  457. if (ofs)
  458. {
  459. // add the offset to the vertex
  460. for (unsigned int q = 0; q < face.mNumIndices; ++q)
  461. face.mIndices[q] += ofs;
  462. }
  463. ofs += (*it)->mNumVertices;
  464. face.mIndices = NULL;
  465. }
  466. }
  467. }
  468. // bones - as this is quite lengthy, I moved the code to a separate function
  469. if (out->mNumBones)JoinBones(meshList.begin(),end,out);
  470. // delete all source meshes
  471. for (std::vector<aiMesh*>::const_iterator it = meshList.begin(); it != end;++it)
  472. delete *it;
  473. }
  474. // ------------------------------------------------------------------------------------------------
  475. void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode)
  476. {
  477. ai_assert(NULL != pNode);
  478. // find all meshes which are compatible and could therefore be joined.
  479. // we can't join meshes that are locked
  480. std::vector<aiMesh*> apcMeshes(pNode->mNumMeshes);
  481. unsigned int iNumMeshes;
  482. for (unsigned int m = 0, ttt = 0; m < pNode->mNumMeshes;++m)
  483. {
  484. iNumMeshes = 0;
  485. unsigned int nm = pNode->mMeshes[m];
  486. if (0xffffffff == nm || AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nm]))continue;
  487. for (unsigned int q = m+1; q < pNode->mNumMeshes;++q)
  488. {
  489. register unsigned int nq = pNode->mMeshes[q];
  490. // skip locked meshes
  491. if (AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nq]))continue;
  492. // compare the mesh hashes
  493. if (mMeshHashes[nm] == mMeshHashes[nq])
  494. {
  495. apcMeshes[iNumMeshes++] = pScene->mMeshes[nq];
  496. pNode->mMeshes[q] = 0xffffffff;
  497. }
  498. }
  499. aiMesh* out;
  500. if (iNumMeshes > 0)
  501. {
  502. apcMeshes[iNumMeshes++] = pScene->mMeshes[nm];
  503. JoinMeshes(apcMeshes,out,iNumMeshes);
  504. }
  505. else out = pScene->mMeshes[nm];
  506. pNode->mMeshes[ttt++] = (unsigned int)mOutputMeshes.size();
  507. mOutputMeshes.push_back(out);
  508. }
  509. }
  510. // ------------------------------------------------------------------------------------------------
  511. void OptimizeGraphProcess::TransformMeshes(aiNode* quak,aiNode* pNode)
  512. {
  513. for (unsigned int ä = 0; ä < quak->mNumMeshes;++ä)
  514. {
  515. aiMesh* mariusIsHot = pScene->mMeshes[quak->mMeshes[ä]];
  516. aiMatrix4x4 mMatTransform = pNode->mTransformation;
  517. // transformation: first back to the parent's local space,
  518. // later into the local space of the destination child node
  519. mMatTransform.Inverse();
  520. mMatTransform = quak->mTransformation * mMatTransform;
  521. // transform all vertices
  522. for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
  523. mariusIsHot->mVertices[oo] = mMatTransform * mariusIsHot->mVertices[oo];
  524. // transform all normal vectors
  525. if (mariusIsHot->HasNormals())
  526. {
  527. mMatTransform.Inverse().Transpose();
  528. for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo)
  529. mariusIsHot->mNormals[oo] = mMatTransform * mariusIsHot->mNormals[oo];
  530. }
  531. }
  532. }
  533. // ------------------------------------------------------------------------------------------------
  534. void OptimizeGraphProcess::ApplyOptimizations(aiNode* node)
  535. {
  536. ai_assert(NULL != node);
  537. unsigned int iJoinedIndex = 0;
  538. // first: node index; second: number of faces in node
  539. NodeIndexList aiBelowTreshold;
  540. aiBelowTreshold.reserve(node->mNumChildren);
  541. for (unsigned int i = 0; i < node->mNumChildren;++i)
  542. {
  543. aiNode* pChild = node->mChildren[i];
  544. if (AI_OG_IS_NODE_LOCKED(pChild) || !pChild->mNumMeshes)continue;
  545. // find out how many faces this node is referencing
  546. unsigned int iFaceCnt = 0;
  547. for (unsigned int a = 0; a < pChild->mNumMeshes;++a)
  548. iFaceCnt += pScene->mMeshes[pChild->mMeshes[a]]->mNumFaces;
  549. // are we below the treshold?
  550. if (iFaceCnt < configMinNumFaces)
  551. {
  552. aiBelowTreshold.push_back(NodeIndexEntry());
  553. NodeIndexEntry& p = aiBelowTreshold.back();
  554. p.first = i;
  555. p.second = iFaceCnt;
  556. p.pNode = pChild;
  557. }
  558. }
  559. if (!aiBelowTreshold.empty())
  560. {
  561. // some typedefs for the data structures we'll need
  562. typedef std::pair<unsigned int, unsigned int> JoinListEntry;
  563. std::vector<JoinListEntry> aiJoinList(aiBelowTreshold.size());
  564. std::vector<unsigned int> aiTempList(aiBelowTreshold.size());
  565. unsigned int iNumJoins, iNumTemp;
  566. // sort the list by size
  567. std::sort(aiBelowTreshold.begin(),aiBelowTreshold.end());
  568. unsigned int iStart = 0;
  569. for (NodeIndexList::const_iterator it = aiBelowTreshold.begin(),end = aiBelowTreshold.end();
  570. it != end; /*++it */++iStart)
  571. {
  572. aiNode* pNode = node->mChildren[(*it).first];
  573. // get the hash of the mesh
  574. const unsigned int iMeshVFormat = mMeshHashes[pNode->mMeshes[0]];
  575. // we search for a node with more faces than this ... find
  576. // the one that fits best and continue until we've reached
  577. // treshold size.
  578. int iDiff = configMinNumFaces-(*it).second;
  579. for (;;)
  580. {
  581. // do a binary search and start the iteration there
  582. unsigned int index;
  583. unsigned int start = BinarySearch(aiBelowTreshold,iDiff,index,iStart);
  584. if (index == (*it).first)start++;
  585. if (start >= aiBelowTreshold.size())
  586. {
  587. // there is no node with enough faces. take the first
  588. start = 0;
  589. }
  590. // todo: implement algorithm to find the best possible combination ...
  591. iNumTemp = 0;
  592. while( start < aiBelowTreshold.size())
  593. {
  594. // check whether the node has akready been processed before
  595. const NodeIndexEntry& entry = aiBelowTreshold[start];
  596. if (!entry.pNode)continue;
  597. const aiNode* pip = node->mChildren[entry.first];
  598. if (configJoinInequalTransforms )
  599. {
  600. // we need to check whether this node has locked meshes
  601. // in this case we can't add it here - the meshes will
  602. // be transformed from one to another coordinate space
  603. if (!AI_OG_HAS_NODE_LOCKED_MESHES(pip) || pip->mTransformation == pNode->mTransformation)
  604. aiTempList[iNumTemp++] = start;
  605. }
  606. else if (node->mChildren[entry.first]->mTransformation == pNode->mTransformation)
  607. {
  608. aiTempList[iNumTemp++] = start;
  609. break;
  610. }
  611. ++start;
  612. }
  613. if (iNumTemp)
  614. {
  615. // search for a node which has a mesh with
  616. // - the same material index
  617. // - the same vertex layout
  618. unsigned int d = iNumJoins = 0;
  619. for (unsigned int m = 0; m < iNumTemp;++m)
  620. {
  621. register unsigned int mn = aiTempList[m];
  622. aiNode* pip = aiBelowTreshold[mn].pNode;
  623. for (unsigned int tt = 0; tt < pip->mNumMeshes;++tt)
  624. {
  625. register unsigned int mm = pip->mMeshes[tt];
  626. if (mMeshHashes [ mm ] == iMeshVFormat)
  627. {
  628. d = mn;
  629. goto break_out;
  630. }
  631. }
  632. }
  633. break_out:
  634. aiJoinList[iNumJoins++] = JoinListEntry( aiBelowTreshold[d].first, d );
  635. iDiff -= aiBelowTreshold[d].second;
  636. }
  637. // did we reach the target treshold?
  638. if (iDiff <= 0)break;
  639. }
  640. // did we found any nodes to be joined with *this* one?
  641. if (iNumJoins)
  642. {
  643. unsigned int iNumTotalChilds = pNode->mNumChildren;
  644. unsigned int iNumTotalMeshes = pNode->mNumMeshes;
  645. std::vector<JoinListEntry>::const_iterator wend = aiJoinList.begin()+iNumJoins;
  646. // get output array bounds
  647. for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
  648. wit != wend;++wit )
  649. {
  650. aiNode*& quak = node->mChildren[(*wit).first];
  651. iNumTotalChilds += AI_OG_UNMASK( quak->mNumChildren );
  652. iNumTotalMeshes += quak->mNumMeshes;
  653. }
  654. // build the output child list
  655. if (iNumTotalChilds != pNode->mNumChildren)
  656. {
  657. aiNode** ppc = pNode->mChildren;
  658. delete[] pNode->mChildren;
  659. pNode->mChildren = new aiNode*[iNumTotalChilds];
  660. ::memcpy(pNode->mChildren,ppc, sizeof(void*)* AI_OG_UNMASK( pNode->mNumChildren ));
  661. for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
  662. wit != wend;++wit )
  663. {
  664. aiNode*& quak = node->mChildren[(*wit).first];
  665. ::memcpy(pNode->mChildren+pNode->mNumChildren,
  666. quak->mChildren, sizeof(void*)*quak->mNumChildren);
  667. pNode->mNumChildren += AI_OG_UNMASK( quak->mNumChildren );
  668. }
  669. }
  670. // build the output mesh list
  671. unsigned int* ppc = pNode->mMeshes;
  672. delete[] pNode->mMeshes;
  673. pNode->mMeshes = new unsigned int[iNumTotalMeshes];
  674. ::memcpy(pNode->mMeshes,ppc, sizeof(void*)*pNode->mNumMeshes);
  675. for (std::vector<JoinListEntry>::const_iterator wit = aiJoinList.begin();
  676. wit != wend;++wit )
  677. {
  678. aiNode*& quak = node->mChildren[(*wit).first];
  679. ::memcpy(pNode->mMeshes+pNode->mNumMeshes,
  680. quak->mMeshes, sizeof(unsigned int)*quak->mNumMeshes);
  681. // if the node has a transformation matrix that is not equal to ours,
  682. // we'll need to transform all vertices of the mesh into our
  683. // local coordinate space.
  684. if (configJoinInequalTransforms && quak->mTransformation != pNode->mTransformation)
  685. TransformMeshes(quak,pNode);
  686. pNode->mNumMeshes += quak->mNumMeshes;
  687. // remove the joined nodes from all lists.
  688. aiBelowTreshold[(*wit).second].pNode = NULL;
  689. if ((*wit).second == iStart+1)++iStart;
  690. }
  691. // now generate an output name for the joined nodes
  692. if (1 == iNumTotalChilds)
  693. {
  694. pNode->mName.length = ::sprintf( pNode->mName.data, "<Joined_%i_%i>",
  695. iJoinedIndex++,iNumJoins+1);
  696. }
  697. }
  698. // now optimize the meshes in this node
  699. ApplyNodeMeshesOptimization(pNode);
  700. // note - this has been optimized away. The search in the binary
  701. // list starts with iStart, which is incremented each iteration
  702. ++it; // = aiBelowTreshold.erase(it);
  703. }
  704. }
  705. // call all children recursively
  706. for (unsigned int i = 0; i < node->mNumChildren;++i)
  707. ApplyOptimizations(node->mChildren[i]);
  708. }
  709. // ------------------------------------------------------------------------------------------------
  710. void OptimizeGraphProcess::BuildOutputMeshList()
  711. {
  712. // all meshes should have been deleted before if they are
  713. // not contained in the new mesh list
  714. if (pScene->mNumMeshes < mOutputMeshes.size())
  715. {
  716. delete[] pScene->mMeshes;
  717. pScene->mMeshes = new aiMesh*[mOutputMeshes.size()];
  718. }
  719. pScene->mNumMeshes = (unsigned int)mOutputMeshes.size();
  720. ::memcpy(pScene->mMeshes,&mOutputMeshes[0],pScene->mNumMeshes*sizeof(void*));
  721. }
  722. // ------------------------------------------------------------------------------------------------
  723. // Executes the post processing step on the given imported data.
  724. void OptimizeGraphProcess::Execute( aiScene* pScene)
  725. {
  726. this->pScene = pScene;
  727. /*
  728. a) the term "mesh node" stands for a node with numMeshes > 0
  729. b) the term "animation node" stands for a node with numMeshes == 0,
  730. regardless whether the node is referenced by animation channels.
  731. Algorithm:
  732. 1. Compute hashes for all meshes that we're able to check whether
  733. two meshes are compatible.
  734. 2. Remove animation nodes if we have been configured to do so
  735. 3. Find out which nodes may not be moved, so to speak are "locked" - a
  736. locked node will never be joined with neighbors.
  737. - A node lock is indicated by a set MSB in the aiNode::mNumChildren member
  738. 4. Find out which meshes are locked - they are referenced by
  739. more than one node. They will never be joined. Mark all
  740. nodes referencing such a mesh as "locked", too.
  741. - A mesh lock is indicated by a set MSB in the aiMesh::mNumBones member
  742. 5. For each unlocked node count the face numbers of all assigned meshes
  743. - if it is below the pre-defined treshold add the node to a list.
  744. For each node in the list - try to find enough joinable nodes to
  745. have enough faces all together.
  746. Two nodes are joined if:
  747. - none of them is locked
  748. - (optional) their world matrices are identical
  749. - nodes whose meshes share the same material indices are prefered
  750. Two meshes in one node are joined if:
  751. - their material indices are identical
  752. - none of them is locked
  753. - they share the same vertex format
  754. 6. Build the final mesh list
  755. 7. For all meshes and all nodes - remove locks.
  756. */
  757. throw new ImportErrorException("OG step is still undeer development and not yet finished");
  758. // STEP 1
  759. ComputeMeshHashes();
  760. // STEP 2
  761. if (configRemoveAnimations)
  762. pScene->mRootNode = RemoveAnimationNodes(pScene->mRootNode);
  763. // STEP 3
  764. else FindLockedNodes(pScene->mRootNode);
  765. // STEP 4
  766. FindLockedMeshes(pScene->mRootNode);
  767. // STEP 5
  768. ApplyOptimizations(pScene->mRootNode);
  769. // STEP 6
  770. BuildOutputMeshList();
  771. // STEP 7
  772. UnlockNodes(pScene->mRootNode);
  773. UnlockMeshes();
  774. }