OptimizeGraphProcess.cpp 27 KB

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