Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

815 lines
23 KiB

  1. //=========== Copyright � Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Mesh simplification routines.
  4. //
  5. //===========================================================================//
  6. #include "mathlib/vector.h"
  7. #include "simplify.h"
  8. #include "tier1/utlpriorityqueue.h"
  9. #include "mathlib/cholesky.h"
  10. #include "tier1/utlhash.h"
  11. #include "tier0/vprof.h"
  12. #include "memdbgon.h"
  13. // quick vprof wrappers
  14. static void Vprof_MarkFrame_IfEnabled()
  15. {
  16. #if VPROF_LEVEL > 0
  17. g_VProfCurrentProfile.MarkFrame();
  18. #endif
  19. }
  20. static void Vprof_Start_IfEnabled()
  21. {
  22. #if VPROF_LEVEL > 0
  23. g_VProfCurrentProfile.Reset();
  24. g_VProfCurrentProfile.ResetPeaks();
  25. g_VProfCurrentProfile.Start();
  26. #endif
  27. }
  28. static void Vprof_Report_IfEnabled()
  29. {
  30. #if VPROF_LEVEL > 0
  31. g_VProfCurrentProfile.Stop();
  32. g_VProfCurrentProfile.OutputReport( VPRT_FULL & ~VPRT_HIERARCHY, NULL );
  33. Msg("VPROF Trace Complete\n");
  34. #endif
  35. }
  36. // This is a shared edge and its corresponding error metric
  37. class CQEMEdge
  38. {
  39. public:
  40. CQEMEdge() { Init(0,0); }
  41. CQEMEdge( int nV0, int nV1 ) { Init(nV0, nV1); }
  42. void Init( int nV0, int nV1 )
  43. {
  44. m_bCollapsed = 0;
  45. m_bNonManifold = 0;
  46. m_flCurrentError = 0.0f;
  47. m_nBestIndex = 0;
  48. m_nReferences = 0;
  49. m_nVert[0] = nV0;
  50. m_nVert[1] = nV1;
  51. }
  52. void InitVerts( int nV0, int nV1 )
  53. {
  54. m_nVert[0] = MIN(nV0, nV1);
  55. m_nVert[1] = MAX(nV0, nV1);
  56. }
  57. inline void MarkCollapsed() { m_bCollapsed = true; }
  58. inline bool IsCollapsed() { return m_bCollapsed; }
  59. void UpdateError( const Vector &v0, const Vector &v1, const CQuadricError *pVertError )
  60. {
  61. m_error = pVertError[m_nVert[0]] + pVertError[m_nVert[1]];
  62. m_flCurrentError = m_error.ComputeError(v0);
  63. float err1 = m_error.ComputeError(v1);
  64. m_nBestIndex = 0;
  65. if ( err1 < m_flCurrentError )
  66. {
  67. m_vOptimal = v1;
  68. m_nBestIndex = 1;
  69. m_flCurrentError = err1;
  70. m_flInterp = 1.0f;
  71. }
  72. else
  73. {
  74. m_vOptimal = v0;
  75. m_flInterp = 0.0f;
  76. }
  77. Vector vTest = m_error.SolveForMinimumError();
  78. float flError = m_error.ComputeError( vTest );
  79. if ( flError < m_flCurrentError )
  80. {
  81. Vector vEdge = v1 - v0;
  82. float flLen = VectorNormalize(vEdge);
  83. float flDist0 = (vTest - v0).Length();
  84. float flDist1 = (vTest - v1).Length();
  85. if ( flDist0 < flLen || flDist1 < flLen )
  86. {
  87. m_vOptimal = vTest;
  88. m_flCurrentError = flError;
  89. Vector vTmp = m_vOptimal - v0;
  90. if ( flLen > 0.0f )
  91. {
  92. m_flInterp = (1.0f / flLen) * DotProduct( vTmp, vEdge );
  93. m_flInterp = clamp( m_flInterp, 0.0f, 1.0f );
  94. }
  95. }
  96. }
  97. m_flCurrentError = MAX(0.0f, m_flCurrentError);
  98. }
  99. int GetVertIndex( int nVert ) { return m_nVert[0] == nVert ? 0 : 1; }
  100. CQuadricError m_error;
  101. Vector m_vOptimal;
  102. float m_flCurrentError;
  103. float m_flInterp;
  104. int m_nVert[2];
  105. int m_nReferences;
  106. short m_nBestIndex;
  107. bool m_bCollapsed;
  108. bool m_bNonManifold;
  109. };
  110. // We maintain a sorted queue of edges to collapse. The queue stores a copy of the error and links back to an edge
  111. // as edges collapse, adjacent edges are updated. Rather than searching the queue for their current errors we simply
  112. // insert additional records. So the copy of the error is convenient for sorting the queue and also determining if
  113. // this is the most up to date record for a given edge
  114. struct edge_queue_entry_t
  115. {
  116. float m_flError;
  117. int m_nEdgeIndex;
  118. };
  119. // This is the sorted queue of edges to collapse. It includes some invalid records
  120. // This implements the comparison function for sorting
  121. class CEdgeQueue : public CUtlPriorityQueue<edge_queue_entry_t>
  122. {
  123. public:
  124. static bool IsLowerPriority( const edge_queue_entry_t &node1, const edge_queue_entry_t &node2 )
  125. {
  126. // edges with higher error are lower priority
  127. return ( node1.m_flError > node2.m_flError ) ? true : false;
  128. }
  129. CEdgeQueue( int nInitSize = 0 ) : CUtlPriorityQueue<edge_queue_entry_t>( 0, nInitSize, IsLowerPriority ) {}
  130. };
  131. // This is the adjacency and error metric storage class for a mesh
  132. // Each vertex stores a list of triangles (v0,v1,v2). v0 is the vertex storing the list, the list stores the other two
  133. struct vertex_triangle_t
  134. {
  135. uint32 nV1;
  136. uint32 nV2;
  137. };
  138. class CVertVisit
  139. {
  140. public:
  141. CUtlVector<vertex_triangle_t> m_triangles;
  142. };
  143. struct edge_hash_t
  144. {
  145. CQEMEdge *m_pSharedEdge;
  146. inline bool operator==( const edge_hash_t& src ) const { return src.m_nV0 == m_nV0 && src.m_nV1 == m_nV1; }
  147. uint32 m_nV0;
  148. uint32 m_nV1;
  149. };
  150. class CUniqueVertexList : public CUtlVectorFixedGrowable<uint32, 32>
  151. {
  152. public:
  153. void AddIfUnique( uint32 nVertex )
  154. {
  155. if ( Find( nVertex ) == -1 )
  156. {
  157. AddToTail( nVertex );
  158. }
  159. }
  160. };
  161. class CMeshVisit
  162. {
  163. public:
  164. void BuildFromMesh( const CMesh &input );
  165. int FindMinErrorEdge();
  166. void CollapseEdge( int nCollapse );
  167. void RemapEdge( int nVertexRemove, int nVertexConnect, int nVertexKeep );
  168. void ComputeVertListError( float flOpenEdgePenalty, float flMinArea, float flMaxArea, const mesh_simplifyweights_t *pWeights );
  169. inline void UpdateEdgeError( CQEMEdge *pEdge )
  170. {
  171. pEdge->UpdateError( GetVertexPosition(pEdge->m_nVert[0]), GetVertexPosition(pEdge->m_nVert[1]), m_errorVert.Base() );
  172. edge_queue_entry_t entry;
  173. entry.m_flError = pEdge->m_flCurrentError;
  174. entry.m_nEdgeIndex = pEdge - m_edgeList.Base();
  175. m_edgeQueue.Insert( entry );
  176. }
  177. void Get1Ring( CUniqueVertexList &list, uint32 nVertex );
  178. int CountSharedVerts( int nVert0, int nVert1 );
  179. bool IsValidCollapse( int nMinEdge );
  180. bool IsValidCollapseVertex( int nVertCheck, int nVertOpposite, const Vector &vReplacePos );
  181. int CountTotalUsedVerts();
  182. UtlHashFastHandle_t FindEdge( int nV0, int nV1 )
  183. {
  184. edge_hash_t tmp;
  185. tmp.m_nV0 = MIN(nV0, nV1);
  186. tmp.m_nV1 = MAX(nV0, nV1);
  187. uint nHashKey = VertHashKey(MIN(nV0, nV1), MAX(nV0, nV1));
  188. tmp.m_pSharedEdge = NULL;
  189. return m_edgeHash.Find( nHashKey, tmp );
  190. }
  191. bool IsOpenEdge( int nV0, int nV1 )
  192. {
  193. UtlHashFastHandle_t edgeHashIndex = FindEdge( nV0, nV1 );
  194. if ( m_edgeHash.InvalidHandle() != edgeHashIndex )
  195. {
  196. return m_edgeHash.Element(edgeHashIndex).m_pSharedEdge->m_nReferences < 2 ? true : false;
  197. }
  198. return false;
  199. }
  200. // Copies out the mesh in its current state
  201. void WriteMeshIndexList( CUtlVector<uint32> &indexOut );
  202. inline float *GetVertex( int nIndex ) { return m_pVertexBase + nIndex * m_nVertexStrideFloats; }
  203. inline Vector &GetVertexPosition(int nIndex) { return *(Vector *)GetVertex(nIndex); }
  204. CUtlVector<CQEMEdge> m_edgeList;
  205. CUtlScalarHash<edge_hash_t> m_edgeHash;
  206. CUtlVector<CQuadricError> m_errorVert;
  207. CUtlVector<CVertVisit> m_vertList;
  208. CEdgeQueue m_edgeQueue;
  209. CUtlVector<float> m_vertData;
  210. float *m_pVertexBase;
  211. int m_nVertexStrideFloats;
  212. int m_nInputVertCount;
  213. int m_nTriangleCount;
  214. int m_nCollapseIndex;
  215. float m_flIntegrationPenalty;
  216. };
  217. // Copies out the mesh in its current state
  218. void CMeshVisit::WriteMeshIndexList( CUtlVector<uint32> &indexOut )
  219. {
  220. uint32 nCurrentVertexCount = m_vertList.Count();
  221. int nTotalTrianglesRemaining = 0;
  222. for ( uint32 i = 0; i < nCurrentVertexCount; i++ )
  223. {
  224. nTotalTrianglesRemaining += m_vertList[i].m_triangles.Count();
  225. }
  226. // each triangle must be referenced 3 times, once at each vertex
  227. Assert( (nTotalTrianglesRemaining % 3) == 0 );
  228. indexOut.SetCount( nTotalTrianglesRemaining );
  229. int nWriteIndex = 0;
  230. for ( uint32 i = 0; i < nCurrentVertexCount; i++ )
  231. {
  232. for ( int j = 0; j < m_vertList[i].m_triangles.Count(); j++ )
  233. {
  234. vertex_triangle_t &tri = m_vertList[i].m_triangles[j];
  235. // only write each triangle once, skip the other two defs.
  236. // do this by only writing if v0 is the min vert index
  237. if ( tri.nV1 < i || tri.nV2 < i )
  238. continue;
  239. indexOut[nWriteIndex++] = i;
  240. indexOut[nWriteIndex++] = tri.nV1;
  241. indexOut[nWriteIndex++] = tri.nV2;
  242. }
  243. }
  244. }
  245. // Remaps one of the verts on an edge
  246. void CMeshVisit::RemapEdge( int nVertexRemove, int nVertexConnect, int nVertexKeep )
  247. {
  248. UtlHashFastHandle_t edgeHashIndex = FindEdge( nVertexRemove, nVertexConnect );
  249. if ( m_edgeHash.InvalidHandle() == edgeHashIndex )
  250. return;
  251. edge_hash_t tmp = m_edgeHash.Element( edgeHashIndex );
  252. CQEMEdge *pEdge = tmp.m_pSharedEdge;
  253. bool bNeedsUpdate = false;
  254. for ( int i = 0; i < 2; i++ )
  255. {
  256. if ( pEdge->m_nVert[i] == nVertexRemove )
  257. {
  258. pEdge->m_nVert[i] = nVertexKeep;
  259. bNeedsUpdate = true;
  260. }
  261. }
  262. if ( bNeedsUpdate )
  263. {
  264. m_edgeHash.Remove( edgeHashIndex );
  265. if ( pEdge->m_nVert[0] == pEdge->m_nVert[1] )
  266. {
  267. pEdge->MarkCollapsed();
  268. }
  269. else
  270. {
  271. UpdateEdgeError(pEdge);
  272. tmp.m_nV0 = pEdge->m_nVert[0];
  273. tmp.m_nV1 = pEdge->m_nVert[1];
  274. if ( tmp.m_nV0 > tmp.m_nV1 ) // swap so min is in m_nV0
  275. {
  276. tmp.m_nV0 = tmp.m_nV1;
  277. tmp.m_nV1 = pEdge->m_nVert[0];
  278. }
  279. uint nHashKey = VertHashKey(tmp.m_nV0, tmp.m_nV1);
  280. if ( m_edgeHash.Find( nHashKey, tmp ) != m_edgeHash.InvalidHandle() )
  281. {
  282. // another edge with these indices exists in the table, mark as collapsed
  283. pEdge->MarkCollapsed();
  284. }
  285. else
  286. {
  287. m_edgeHash.Insert( nHashKey, tmp );
  288. }
  289. }
  290. }
  291. }
  292. void CMeshVisit::Get1Ring( CUniqueVertexList &list, uint32 nVertex )
  293. {
  294. int nTriCount = m_vertList[nVertex].m_triangles.Count();
  295. for ( int i = 0; i < nTriCount; i++ )
  296. {
  297. const vertex_triangle_t &tri = m_vertList[nVertex].m_triangles[i];
  298. list.AddIfUnique( tri.nV1 );
  299. list.AddIfUnique( tri.nV2 );
  300. }
  301. }
  302. // Counts the number of adjacent verts shared by a pair of verts
  303. // This is used to prevent creating shark fin topologies
  304. int CMeshVisit::CountSharedVerts( int nVert0, int nVert1 )
  305. {
  306. CUniqueVertexList vertIndex0;
  307. CUniqueVertexList vertIndex1;
  308. Get1Ring( vertIndex0, nVert0 );
  309. Get1Ring( vertIndex1, nVert1 );
  310. int nSharedCount = 0;
  311. for ( int i = 0; i < vertIndex1.Count(); i++ )
  312. {
  313. if ( vertIndex0.Find(vertIndex1[i]) != -1 )
  314. {
  315. nSharedCount++;
  316. }
  317. }
  318. return nSharedCount;
  319. }
  320. // Heuristics for avoiding collapsing edges that will generate bad topology
  321. bool CMeshVisit::IsValidCollapseVertex( int nVertCheck, int nVertOpposite, const Vector &vReplacePos )
  322. {
  323. Vector vOld = GetVertexPosition(nVertCheck);
  324. int nTriCount = m_vertList[nVertCheck].m_triangles.Count();
  325. for ( int i = 0; i < nTriCount; i++ )
  326. {
  327. const vertex_triangle_t &tri = m_vertList[nVertCheck].m_triangles[i];
  328. int nV1 = tri.nV1;
  329. int nV2 = tri.nV2;
  330. // this triangle has the collapsing edge, skip it
  331. if ( nV1 == nVertOpposite || nV2 == nVertOpposite )
  332. continue;
  333. Vector v1 = GetVertexPosition(nV1);
  334. Vector v2 = GetVertexPosition(nV2);
  335. Vector vEdge0 = v1 - vOld;
  336. Vector vEdge1 = v2 - vOld;
  337. Vector vNormal = CrossProduct( vEdge1, vEdge0 );
  338. Vector vNewEdge0 = v1 - vReplacePos;
  339. Vector vNewEdge1 = v2 - vReplacePos;
  340. Vector vNewNormal = CrossProduct( vNewEdge1, vNewEdge0 );
  341. float flDot = DotProduct( vNewNormal, vNormal );
  342. // If the collapse will flip the face, avoid it
  343. if ( flDot < 0.0f )
  344. return false;
  345. }
  346. return true;
  347. }
  348. bool CMeshVisit::IsValidCollapse( int nMinEdge )
  349. {
  350. VPROF("IsValidcollapse");
  351. if ( nMinEdge < 0 )
  352. return true;
  353. if ( m_edgeList[nMinEdge].m_bNonManifold )
  354. return false;
  355. int nVert0 = m_edgeList[nMinEdge].m_nVert[0];
  356. int nVert1 = m_edgeList[nMinEdge].m_nVert[1];
  357. // This constraint should keep shark-fin like geometries from forming
  358. if ( CountSharedVerts( nVert0, nVert1 ) > 2 )
  359. return false;
  360. Vector vOptimal = m_edgeList[nMinEdge].m_vOptimal;
  361. return IsValidCollapseVertex( nVert0, nVert1, vOptimal ) && IsValidCollapseVertex( nVert1, nVert0, vOptimal );
  362. }
  363. void CMeshVisit::CollapseEdge( int nCollapse )
  364. {
  365. VPROF("CollapseEdge");
  366. m_edgeList[nCollapse].MarkCollapsed();
  367. Vector vOptimal = m_edgeList[nCollapse].m_vOptimal;
  368. // get the vert being removed
  369. Assert(nCollapse < m_edgeList.Count() );
  370. int nV0 = m_edgeList[nCollapse].m_nVert[0];
  371. int nV1 = m_edgeList[nCollapse].m_nVert[1];
  372. Assert( nV0 < m_vertList.Count() );
  373. Assert( nV1 < m_vertList.Count() );
  374. float flInterp = m_edgeList[nCollapse].m_flInterp;
  375. uint32 nVertexKeep = nV0;
  376. uint32 nVertexRemove = nV1;
  377. if ( m_edgeList[nCollapse].m_nBestIndex == 1 )
  378. {
  379. nVertexKeep = m_edgeList[nCollapse].m_nVert[1];
  380. nVertexRemove = m_edgeList[nCollapse].m_nVert[0];
  381. }
  382. // propagate the error
  383. m_errorVert[nVertexKeep] += m_errorVert[nVertexRemove];
  384. m_errorVert[nVertexKeep] *= m_flIntegrationPenalty;
  385. CUniqueVertexList vertIndex;
  386. Get1Ring( vertIndex, nVertexRemove );
  387. // @TODO: Need to copy triangles over if we allow merging unconnected pairs
  388. // Assert that this pair is connected
  389. // Assert( vertIndex.Find(nVertexKeep) != -1 );
  390. int nTrianglesRemoved = 0;
  391. CUtlVectorFixedGrowable<uint32, 32> removeList;
  392. for ( int i = 0; i < vertIndex.Count(); i++ )
  393. {
  394. uint32 nVertex = vertIndex[i];
  395. for ( int j = 0; j < m_vertList[nVertex].m_triangles.Count(); j++ )
  396. {
  397. vertex_triangle_t &tri = m_vertList[nVertex].m_triangles[j];
  398. if ( tri.nV1 != nVertexRemove && tri.nV2 != nVertexRemove )
  399. continue;
  400. if ( tri.nV1 == nVertexRemove )
  401. {
  402. tri.nV1 = nVertexKeep;
  403. }
  404. if ( tri.nV2 == nVertexRemove )
  405. {
  406. tri.nV2 = nVertexKeep;
  407. }
  408. if ( tri.nV1 == tri.nV2 || tri.nV1 == nVertex || tri.nV2 == nVertex )
  409. {
  410. removeList.AddToTail(j);
  411. }
  412. }
  413. nTrianglesRemoved += removeList.Count();
  414. for ( int j = removeList.Count(); --j >= 0; )
  415. {
  416. m_vertList[nVertex].m_triangles.FastRemove(removeList[j]);
  417. }
  418. removeList.RemoveAll();
  419. }
  420. for ( int i = 0; i < vertIndex.Count(); i++ )
  421. {
  422. RemapEdge( nVertexRemove, vertIndex[i], nVertexKeep );
  423. }
  424. int nRemoveCount = 0;
  425. for ( int j = 0; j < m_vertList[nVertexRemove].m_triangles.Count(); j++ )
  426. {
  427. vertex_triangle_t &tri = m_vertList[nVertexRemove].m_triangles[j];
  428. if ( tri.nV1 == nVertexKeep || tri.nV2 == nVertexKeep )
  429. {
  430. nRemoveCount++;
  431. continue;
  432. }
  433. m_vertList[nVertexKeep].m_triangles.AddToTail( tri );
  434. }
  435. nTrianglesRemoved += nRemoveCount;
  436. // These triangles are all invalid now
  437. m_vertList[nVertexRemove].m_triangles.RemoveAll();
  438. Assert( (nTrianglesRemoved % 3) == 0 );
  439. // each triangle has 3 copies in the list (one at each vert) so the number of real triangles removed
  440. // is 1/3rd of the number removed from the vertex lists
  441. m_nTriangleCount -= (nTrianglesRemoved / 3);
  442. LerpVertex( GetVertex(nVertexKeep), GetVertex(nV0), GetVertex(nV1), flInterp, m_nVertexStrideFloats );
  443. m_nCollapseIndex++;
  444. }
  445. int CMeshVisit::CountTotalUsedVerts()
  446. {
  447. int nVertCount = m_vertList.Count();
  448. int nMaxVertexIndex = m_vertList.Count();
  449. CUtlVector<int> used;
  450. used.SetCount( nMaxVertexIndex + 1 );
  451. used.FillWithValue( 0 );
  452. int nUsedCount = 0;
  453. for ( int i = 0; i < nVertCount; i++ )
  454. {
  455. int nTriCount = m_vertList[i].m_triangles.Count();
  456. if ( nTriCount )
  457. {
  458. if ( !used[i] )
  459. {
  460. used[i] = 1;
  461. nUsedCount++;
  462. }
  463. }
  464. for ( int j = 0; j < nTriCount; j++ )
  465. {
  466. if ( !used[m_vertList[i].m_triangles[j].nV1] )
  467. {
  468. used[m_vertList[i].m_triangles[j].nV1] = 1;
  469. nUsedCount++;
  470. }
  471. if ( !used[m_vertList[i].m_triangles[j].nV2] )
  472. {
  473. used[m_vertList[i].m_triangles[j].nV2] = 1;
  474. nUsedCount++;
  475. }
  476. }
  477. }
  478. return nUsedCount;
  479. }
  480. int CountUsedVerts( const uint32 *pIndexList, int nIndexCount, int nVertexCount )
  481. {
  482. CUtlVector<uint8> used;
  483. used.SetCount(nVertexCount);
  484. for ( int i = 0; i < nVertexCount; i++ )
  485. {
  486. used[i] = 0;
  487. }
  488. int nUsedCount = 0;
  489. for ( int i = 0; i < nIndexCount; i++ )
  490. {
  491. int nIndex = pIndexList[i];
  492. if ( !used[nIndex] )
  493. {
  494. used[nIndex] = 1;
  495. nUsedCount++;
  496. }
  497. }
  498. return nUsedCount;
  499. }
  500. void CMeshVisit::BuildFromMesh( const CMesh &input )
  501. {
  502. VPROF("InitFromSimpleMesh");
  503. // NOTE: This assumes that position is the FIRST float3 in the buffer
  504. int nPosOffset = input.FindFirstAttributeOffset( VERTEX_ELEMENT_POSITION );
  505. if ( nPosOffset != 0 )
  506. {
  507. return;
  508. }
  509. int nInputVertCount = input.m_nVertexCount;
  510. int nInputIndexCount = input.m_nIndexCount;
  511. int nInputTriangleCount = nInputIndexCount / 3;
  512. m_nTriangleCount = nInputTriangleCount;
  513. m_edgeHash.Init( nInputIndexCount * 2 );
  514. // reserve space for vertex tables
  515. m_vertList.SetCount( nInputVertCount );
  516. for ( int i = 0; i < nInputVertCount; i++ )
  517. {
  518. m_vertList[i].m_triangles.EnsureCapacity( 8 );
  519. }
  520. m_edgeList.EnsureCapacity( nInputTriangleCount * 3 * 2 );
  521. // check for degenerate triangles in debug builds
  522. #if _DEBUG
  523. for ( int i = 0; i < nInputIndexCount; i += 3 )
  524. {
  525. if ( input.m_pIndices[i+0] == input.m_pIndices[i+1] || input.m_pIndices[i+0] == input.m_pIndices[i+2] || input.m_pIndices[i+1] == input.m_pIndices[i+2] )
  526. {
  527. // Found a degenerate triangle
  528. // use CleanMesh() to remove degenerates if you aren't sure if the mesh contains degenerates
  529. // The simplification code does not tolerate degenerate triangles
  530. Assert(0);
  531. }
  532. }
  533. #endif
  534. for ( int i = 0; i < nInputIndexCount; i += 3 )
  535. {
  536. for ( int j = 0; j < 3; j++ )
  537. {
  538. int nV0 = input.m_pIndices[i+j];
  539. int nNext = (j+1)%3;
  540. int nV1 = input.m_pIndices[i+nNext];
  541. edge_hash_t tmp;
  542. tmp.m_nV0 = MIN(nV0, nV1);
  543. tmp.m_nV1 = MAX(nV0, nV1);
  544. uint nHashKey = VertHashKey(MIN(nV0, nV1), MAX(nV0, nV1));
  545. tmp.m_pSharedEdge = NULL;
  546. UtlHashFastHandle_t edgeHashIndex = m_edgeHash.Find( nHashKey, tmp );
  547. // new edge, initialize it
  548. if ( m_edgeHash.InvalidHandle() == edgeHashIndex )
  549. {
  550. int nEdgeIndex = m_edgeList.AddToTail();
  551. tmp.m_pSharedEdge = &m_edgeList[nEdgeIndex];
  552. tmp.m_pSharedEdge->InitVerts( tmp.m_nV0, tmp.m_nV1 );
  553. edgeHashIndex = m_edgeHash.Insert( nHashKey, tmp );
  554. }
  555. edge_hash_t &edgeHash = m_edgeHash.Element( edgeHashIndex );
  556. edgeHash.m_pSharedEdge->m_nReferences++;
  557. vertex_triangle_t tri;
  558. tri.nV1 = nV1;
  559. tri.nV2 = input.m_pIndices[ i + ((j+2)%3) ];
  560. m_vertList[nV0].m_triangles.AddToTail( tri );
  561. }
  562. }
  563. m_nInputVertCount = input.m_nVertexCount;
  564. m_vertData.SetCount( input.m_nVertexCount * input.m_nVertexStrideFloats );
  565. m_pVertexBase = m_vertData.Base();
  566. m_nVertexStrideFloats = input.m_nVertexStrideFloats;
  567. V_memcpy( m_pVertexBase, input.GetVertex(0), input.GetTotalVertexSizeInBytes() );
  568. m_nCollapseIndex = 0;
  569. }
  570. // Removes elements from the queue until a valid one is found, returns that index or -1 indicating there are no valid edges
  571. int CMeshVisit::FindMinErrorEdge()
  572. {
  573. VPROF("FindMinErrorEdge");
  574. int nBest = -1;
  575. while ( true )
  576. {
  577. if ( !m_edgeQueue.Count() )
  578. return -1;
  579. edge_queue_entry_t entry = m_edgeQueue.ElementAtHead();
  580. m_edgeQueue.RemoveAtHead();
  581. CQEMEdge &edge = m_edgeList[entry.m_nEdgeIndex];
  582. if ( edge.m_flCurrentError == entry.m_flError && !edge.IsCollapsed() )
  583. {
  584. if ( IsValidCollapse( entry.m_nEdgeIndex ) )
  585. {
  586. nBest = entry.m_nEdgeIndex;
  587. break;
  588. }
  589. }
  590. }
  591. return nBest;
  592. }
  593. void CMeshVisit::ComputeVertListError( float flOpenEdgePenalty, float flMinArea, float flMaxArea, const mesh_simplifyweights_t *pWeights )
  594. {
  595. m_errorVert.SetCount( m_nInputVertCount );
  596. if ( pWeights )
  597. {
  598. // can we use all of the weights? If not, don't use any of them
  599. if ( pWeights->m_nVertexCount != m_nInputVertCount )
  600. {
  601. pWeights = NULL;
  602. }
  603. }
  604. for ( int i = 0; i < m_nInputVertCount; i++ )
  605. {
  606. m_errorVert[i].SetToZero();
  607. for ( int j = 0; j < m_vertList[i].m_triangles.Count(); j++ )
  608. {
  609. int nV0 = i;
  610. int nV1 = m_vertList[i].m_triangles[j].nV1;
  611. int nV2 = m_vertList[i].m_triangles[j].nV2;
  612. if ( IsOpenEdge( nV0, nV1 ) )
  613. {
  614. Vector v0 = GetVertexPosition(nV0);
  615. Vector v1 = GetVertexPosition(nV1);
  616. Vector v2 = GetVertexPosition(nV2);
  617. Vector vNormal = CrossProduct( v2 - v0, v1 - v0 );
  618. Vector vPlaneNormal = CrossProduct( v1 - v0, vNormal );
  619. float flArea = 0.5f * vPlaneNormal.NormalizeInPlace() * flOpenEdgePenalty;
  620. CQuadricError errorThisEdge;
  621. errorThisEdge.InitFromPlane( vPlaneNormal, -DotProduct(vPlaneNormal, v0), flArea );
  622. m_errorVert[i] += errorThisEdge;
  623. continue;
  624. }
  625. CQuadricError errorThisTri;
  626. errorThisTri.InitFromTriangle( GetVertexPosition(nV0), GetVertexPosition(nV1), GetVertexPosition(nV2), flMinArea );
  627. m_errorVert[i] += errorThisTri;
  628. }
  629. if ( pWeights && pWeights->m_pVertexWeights )
  630. {
  631. m_errorVert[i] *= pWeights->m_pVertexWeights[i];
  632. }
  633. }
  634. int nInputEdgeCount = m_edgeList.Count();
  635. for ( int i = 0; i < nInputEdgeCount; i++ )
  636. {
  637. UpdateEdgeError( &m_edgeList[i] );
  638. }
  639. }
  640. void GetOpenEdges( CUtlVector<Vector> &list, const CMesh &input )
  641. {
  642. CMeshVisit visit;
  643. visit.BuildFromMesh( input );
  644. for ( int i = 0; i < visit.m_edgeList.Count(); i++ )
  645. {
  646. if ( visit.m_edgeList[i].m_nReferences < 2 )
  647. {
  648. list.AddToTail( visit.GetVertexPosition( visit.m_edgeList[i].m_nVert[0] ) );
  649. list.AddToTail( visit.GetVertexPosition( visit.m_edgeList[i].m_nVert[1] ) );
  650. }
  651. }
  652. }
  653. void SimplifyMeshQEM2( CMesh &meshOut, const CMesh &input, const mesh_simplifyparams_t &params, const mesh_simplifyweights_t *pWeights )
  654. {
  655. VPROF("Simplify");
  656. CMeshVisit visit;
  657. visit.BuildFromMesh( input );
  658. CUtlVector<CQuadricError> errorEdge;
  659. int nInputVertCount = input.m_nVertexCount;
  660. int nInputEdgeCount = visit.m_edgeList.Count();
  661. errorEdge.SetCount( nInputEdgeCount );
  662. visit.m_flIntegrationPenalty = params.m_flIntegrationPenalty;
  663. visit.ComputeVertListError( params.m_flOpenEdgePenalty, 0.0f, 1.0f, pWeights );
  664. int nMinEdge = visit.FindMinErrorEdge();
  665. int nVertexCurrent = CountUsedVerts( input.m_pIndices, input.m_nIndexCount, input.m_nVertexCount );
  666. if ( nMinEdge >= 0 )
  667. {
  668. float flMinError = visit.m_edgeList[nMinEdge].m_flCurrentError;
  669. while ( flMinError < params.m_flMaxError || nVertexCurrent > params.m_nMaxVertexCount || visit.m_nTriangleCount > params.m_nMaxTriangleCount )
  670. {
  671. visit.CollapseEdge( nMinEdge );
  672. nVertexCurrent--;
  673. // don't collapse to anything two dimensional
  674. if ( nVertexCurrent < 5 )
  675. return;
  676. nMinEdge = visit.FindMinErrorEdge();
  677. if ( nMinEdge < 0 )
  678. break;
  679. flMinError = visit.m_edgeList[nMinEdge].m_flCurrentError;
  680. }
  681. }
  682. Vprof_MarkFrame_IfEnabled();
  683. CUtlVector<uint32> indexOut;
  684. visit.WriteMeshIndexList( indexOut );
  685. int nOutputIndexCount = indexOut.Count();
  686. const uint32 nInvalidIndex = uint32(-1);
  687. CUtlVector<uint32> nIndexMap;
  688. nIndexMap.SetCount( nInputVertCount );
  689. nIndexMap.FillWithValue( nInvalidIndex );
  690. int nOutputVertexCount = 0;
  691. for ( int i = 0; i < nOutputIndexCount; i++ )
  692. {
  693. int nIndex = indexOut[i];
  694. if ( nIndexMap[nIndex] == nInvalidIndex )
  695. {
  696. nIndexMap[nIndex] = nOutputVertexCount;
  697. nOutputVertexCount++;
  698. }
  699. indexOut[i] = nIndexMap[nIndex];
  700. }
  701. meshOut.AllocateMesh( nOutputVertexCount, nOutputIndexCount, input.m_nVertexStrideFloats, input.m_pAttributes, input.m_nAttributeCount );
  702. for ( int i = 0; i < nOutputIndexCount; i++ )
  703. {
  704. meshOut.m_pIndices[i] = indexOut[i];
  705. }
  706. for ( int i = 0; i < nInputVertCount; i++ )
  707. {
  708. if ( nIndexMap[i] != nInvalidIndex )
  709. {
  710. V_memcpy( meshOut.GetVertex(nIndexMap[i]), visit.GetVertex(i), meshOut.m_nVertexStrideFloats * sizeof(float) );
  711. }
  712. }
  713. #if _DEBUG
  714. int nDeltaIndex = input.m_nIndexCount - indexOut.Count();
  715. Msg("Simplified. Removed %d triangles (now %d was %d) (now %d verts, was %d)\n", nDeltaIndex / 3, indexOut.Count() / 3, input.m_nIndexCount / 3, nOutputVertexCount, nInputVertCount );
  716. #endif
  717. }
  718. void SimplifyMesh( CMesh &meshOut, const CMesh &input, const mesh_simplifyparams_t &params, const mesh_simplifyweights_t *pWeights )
  719. {
  720. Vprof_Start_IfEnabled();
  721. SimplifyMeshQEM2( meshOut, input, params, pWeights);
  722. Vprof_Report_IfEnabled();
  723. }