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.

1178 lines
37 KiB

  1. //================ Copyright (c) Valve Corporation. All Rights Reserved. ===========================
  2. //
  3. //
  4. //
  5. //==================================================================================================
  6. #include "mesh.h"
  7. #include "tier1/mempool.h"
  8. #include "utlintrusivelist.h"
  9. #include "mathlib/ssemath.h"
  10. class CHullTri;
  11. static int g_Mod3Table[] = { 0, 1, 2, 0, 1, 2 };
  12. int SupportPoint( const Vector &vDirection, const float *pVerts, int nVertCount, uint32 nVertexStrideFloats )
  13. {
  14. if ( nVertCount < 2 )
  15. return 0;
  16. const float *pDirection = vDirection.Base();
  17. float flMax = DotProduct( pDirection, pVerts );
  18. int nMax = 0;
  19. pVerts += nVertexStrideFloats;
  20. for ( int i = 1; i < nVertCount; i++ )
  21. {
  22. float flDot = DotProduct( pDirection, pVerts );
  23. if ( flDot > flMax )
  24. {
  25. flMax = flDot;
  26. nMax = i;
  27. }
  28. pVerts += nVertexStrideFloats;
  29. }
  30. return nMax;
  31. }
  32. // Class half-edge data structure. Stores neighboring information per triangle edge
  33. // so it is possible to walk adjacent parts of the mesh
  34. class CHullHalfEdge
  35. {
  36. public:
  37. CHullHalfEdge()
  38. {
  39. m_pTri = NULL;
  40. m_pNext = NULL;
  41. m_pPrev = NULL;
  42. m_pOpposite = NULL;
  43. }
  44. void SetNext( CHullHalfEdge *pNext )
  45. {
  46. pNext->m_pPrev = this;
  47. m_pNext = pNext;
  48. }
  49. void GetVerts( int nVerts[2] );
  50. int GetStartVert();
  51. int GetIndex();
  52. CHullTri *m_pTri; // triangle containing these edges
  53. CHullHalfEdge *m_pNext; // next edge on this tri in clockwise order
  54. CHullHalfEdge *m_pPrev; // previous edge
  55. CHullHalfEdge *m_pOpposite; // the other half edge on the other triangle that shares this edge
  56. };
  57. // each triangle on the hull or potentially on the hull is one of these
  58. class CHullTri
  59. {
  60. public:
  61. // compute and store the plane of this triangle for later computation
  62. void Init( int nV0, int nV1, int nV2, const float *pVerts, int nVertexStrideFloats )
  63. {
  64. // save vertex indices
  65. m_nIndex[0] = nV0;
  66. m_nIndex[1] = nV1;
  67. m_nIndex[2] = nV2;
  68. // compute plane normal and plane constant
  69. Vector vEdge0 = *(Vector *)(pVerts + nV1 * nVertexStrideFloats ) - *(Vector *)(pVerts + nV0 * nVertexStrideFloats );
  70. Vector vEdge1 = *(Vector *)(pVerts + nV2 * nVertexStrideFloats ) - *(Vector *)(pVerts + nV0 * nVertexStrideFloats );
  71. m_vNormal = CrossProduct( vEdge1, vEdge0 );
  72. VectorNormalize( m_vNormal );
  73. m_flDist = DotProduct( pVerts + nV0 * nVertexStrideFloats, m_vNormal.Base() );
  74. // Part of the list of vertices in front of the plane
  75. m_flMaxDist = 0;
  76. m_pMaxVert = NULL;
  77. // mailbox for mesh walking
  78. m_nVisitCount = 0;
  79. // setup half edges
  80. for ( int i = 0; i < 3; i++ )
  81. {
  82. m_edges[i].m_pTri = this;
  83. m_edges[i].SetNext( &m_edges[g_Mod3Table[(i+1)]] );
  84. }
  85. }
  86. // distance from a vert to the triangle's plane
  87. float VertDist( const float *pVert )
  88. {
  89. float flDist = DotProduct( pVert, m_vNormal.Base() ) - m_flDist;
  90. return flDist;
  91. }
  92. // link half edges to neighboring triangles
  93. // NOTE: does the backlinks too, which results in doing that work twice, optimize?
  94. void SetNeighbors( CHullHalfEdge *p0, CHullHalfEdge *p1, CHullHalfEdge *p2 )
  95. {
  96. m_edges[0].m_pOpposite = p0;
  97. p0->m_pOpposite = &m_edges[0];
  98. m_edges[1].m_pOpposite = p1;
  99. p1->m_pOpposite = &m_edges[1];
  100. m_edges[2].m_pOpposite = p2;
  101. p2->m_pOpposite = &m_edges[2];
  102. }
  103. // debug, check validity / consistency. I used this to find bugs when relinking the mesh
  104. bool IsValid()
  105. {
  106. if ( m_edges[0].m_pNext != &m_edges[1] || m_edges[0].m_pPrev != &m_edges[2] ||
  107. m_edges[1].m_pNext != &m_edges[2] || m_edges[1].m_pPrev != &m_edges[0] ||
  108. m_edges[2].m_pNext != &m_edges[0] || m_edges[2].m_pPrev != &m_edges[1] )
  109. return false;
  110. if ( m_edges[0].m_pTri != this || m_edges[1].m_pTri != this || m_edges[2].m_pTri != this )
  111. return false;
  112. if ( m_edges[0].m_pOpposite->m_pOpposite != &m_edges[0] ||
  113. m_edges[1].m_pOpposite->m_pOpposite != &m_edges[1] ||
  114. m_edges[2].m_pOpposite->m_pOpposite != &m_edges[2] )
  115. return false;
  116. return true;
  117. }
  118. // simple accessor
  119. inline CHullHalfEdge *GetEdge( int nIndex ) { return &m_edges[nIndex]; }
  120. CUtlVector<const float *> m_pVerts;
  121. CHullHalfEdge m_edges[3];
  122. Vector m_vNormal;
  123. float m_flDist;
  124. int m_nIndex[3];
  125. int m_nVisitCount;
  126. const float *m_pMaxVert;
  127. float m_flMaxDist;
  128. CHullTri *m_pNext;
  129. CHullTri *m_pPrev;
  130. };
  131. // gets the verts from an edge
  132. void CHullHalfEdge::GetVerts( int nVerts[2] )
  133. {
  134. int nIndex = this - m_pTri->m_edges;
  135. Assert(nIndex>=0 && nIndex<ARRAYSIZE(m_pTri->m_edges));
  136. nVerts[0] = m_pTri->m_nIndex[nIndex];
  137. nVerts[1] = m_pTri->m_nIndex[ g_Mod3Table[nIndex+1] ];
  138. }
  139. // edge to edge index (0,1,2)
  140. int CHullHalfEdge::GetIndex()
  141. {
  142. int nIndex = this - m_pTri->m_edges;
  143. Assert(nIndex>=0 && nIndex<ARRAYSIZE(m_pTri->m_edges));
  144. return nIndex;
  145. }
  146. int CHullHalfEdge::GetStartVert()
  147. {
  148. return m_pTri->m_nIndex[GetIndex()];
  149. }
  150. // instantiate one of these to build convex hulls and fit OBBs
  151. class CConvexHullBuilder
  152. {
  153. public:
  154. CConvexHullBuilder( const float *pVerts, int nVertexCount, uint32 nVertexStrideFloats, float flFrontEpsilon )
  155. : m_triPool(128), m_nCurrentVisit(0), m_pVerts(pVerts), m_nVertexStrideFloats(nVertexStrideFloats), m_nVertexCount(nVertexCount), m_flCoplanarEpsilon(flFrontEpsilon) {}
  156. void BuildHull();
  157. // tries all faces
  158. void FitOBB( matrix3x4_t &xform, Vector &vExtents );
  159. // tries only the face with minimum extents of hull along normal
  160. void FitOBBFast( matrix3x4_t &xform, Vector &vExtents );
  161. void GenerateOutputMesh( CMesh *pOutputMesh );
  162. bool IsValid()
  163. {
  164. for ( CHullTri *pTri = m_faceListVerts.Head(); pTri != NULL; pTri = pTri->m_pNext )
  165. {
  166. if ( !pTri->IsValid() )
  167. return false;
  168. if ( pTri->m_pNext && pTri->m_pNext->m_pPrev != pTri )
  169. return false;
  170. if ( pTri->m_pPrev && pTri->m_pPrev->m_pNext != pTri )
  171. return false;
  172. if ( pTri->m_pMaxVert == NULL || pTri->m_pVerts.Count() <= 0 )
  173. return false;
  174. }
  175. for ( CHullTri *pTri = m_faceListNoVerts.Head(); pTri != NULL; pTri = pTri->m_pNext )
  176. {
  177. if ( !pTri->IsValid() )
  178. return false;
  179. if ( pTri->m_pNext && pTri->m_pNext->m_pPrev != pTri )
  180. return false;
  181. if ( pTri->m_pPrev && pTri->m_pPrev->m_pNext != pTri )
  182. return false;
  183. if ( pTri->m_pMaxVert != NULL || pTri->m_pVerts.Count() > 0 )
  184. return false;
  185. }
  186. return true;
  187. }
  188. private:
  189. void BuildInitialTetrahedron();
  190. void BuildHorizonList( CHullTri *pTri, const float *pVert );
  191. void BuildHorizonList_r( CHullTri *pTri, const float *pVert, int nEdgeStart );
  192. void BuildSilhouette_r( CHullTri *pTri, const Vector &vNormal, int nEdgeStart );
  193. void BuildSilhouette( CHullTri *pTri, const Vector &vNormal );
  194. void TransferVerts( CHullTri *pRemove, CHullTri *pNewTri );
  195. float SupportExtents( const Vector &vDirection, float *pMin, float *pMax ) const;
  196. float SupportExtents_Silhouette( const Vector &vDirection, float *pMin = NULL, float *pMax = NULL );
  197. void FitOBBToFace( CHullTri *pTri, matrix3x4_t &xform, Vector &vExtents );
  198. inline const float *GetVertex( int nIndex ) { return m_pVerts + nIndex * m_nVertexStrideFloats; }
  199. inline const Vector *GetVertexPosition( int nIndex ) { return (const Vector *)(m_pVerts + nIndex * m_nVertexStrideFloats); }
  200. void NextVisit()
  201. {
  202. m_nCurrentVisit++;
  203. }
  204. void AddTriangle( CHullTri *pTri )
  205. {
  206. if ( pTri->m_pMaxVert )
  207. {
  208. // add the new faces to the tail since they are likely to have fewer verts in front than faces already in the list
  209. m_faceListVerts.AddToTail( pTri );
  210. }
  211. else
  212. {
  213. m_faceListNoVerts.AddToHead( pTri );
  214. }
  215. }
  216. void RemoveTriangle( CHullTri *pTri )
  217. {
  218. if ( pTri->m_pMaxVert )
  219. {
  220. m_faceListVerts.RemoveNode( pTri );
  221. }
  222. else
  223. {
  224. m_faceListNoVerts.RemoveNode( pTri );
  225. }
  226. }
  227. bool Visit( CHullTri *pTri )
  228. {
  229. if ( pTri->m_nVisitCount != m_nCurrentVisit )
  230. {
  231. pTri->m_nVisitCount = m_nCurrentVisit;
  232. return true;
  233. }
  234. return false;
  235. }
  236. CUtlIntrusiveDListWithTailPtr<CHullTri> m_faceListVerts;
  237. CUtlIntrusiveDList<CHullTri> m_faceListNoVerts;
  238. CClassMemoryPool<CHullTri> m_triPool;
  239. CUtlVectorFixedGrowable<CHullHalfEdge *, 256> m_halfEdgeList;
  240. CUtlVectorFixedGrowable<CHullTri *, 256> m_removeList;
  241. CUtlVectorFixedGrowable<const float *, 256> m_silhouetteVertexList;
  242. const float *m_pVerts;
  243. float m_flCoplanarEpsilon;
  244. uint32 m_nVertexStrideFloats;
  245. int m_nVertexCount;
  246. int m_nCurrentVisit;
  247. };
  248. // Builds a tetradhedron to start the convex hull
  249. // This does an iteration like GJK - could use bounding box or some other method
  250. // BUGBUG: Test this with 2D mesh for failure cases
  251. void CConvexHullBuilder::BuildInitialTetrahedron()
  252. {
  253. Vector vDir(0,0,1);
  254. int i0 = SupportPoint( vDir, m_pVerts, m_nVertexCount, m_nVertexStrideFloats );
  255. int i1 = SupportPoint( -vDir, m_pVerts, m_nVertexCount, m_nVertexStrideFloats );
  256. Vector vEdge0 = *GetVertexPosition(i1) - *GetVertexPosition(i0);
  257. Vector vNormal = CrossProduct( *GetVertexPosition(i0), vEdge0 );
  258. if ( vNormal.LengthSqr() < 1e-3f )
  259. {
  260. vNormal.x = 1.0f;
  261. }
  262. int i2 = SupportPoint( vNormal, m_pVerts, m_nVertexCount, m_nVertexStrideFloats );
  263. if ( i2 == i0 || i2 == i1 )
  264. {
  265. i2 = SupportPoint( -vNormal, m_pVerts, m_nVertexCount, m_nVertexStrideFloats );
  266. }
  267. Vector vEdge1 = *GetVertexPosition(i2) - *GetVertexPosition(i0);
  268. vNormal = CrossProduct( vEdge1, vEdge0 );
  269. int i3 = SupportPoint( -vNormal, m_pVerts, m_nVertexCount, m_nVertexStrideFloats );
  270. CHullTri *pTri0 = m_triPool.Alloc();
  271. pTri0->Init( i0, i1, i2, m_pVerts, m_nVertexStrideFloats );
  272. CHullTri *pTri1 = m_triPool.Alloc();
  273. pTri1->Init( i0, i3, i1, m_pVerts, m_nVertexStrideFloats );
  274. CHullTri *pTri2 = m_triPool.Alloc();
  275. pTri2->Init( i1, i3, i2, m_pVerts, m_nVertexStrideFloats );
  276. CHullTri *pTri3 = m_triPool.Alloc();
  277. pTri3->Init( i2, i3, i0, m_pVerts, m_nVertexStrideFloats );
  278. m_faceListVerts.AddToTail(pTri0);
  279. m_faceListVerts.AddToTail(pTri1);
  280. m_faceListVerts.AddToTail(pTri2);
  281. m_faceListVerts.AddToTail(pTri3);
  282. pTri0->SetNeighbors( pTri1->GetEdge(2), pTri2->GetEdge(2), pTri3->GetEdge(2) );
  283. pTri1->SetNeighbors( pTri3->GetEdge(1), pTri2->GetEdge(0), pTri0->GetEdge(0) );
  284. pTri2->SetNeighbors( pTri1->GetEdge(1), pTri3->GetEdge(0), pTri0->GetEdge(1) );
  285. pTri3->SetNeighbors( pTri2->GetEdge(1), pTri1->GetEdge(0), pTri0->GetEdge(2) );
  286. const float flFrontDist = m_flCoplanarEpsilon;
  287. for ( int i = 0; i < m_nVertexCount; i++ )
  288. {
  289. for ( CHullTri *pTri = m_faceListVerts.Head(); pTri != NULL; pTri = pTri->m_pNext )
  290. {
  291. const float *pVert = GetVertex(i);
  292. float flDist = pTri->VertDist( pVert );
  293. if ( flDist > flFrontDist )
  294. {
  295. pTri->m_pVerts.AddToTail( pVert );
  296. if ( flDist > pTri->m_flMaxDist )
  297. {
  298. pTri->m_flMaxDist = flDist;
  299. pTri->m_pMaxVert = pVert;
  300. }
  301. break;
  302. }
  303. }
  304. }
  305. for ( CHullTri *pNext = NULL, *pTri = m_faceListVerts.Head(); pTri != NULL; pTri = pNext )
  306. {
  307. pNext = pTri->m_pNext;
  308. if ( !pTri->m_pMaxVert )
  309. {
  310. m_faceListVerts.RemoveNode( pTri );
  311. m_faceListNoVerts.AddToTail( pTri );
  312. }
  313. }
  314. }
  315. // Recursively visit neighboring triangles to produce the list of edges that are silhouettes with respect
  316. // to the new vertex.
  317. // IMPORTANT NOTE: This recursion is carefully designed to visit the boundary edge list exactly in order
  318. // Once it reaches the boundary it will form an edge loop in order in the m_halfEdgeList array
  319. // If this array is not in order the half-edges will not be linked up properly in BuildHull
  320. void CConvexHullBuilder::BuildHorizonList_r( CHullTri *pTri, const float *pVert, int nEdgeStart )
  321. {
  322. RemoveTriangle( pTri );
  323. for ( int i = 0; i < 3; i++ )
  324. {
  325. // this visits the neighbors in clockwise order starting with the edge you came in on
  326. CHullHalfEdge *pNeighborEdge = pTri->GetEdge( g_Mod3Table[( i + nEdgeStart)])->m_pOpposite;
  327. CHullTri *pNeighbor = pNeighborEdge->m_pTri;
  328. bool bRecurse = Visit( pNeighbor );
  329. if ( pNeighbor->VertDist(pVert) > 0 )
  330. {
  331. if ( bRecurse )
  332. {
  333. BuildHorizonList_r( pNeighbor, pVert, pNeighborEdge->GetIndex() );
  334. }
  335. }
  336. else
  337. {
  338. // This edge has a neighbor that has the new vert in front (so removed) but it's triangle
  339. // is not in front. This is a silhouette edge that will remain on the hull, but needs to
  340. // be relinked to a new triangle
  341. m_halfEdgeList.AddToTail( pNeighborEdge );
  342. }
  343. }
  344. m_removeList.AddToTail( pTri );
  345. }
  346. // Starts a new recursion to found the silhouette edge loop
  347. void CConvexHullBuilder::BuildHorizonList( CHullTri *pTri, const float *pVert )
  348. {
  349. // clear out any list - we're going to write this
  350. m_halfEdgeList.RemoveAll();
  351. // advance the token we use to avoid visiting triangles twice
  352. NextVisit();
  353. // mark the starting triangle as visited so we don't re-enter it
  354. Visit( pTri );
  355. BuildHorizonList_r( pTri, pVert, 0 );
  356. }
  357. void CConvexHullBuilder::BuildSilhouette_r( CHullTri *pTri, const Vector &vNormal, int nEdgeStart )
  358. {
  359. for ( int i = 0; i < 3; i++ )
  360. {
  361. // this visits the neighbors in clockwise order starting with the edge you came in on
  362. CHullHalfEdge *pNeighborEdge = pTri->GetEdge( g_Mod3Table[( i + nEdgeStart)])->m_pOpposite;
  363. CHullTri *pNeighbor = pNeighborEdge->m_pTri;
  364. bool bRecurse = Visit( pNeighbor );
  365. float flDot = DotProduct( vNormal, pNeighbor->m_vNormal );
  366. if ( flDot > 0 )
  367. {
  368. if ( bRecurse )
  369. {
  370. BuildSilhouette_r( pNeighbor, vNormal, pNeighborEdge->GetIndex() );
  371. }
  372. }
  373. else
  374. {
  375. // add edge to sil
  376. m_silhouetteVertexList.AddToTail( GetVertex( pNeighborEdge->m_pNext->GetStartVert() ) );
  377. }
  378. }
  379. }
  380. // Starts a new recursion to found the silhouette edge loop
  381. void CConvexHullBuilder::BuildSilhouette( CHullTri *pTri, const Vector &vNormal )
  382. {
  383. // clear out any list - we're going to write this
  384. m_silhouetteVertexList.RemoveAll();
  385. // advance the token we use to avoid visiting triangles twice
  386. NextVisit();
  387. // mark the starting triangle as visited so we don't re-enter it
  388. Visit( pTri );
  389. BuildSilhouette_r( pTri, vNormal, 0 );
  390. }
  391. // transfer any verts in the front list of pRemove to the front list of pNewTri if they
  392. // are in front of pNewTri
  393. // Remove them from the original list so they won't get transferred to subsequent triangles
  394. void CConvexHullBuilder::TransferVerts( CHullTri *pRemove, CHullTri *pNewTri )
  395. {
  396. int nVertex = 0;
  397. const float flFrontDist = m_flCoplanarEpsilon;
  398. while ( nVertex < pRemove->m_pVerts.Count() )
  399. {
  400. const float *pCheckVert = pRemove->m_pVerts[nVertex];
  401. if ( pCheckVert == pRemove->m_pMaxVert )
  402. {
  403. pRemove->m_pVerts.FastRemove(nVertex);
  404. continue;
  405. }
  406. float flDist = pNewTri->VertDist(pCheckVert);
  407. if ( flDist > flFrontDist )
  408. {
  409. pNewTri->m_pVerts.AddToTail( pCheckVert );
  410. if ( flDist > pNewTri->m_flMaxDist )
  411. {
  412. pNewTri->m_flMaxDist = flDist;
  413. pNewTri->m_pMaxVert = pCheckVert;
  414. }
  415. pRemove->m_pVerts.FastRemove(nVertex);
  416. }
  417. else
  418. {
  419. nVertex++;
  420. }
  421. }
  422. }
  423. float CConvexHullBuilder::SupportExtents( const Vector &vDirection, float *pMin, float *pMax ) const
  424. {
  425. const float *pVerts = m_pVerts;
  426. const float *pDirection = vDirection.Base();
  427. float flMax = DotProduct( pDirection, pVerts );
  428. float flMin = flMax;
  429. const uint32 nStride = m_nVertexStrideFloats;
  430. pVerts += nStride;
  431. for ( int i = 1; i < m_nVertexCount; i++ )
  432. {
  433. float flDot = DotProduct( pDirection, pVerts );
  434. flMax = MAX(flMax, flDot);
  435. flMin = MIN(flMin, flDot);
  436. pVerts += nStride;
  437. }
  438. if ( pMin )
  439. {
  440. *pMin = flMin;
  441. }
  442. if ( pMax )
  443. {
  444. *pMax = flMax;
  445. }
  446. return flMax - flMin;
  447. }
  448. // UNDONE: Only look at half edge verts
  449. float CConvexHullBuilder::SupportExtents_Silhouette( const Vector &vDirection, float *pMin, float *pMax )
  450. {
  451. if ( !m_silhouetteVertexList.Count() )
  452. return 0;
  453. const float *pDirection = vDirection.Base();
  454. float flMax = DotProduct( pDirection, m_silhouetteVertexList[0] );
  455. float flMin = flMax;
  456. int nVertCount = m_silhouetteVertexList.Count();
  457. for ( int i = 1; i < nVertCount; i++ )
  458. {
  459. float flDot = DotProduct( pDirection, m_silhouetteVertexList[i] );
  460. flMax = MAX(flMax, flDot);
  461. flMin = MIN(flMin, flDot);
  462. }
  463. if ( pMin )
  464. {
  465. *pMin = flMin;
  466. }
  467. if ( pMax )
  468. {
  469. *pMax = flMax;
  470. }
  471. return flMax - flMin;
  472. }
  473. // Construct the triangulated convex hull from the point set
  474. void CConvexHullBuilder::BuildHull()
  475. {
  476. // get a tetrahedron for this mesh and build a list of verts for each face
  477. BuildInitialTetrahedron();
  478. // process each face that has vertices in front of it until all are done
  479. while ( m_faceListVerts.Head() != NULL )
  480. {
  481. CHullTri *pTri = m_faceListVerts.Head();
  482. const float *pNewVert = pTri->m_pMaxVert;
  483. int nNewVertIndex = (pNewVert - m_pVerts) / m_nVertexStrideFloats;
  484. // Get the horizon edge loop of the new vert
  485. BuildHorizonList( pTri, pNewVert );
  486. // now build a new triangle along each horizon edge and the new vert
  487. int nEdgeVerts[2];
  488. CUtlVectorFixedGrowable<CHullTri *, 64> addedList;
  489. int nLastEdgeVert = -1;
  490. for ( int i = 0; i < m_halfEdgeList.Count(); i++ )
  491. {
  492. CHullHalfEdge *pEdge = m_halfEdgeList[i];
  493. pEdge->GetVerts( nEdgeVerts );
  494. CHullTri *pNewTri = m_triPool.Alloc();
  495. if ( nLastEdgeVert >= 0 )
  496. {
  497. Assert(nEdgeVerts[1]==nLastEdgeVert);
  498. }
  499. nLastEdgeVert = nEdgeVerts[0];
  500. pNewTri->Init( nNewVertIndex, nEdgeVerts[1], nEdgeVerts[0], m_pVerts, m_nVertexStrideFloats );
  501. // now transfer any verts in front of the new triangle
  502. for ( int j = 0; j < m_removeList.Count(); j++ )
  503. {
  504. TransferVerts( m_removeList[j], pNewTri );
  505. }
  506. addedList.AddToTail( pNewTri );
  507. // put this triangle in the list of hull triangles or the list of triangles to be expanded
  508. AddTriangle(pNewTri);
  509. }
  510. // now free the removed triangles (we had to keep them until their verts were transferred)
  511. for ( int j = 0; j < m_removeList.Count(); j++ )
  512. {
  513. m_triPool.Free( m_removeList[j] );
  514. }
  515. m_removeList.RemoveAll();
  516. // now link up the halfEdges between the new triangles and the hull mesh
  517. // NOTE: This depends on m_halfEdgeList being an in-order loop which is guaranteed by
  518. // the recursion order in BuildHorizon
  519. int nNewTriangleCount = addedList.Count();
  520. CHullTri *pPrev = addedList[ nNewTriangleCount-1 ];
  521. for ( int i = 0; i < nNewTriangleCount; i++ )
  522. {
  523. CHullTri *pNext = addedList[ (i + 1) % nNewTriangleCount ];
  524. addedList[i]->SetNeighbors( pPrev->GetEdge(2), m_halfEdgeList[i], pNext->GetEdge(0) );
  525. pPrev = addedList[i];
  526. }
  527. }
  528. Assert(m_faceListVerts.Head()==NULL);
  529. }
  530. // write all of the faces on the hull to an output mesh
  531. void CConvexHullBuilder::GenerateOutputMesh( CMesh *pOutMesh )
  532. {
  533. CUtlVector<int> vertexMap;
  534. vertexMap.SetCount( m_nVertexCount );
  535. vertexMap.FillWithValue( -1 );
  536. int nOutVert = 0;
  537. int nOutTri = 0;
  538. for ( CHullTri *pTri = m_faceListNoVerts.Head(); pTri != NULL; pTri = pTri->m_pNext )
  539. {
  540. for ( int j = 0; j < 3; j++ )
  541. {
  542. int nIndex = pTri->m_nIndex[j];
  543. if ( vertexMap[nIndex] < 0 )
  544. {
  545. vertexMap[nIndex] = nOutVert++;
  546. }
  547. }
  548. nOutTri++;
  549. }
  550. vertexMap.FillWithValue(-1);
  551. pOutMesh->AllocateMesh( nOutVert, nOutTri * 3, m_nVertexStrideFloats, NULL, 0 );
  552. nOutVert = 0;
  553. nOutTri = 0;
  554. for ( CHullTri *pTri = m_faceListNoVerts.Head(); pTri != NULL; pTri = pTri->m_pNext )
  555. {
  556. for ( int j = 0; j < 3; j++ )
  557. {
  558. int nIndex = pTri->m_nIndex[j];
  559. if ( vertexMap[nIndex] < 0 )
  560. {
  561. CopyVertex( pOutMesh->GetVertex(nOutVert), GetVertex(nIndex), m_nVertexStrideFloats );
  562. vertexMap[nIndex] = nOutVert++;
  563. }
  564. pOutMesh->m_pIndices[nOutTri*3+j] = vertexMap[nIndex];
  565. }
  566. nOutTri++;
  567. }
  568. }
  569. void CConvexHullBuilder::FitOBBToFace( CHullTri *pTri, matrix3x4_t &xform, Vector &vExtents )
  570. {
  571. BuildSilhouette( pTri, pTri->m_vNormal );
  572. int nEdgeCount = m_silhouetteVertexList.Count();
  573. int nMinEdge = 0;
  574. float flMinPerimeter = 1e24f;
  575. for ( int i = 0; i < nEdgeCount; i++ )
  576. {
  577. int nNext = (i+1)%nEdgeCount;
  578. Vector v0 = *(Vector *)m_silhouetteVertexList[i];
  579. Vector v1 = *(Vector *)m_silhouetteVertexList[nNext];
  580. Vector vEdge = v1 - v0;
  581. Vector vNormalY = CrossProduct( pTri->m_vNormal, vEdge );
  582. VectorNormalize( vNormalY );
  583. float flExtentTestY = SupportExtents_Silhouette( vNormalY );
  584. Vector vNormalZ = CrossProduct( pTri->m_vNormal, vNormalY );
  585. float flExtentTestZ = SupportExtents_Silhouette( vNormalZ );
  586. float flPerim = flExtentTestZ + flExtentTestY;
  587. if ( flPerim < flMinPerimeter )
  588. {
  589. flMinPerimeter = flPerim;
  590. nMinEdge = i;
  591. }
  592. }
  593. Vector vNormalX = pTri->m_vNormal;
  594. int nNext = (nMinEdge+1)%nEdgeCount;
  595. Vector vEdge = *(Vector *)m_silhouetteVertexList[nNext] - *(Vector *)m_silhouetteVertexList[nMinEdge];
  596. Vector vNormalY = CrossProduct( pTri->m_vNormal, vEdge );
  597. VectorNormalize( vNormalY );
  598. Vector vNormalZ = CrossProduct( pTri->m_vNormal, vNormalY );
  599. VectorNormalize( vNormalZ );
  600. MatrixSetColumn( vNormalX, 0, xform );
  601. MatrixSetColumn( vNormalY, 1, xform );
  602. MatrixSetColumn( vNormalZ, 2, xform );
  603. Vector vMins, vMaxs, vCenter;
  604. SupportExtents( pTri->m_vNormal, &vMins.x, &vMaxs.x );
  605. SupportExtents_Silhouette( vNormalY, &vMins.y, &vMaxs.y );
  606. SupportExtents_Silhouette( vNormalZ, &vMins.z, &vMaxs.z );
  607. vCenter = 0.5f * (vMins + vMaxs);
  608. vExtents = vMaxs - vCenter;
  609. vCenter = vNormalX * vCenter.x + vNormalY * vCenter.y + vNormalZ * vCenter.z;
  610. MatrixSetColumn( vCenter, 3, xform );
  611. }
  612. void CConvexHullBuilder::FitOBBFast( matrix3x4_t &xform, Vector &vExtents )
  613. {
  614. CHullTri *pTri = m_faceListNoVerts.Head();
  615. if ( !pTri )
  616. return;
  617. CHullTri *pMinTri = pTri;
  618. float flMinX = 0, flMaxX = 0;
  619. float flExtentX = SupportExtents( pTri->m_vNormal, &flMinX, &flMaxX );
  620. pTri = pTri->m_pNext;
  621. for ( ; pTri != NULL; pTri = pTri->m_pNext )
  622. {
  623. float flMinTest = 0, flMaxTest = 0;
  624. float flExtentTest = SupportExtents( pTri->m_vNormal, &flMinTest, &flMaxTest );
  625. if ( flExtentTest < flExtentX )
  626. {
  627. flExtentX = flExtentTest;
  628. flMinX = flMinTest;
  629. flMaxX = flMaxTest;
  630. pMinTri = pTri;
  631. }
  632. }
  633. FitOBBToFace( pMinTri, xform, vExtents );
  634. }
  635. void CConvexHullBuilder::FitOBB( matrix3x4_t &xform, Vector &vExtents )
  636. {
  637. // try all faces, return minimum surface area box
  638. CHullTri *pMinTri = NULL;
  639. Vector vTmpExtents;
  640. matrix3x4_t tmpXform;
  641. float flMinSurfacearea = 1e24;
  642. for ( CHullTri *pTri = m_faceListNoVerts.Head(); pTri != NULL; pTri = pTri->m_pNext )
  643. {
  644. FitOBBToFace( pTri, tmpXform, vTmpExtents );
  645. float flSurfaceArea = vTmpExtents.x * vTmpExtents.y + vTmpExtents.x * vTmpExtents.z + vTmpExtents.y * vTmpExtents.z;
  646. if ( flSurfaceArea < flMinSurfacearea )
  647. {
  648. flMinSurfacearea = flSurfaceArea;
  649. pMinTri = pTri;
  650. }
  651. }
  652. FitOBBToFace( pMinTri, xform, vExtents );
  653. }
  654. // returns the mesh containing the convex hull of the input mesh
  655. void ConvexHull3D( CMesh *pOutMesh, const CMesh &inputMesh, float flCoplanarEpsilon )
  656. {
  657. CConvexHullBuilder builder( inputMesh.m_pVerts, inputMesh.m_nVertexCount, inputMesh.m_nVertexStrideFloats, flCoplanarEpsilon );
  658. builder.BuildHull();
  659. builder.GenerateOutputMesh( pOutMesh );
  660. }
  661. void FitOBBToMesh( matrix3x4_t *pCenter, Vector *pExtents, const CMesh &inputMesh, float flCoplanarEpsilon )
  662. {
  663. CConvexHullBuilder builder( inputMesh.m_pVerts, inputMesh.m_nVertexCount, inputMesh.m_nVertexStrideFloats, flCoplanarEpsilon );
  664. builder.BuildHull();
  665. matrix3x4_t xform;
  666. Vector vExtents;
  667. builder.FitOBB( xform, vExtents );
  668. if ( pCenter && pExtents )
  669. {
  670. *pCenter = xform;
  671. *pExtents = vExtents;
  672. }
  673. }
  674. Vector CSGInsidePoint( VPlane *pPlanes, int planeCount )
  675. {
  676. Vector point = vec3_origin;
  677. for ( int i = 0; i < planeCount; i++ )
  678. {
  679. float d = DotProduct( pPlanes[i].m_Normal, point ) - pPlanes[i].m_Dist;
  680. if ( d < 0 )
  681. {
  682. point -= d * pPlanes[i].m_Normal;
  683. }
  684. }
  685. return point;
  686. }
  687. fltx4 CSGInsidePoint_SIMD( fltx4 *pPlanes, int nPlaneCount )
  688. {
  689. fltx4 point = Four_Zeros;
  690. // TODO: Depending of the number of planes that we have, it may be possible to actually calculate several planes at the same time.
  691. // Have to see if the calculation is still correct when done in parallel (the offset may still be similar).
  692. // Even with the current code complexity, it would be good to do 4 by 4 if we have at least 8 to 12 planes every time.
  693. for ( int i = 0; i < nPlaneCount; i++ )
  694. {
  695. fltx4 planeValue = pPlanes[i];
  696. // float d = DotProduct( pPlanes[i].m_Normal, point ) - pPlanes[i].m_Dist;
  697. fltx4 result = Dot3SIMD( planeValue, point ); // Fast on X360, not so fast on PC and PS3 - Still faster than FPU operation though
  698. result = SubSIMD( result, SplatWSIMD( planeValue ) ); // XYZW has the result of the dot product
  699. // if ( d < 0 )
  700. // point -= d * pPlanes[i].m_Normal;
  701. fltx4 offset = MulSIMD( result, planeValue ); // XYZ has d * normal, and W as garbage
  702. bi32x4 mask = CmpLtSIMD( result, Four_Zeros ); // (d < 0) ? 0xffffffff : 0
  703. point = SubSIMD( point, MaskedAssign( mask, offset, Four_Zeros ) ); // point will have garbage W, but this has no impact
  704. // (as we use Dot3 product).
  705. }
  706. point = SetWToZeroSIMD( point ); // Just in case
  707. return point;
  708. }
  709. void TranslatePlaneList( VPlane *pPlanes, int nPlaneCount, const Vector &offset )
  710. {
  711. for ( int i = 0; i < nPlaneCount; i++ )
  712. {
  713. pPlanes[i].m_Dist += DotProduct( offset, pPlanes[i].m_Normal );
  714. }
  715. }
  716. void TranslatePlaneList_SIMD( fltx4 *pPlanes, int nPlaneCount, const fltx4 &offset )
  717. {
  718. int i = 0;
  719. fltx4 f4WMask = LoadAlignedSIMD( g_SIMD_ComponentMask[3] );
  720. while ( nPlaneCount >= 4 )
  721. {
  722. fltx4 plane0 = pPlanes[i];
  723. fltx4 plane1 = pPlanes[i + 1];
  724. fltx4 plane2 = pPlanes[i + 2];
  725. fltx4 plane3 = pPlanes[i + 3];
  726. fltx4 dot0 = Dot3SIMD( offset, plane0 );
  727. fltx4 dot1 = Dot3SIMD( offset, plane1 );
  728. fltx4 dot2 = Dot3SIMD( offset, plane2 );
  729. fltx4 dot3 = Dot3SIMD( offset, plane3 );
  730. dot0 = AndSIMD( dot0, f4WMask ); // 0 0 0 Dot
  731. dot1 = AndSIMD( dot1, f4WMask ); // W contains Dist
  732. dot2 = AndSIMD( dot2, f4WMask );
  733. dot3 = AndSIMD( dot3, f4WMask );
  734. pPlanes[i] = AddSIMD( plane0, dot0 );
  735. pPlanes[i + 1] = AddSIMD( plane1, dot1 );
  736. pPlanes[i + 2] = AddSIMD( plane2, dot2 );
  737. pPlanes[i + 3] = AddSIMD( plane3, dot3 );
  738. nPlaneCount -= 4;
  739. i += 4;
  740. }
  741. while ( nPlaneCount > 0 )
  742. {
  743. fltx4 plane0 = pPlanes[i];
  744. fltx4 dot0 = Dot3SIMD( offset, plane0 );
  745. dot0 = AndSIMD( dot0, f4WMask ); // 0 0 0 Dot
  746. pPlanes[i] = AddSIMD( plane0, dot0 );
  747. ++i;
  748. --nPlaneCount;
  749. }
  750. }
  751. void InvertPlanes( VPlane *pPlaneList, int nPlaneCount )
  752. {
  753. for ( int i = 0; i < nPlaneCount; i++ )
  754. {
  755. pPlaneList[i].m_Normal *= -1;
  756. pPlaneList[i].m_Dist *= -1;
  757. }
  758. }
  759. void InvertPlanes_SIMD( fltx4 * pPlanes, int nPlaneCount )
  760. {
  761. int i = 0;
  762. while ( nPlaneCount >= 4)
  763. {
  764. pPlanes[i] = -pPlanes[i];
  765. pPlanes[i + 1] = -pPlanes[i + 1];
  766. pPlanes[i + 2] = -pPlanes[i + 2];
  767. pPlanes[i + 3] = -pPlanes[i + 3];
  768. i += 4;
  769. nPlaneCount -= 4;
  770. }
  771. while ( nPlaneCount > 0 )
  772. {
  773. pPlanes[i] = -pPlanes[i];
  774. ++i;
  775. --nPlaneCount;
  776. }
  777. }
  778. void CSGPlaneList( CUtlVector<Vector> &verts, CUtlVector<uint32> &index, CUtlVector<uint32> &trianglePlaneIndices, VPlane *pPlaneList, int nPlaneCount, float flCoplanarEpsilon )
  779. {
  780. const int MAX_VERTS = 128;
  781. Vector vertsIn[MAX_VERTS], vertsOut[MAX_VERTS];
  782. // compute a point inside the volume defined by these planes
  783. Vector insidePoint = CSGInsidePoint( pPlaneList, nPlaneCount );
  784. // move the planes so that the inside point is at the origin
  785. // NOTE: This is to maximize precision for the CSG operations
  786. TranslatePlaneList( pPlaneList, nPlaneCount, -insidePoint );
  787. // Build the CSG solid of this leaf given that the planes in the list define a convex solid
  788. for ( int i = 0; i < nPlaneCount; ++i )
  789. {
  790. // Build a big-ass poly in this plane
  791. int vertCount = PolyFromPlane( vertsIn, pPlaneList[i].m_Normal, pPlaneList[i].m_Dist );
  792. // Now chop it by every other plane
  793. int j;
  794. for ( j = 0; j < nPlaneCount; ++j )
  795. {
  796. // don't clip planes with themselves
  797. if ( i == j )
  798. continue;
  799. // Less than a poly left, something is wrong, don't bother with this polygon
  800. if ( vertCount < 3 )
  801. continue;
  802. // Chop the polygon against this plane
  803. vertCount = ClipPolyToPlane( vertsIn, vertCount, vertsOut, pPlaneList[j].m_Normal, pPlaneList[j].m_Dist, flCoplanarEpsilon );
  804. // Just copy the verts each time, don't bother swapping pointers (efficiency is not a goal here)
  805. for ( int k = 0; k < vertCount; ++k )
  806. {
  807. VectorCopy( vertsOut[k], vertsIn[k] );
  808. }
  809. }
  810. // We've got a polygon here
  811. if ( vertCount >= 3 )
  812. {
  813. // Since this is a convex polygon, use the fan algorithm to create the triangles
  814. int baseVertex = verts.Count();
  815. for ( int j = 0; j < vertCount - 2; ++j )
  816. {
  817. trianglePlaneIndices.AddToTail(i);
  818. index.AddToTail( baseVertex );
  819. index.AddToTail( baseVertex + j + 1 );
  820. index.AddToTail( baseVertex + j + 2 );
  821. }
  822. // Copy verts
  823. for ( int j = 0; j < vertCount; ++j )
  824. {
  825. verts.AddToTail( vertsIn[j] + insidePoint );
  826. }
  827. }
  828. }
  829. }
  830. void CSGPlaneList_SIMD( CUtlVector<fltx4> &verts, CUtlVector<uint32> &index, CUtlVector<uint16> &trianglePlaneIndices, fltx4 *pPlaneList, int nPlaneCount, float flCoplanarEpsilon )
  831. {
  832. const int MAX_VERTS = 128;
  833. fltx4 vertsIn[MAX_VERTS], vertsOut[MAX_VERTS];
  834. #if _DEBUG
  835. Vector tempVertsIn[MAX_VERTS], tempVertOuts[MAX_VERTS];
  836. #endif
  837. // compute a point inside the volume defined by these planes
  838. fltx4 insidePoint = CSGInsidePoint_SIMD( pPlaneList, nPlaneCount );
  839. #if _DEBUG
  840. Vector vInsidePoint = CSGInsidePoint( (VPlane *)pPlaneList, nPlaneCount );
  841. Assert( vInsidePoint.x == SubFloat( insidePoint, 0 ) );
  842. Assert( vInsidePoint.y == SubFloat( insidePoint, 1 ) );
  843. Assert( vInsidePoint.z == SubFloat( insidePoint, 2 ) );
  844. #endif
  845. // move the planes so that the inside point is at the origin
  846. // NOTE: This is to maximize precision for the CSG operations
  847. TranslatePlaneList_SIMD( pPlaneList, nPlaneCount, -insidePoint );
  848. // Build the CSG solid of this leaf given that the planes in the list define a convex solid
  849. for ( int i = 0; i < nPlaneCount; ++i )
  850. {
  851. // Build a big-ass poly in this plane
  852. PolyFromPlane_SIMD( vertsIn, pPlaneList[i] );
  853. int nVertCount = 4; // PolyFromPlane actually always return 4. Bake the result.
  854. #if _DEBUG
  855. Vector normalI( SubFloat( pPlaneList[i], 0 ), SubFloat( pPlaneList[i], 1 ), SubFloat( pPlaneList[i], 2 ) );
  856. int nResult = PolyFromPlane( tempVertsIn, normalI, SubFloat( pPlaneList[i], 3 ) );
  857. Assert( nVertCount == nResult );
  858. for (int n = 0 ; n < nVertCount ; ++n )
  859. {
  860. Assert( tempVertsIn[n].x == SubFloat( vertsIn[n], 0 ) );
  861. Assert( tempVertsIn[n].y == SubFloat( vertsIn[n], 1 ) );
  862. Assert( tempVertsIn[n].z == SubFloat( vertsIn[n], 2 ) );
  863. }
  864. #endif
  865. // Now chop it by every other plane
  866. int j;
  867. for ( j = 0; j < nPlaneCount; ++j )
  868. {
  869. // don't clip planes with themselves
  870. if ( i == j )
  871. {
  872. continue;
  873. }
  874. // Less than a poly left, something is wrong, don't bother with this polygon
  875. if ( nVertCount < 3 )
  876. {
  877. break; // Or with any other polygon (as nVertCount would not be set in the loop).
  878. }
  879. // Chop the polygon against this plane
  880. #if _DEBUG
  881. int nOldVertCount = nVertCount;
  882. #endif
  883. nVertCount = ClipPolyToPlane_SIMD( vertsIn, nVertCount, vertsOut, pPlaneList[j], flCoplanarEpsilon );
  884. #if _DEBUG
  885. Vector normal( SubFloat( pPlaneList[j], 0 ), SubFloat( pPlaneList[j], 1 ), SubFloat( pPlaneList[j], 2 ) );
  886. int nClipResult = ClipPolyToPlane( tempVertsIn, nOldVertCount, tempVertOuts, normal, SubFloat( pPlaneList[j], 3 ), flCoplanarEpsilon );
  887. Assert( nClipResult == nVertCount );
  888. for ( int n = 0 ; n < nVertCount ; ++n )
  889. {
  890. // In some cases, the SIMD algorithm does not return the exact same result as the fpu version.
  891. Assert( fabs( tempVertOuts[n].x - SubFloat( vertsOut[n], 0 ) ) < 0.001f );
  892. Assert( fabs( tempVertOuts[n].y - SubFloat( vertsOut[n], 1 ) ) < 0.001f );
  893. Assert( fabs( tempVertOuts[n].z - SubFloat( vertsOut[n], 2 ) ) < 0.001f );
  894. }
  895. #endif
  896. // Just copy the verts each time, don't bother swapping pointers (efficiency is not a goal here) - Not anymore :)
  897. int nNumberOfVertices = nVertCount;
  898. int k = 0;
  899. while ( nNumberOfVertices >= 4)
  900. {
  901. vertsIn[k] = vertsOut[k];
  902. vertsIn[k + 1] = vertsOut[k + 1];
  903. vertsIn[k + 2] = vertsOut[k + 2];
  904. vertsIn[k + 3] = vertsOut[k + 3];
  905. #if _DEBUG
  906. tempVertsIn[k] = tempVertOuts[k];
  907. tempVertsIn[k + 1] = tempVertOuts[k + 1];
  908. tempVertsIn[k + 2] = tempVertOuts[k + 2];
  909. tempVertsIn[k + 3] = tempVertOuts[k + 3];
  910. #endif
  911. nNumberOfVertices -= 4;
  912. k += 4;
  913. }
  914. while ( nNumberOfVertices > 0 )
  915. {
  916. vertsIn[k] = vertsOut[k];
  917. #if _DEBUG
  918. tempVertsIn[k] = tempVertOuts[k];
  919. #endif
  920. --nNumberOfVertices;
  921. ++k;
  922. }
  923. }
  924. // We've got a polygon here
  925. if ( nVertCount >= 3 )
  926. {
  927. // Since this is a convex polygon, use the fan algorithm to create the triangles
  928. const int NUMBER_OF_TRIANGLES = nVertCount - 2;
  929. int nDestTrianglePlaneIndex = trianglePlaneIndices.Count();
  930. int nDestIndex = index.Count();
  931. // Set the count once (will grow as needed), so we don't call AddTail() several times.
  932. trianglePlaneIndices.SetCountNonDestructively( nDestTrianglePlaneIndex + NUMBER_OF_TRIANGLES );
  933. index.SetCountNonDestructively( nDestIndex + ( 3 * NUMBER_OF_TRIANGLES ) );
  934. int nBaseVertex = verts.Count();
  935. for ( int j = 0; j < NUMBER_OF_TRIANGLES; ++j )
  936. {
  937. trianglePlaneIndices[nDestTrianglePlaneIndex] = i;
  938. index[nDestIndex] = nBaseVertex;
  939. index[nDestIndex + 1] = nBaseVertex + j + 1;
  940. index[nDestIndex + 2] = nBaseVertex + j + 2;
  941. ++nDestTrianglePlaneIndex;
  942. nDestIndex += 3;
  943. }
  944. // Copy verts
  945. int nNumberOfVertices = nVertCount;
  946. int nDestVertIndex = verts.Count();
  947. // Set the count once (will grow as needed), so we don't call AddTail() several times.
  948. verts.SetCountNonDestructively( nDestVertIndex + nNumberOfVertices );
  949. int k = 0;
  950. while ( nNumberOfVertices >= 4 )
  951. {
  952. fltx4 result0 = AddSIMD( vertsIn[k], insidePoint );
  953. fltx4 result1 = AddSIMD( vertsIn[k + 1], insidePoint );
  954. fltx4 result2 = AddSIMD( vertsIn[k + 2], insidePoint );
  955. fltx4 result3 = AddSIMD( vertsIn[k + 3], insidePoint );
  956. verts[nDestVertIndex] = result0;
  957. verts[nDestVertIndex + 1] = result1;
  958. verts[nDestVertIndex + 2] = result2;
  959. verts[nDestVertIndex + 3] = result3;
  960. nNumberOfVertices -= 4;
  961. k += 4;
  962. nDestVertIndex += 4;
  963. }
  964. while ( nNumberOfVertices > 0 )
  965. {
  966. fltx4 result = AddSIMD( vertsIn[k], insidePoint );
  967. verts[nDestVertIndex] = result;
  968. --nNumberOfVertices;
  969. ++k;
  970. ++nDestVertIndex;
  971. }
  972. }
  973. }
  974. }
  975. void HullFromPlanes( CMesh *pOutMesh, CUtlVector<uint32> *pTrianglePlaneIndices, const float *pPlanesInput, int nPlaneCount, int nPlaneStrideFloats, float flCoplanarEpsilon )
  976. {
  977. CUtlVector<VPlane> planes;
  978. planes.SetCount( nPlaneCount );
  979. const float *pPlane = pPlanesInput;
  980. for ( int i = 0; i < nPlaneCount; i++, pPlane += nPlaneStrideFloats )
  981. {
  982. planes[i].m_Normal.Init( pPlane[0], pPlane[1], pPlane[2] );
  983. planes[i].m_Dist = pPlane[3];
  984. }
  985. // the clipping code returns polys IN FRONT of the plane, whereas our interface has planes pointing out of the solid
  986. InvertPlanes( planes.Base(), planes.Count() );
  987. CUtlVector<Vector> verts;
  988. CUtlVector<uint32> index;
  989. CUtlVector<uint32> trianglePlaneIndices;
  990. CSGPlaneList( verts, index, trianglePlaneIndices, planes.Base(), planes.Count(), flCoplanarEpsilon );
  991. if ( verts.Count() )
  992. {
  993. pOutMesh->AllocateAndCopyMesh( verts.Count(), (float *)verts.Base(), index.Count(), index.Base(), 3, 0, 0 );
  994. }
  995. // If the caller wants the index of the originating plane of each triangle, put it in the output.
  996. if( pTrianglePlaneIndices )
  997. pTrianglePlaneIndices->Swap( trianglePlaneIndices );
  998. }
  999. // Optimized version where there is no need to copy, planes are modified in place (assuming that the list has been created dynamically), and we use VMX intensively
  1000. void HullFromPlanes_SIMD( CMesh *pOutMesh, CUtlVector<uint16> *pTrianglePlaneIndices, fltx4 *pPlanesInput, int nPlaneCount, float flCoplanarEpsilon )
  1001. {
  1002. #if _DEBUG
  1003. CUtlVector<VPlane> planes;
  1004. planes.SetCount( nPlaneCount );
  1005. const float *pPlane = (const float *)pPlanesInput;
  1006. for ( int i = 0; i < nPlaneCount; i++, pPlane += 4 )
  1007. {
  1008. planes[i].m_Normal.Init( pPlane[0], pPlane[1], pPlane[2] );
  1009. planes[i].m_Dist = pPlane[3];
  1010. }
  1011. // the clipping code returns polys IN FRONT of the plane, whereas our interface has planes pointing out of the solid
  1012. InvertPlanes( planes.Base(), planes.Count() );
  1013. #endif
  1014. // the clipping code returns polys IN FRONT of the plane, whereas our interface has planes pointing out of the solid
  1015. InvertPlanes_SIMD( pPlanesInput, nPlaneCount ); // Too bad we are doing this here instead of in the caller code (when creating the array).
  1016. // At the same time, this is implementation specific that the caller should not be aware of
  1017. CUtlVector<fltx4> verts;
  1018. CUtlVector<uint32> index;
  1019. CUtlVector<uint16> trianglePlaneIndices;
  1020. CSGPlaneList_SIMD( verts, index, trianglePlaneIndices, pPlanesInput, nPlaneCount, flCoplanarEpsilon );
  1021. #if _DEBUG
  1022. CUtlVector<Vector> vertsSlow;
  1023. CUtlVector<uint32> indexSlow;
  1024. CUtlVector<uint32> trianglePlaneIndicesSlow;
  1025. CSGPlaneList( vertsSlow, indexSlow, trianglePlaneIndicesSlow, planes.Base(), planes.Count(), flCoplanarEpsilon );
  1026. Assert( verts.Count() == vertsSlow.Count() );
  1027. Assert( index.Count() == indexSlow.Count() );
  1028. Assert( trianglePlaneIndices.Count() == trianglePlaneIndicesSlow.Count() );
  1029. for (int i = 0 ; i < verts.Count() ; ++i )
  1030. {
  1031. Assert( SubFloat( verts[i], 0 ) == vertsSlow[i].x );
  1032. Assert( SubFloat( verts[i], 1 ) == vertsSlow[i].y );
  1033. Assert( SubFloat( verts[i], 2 ) == vertsSlow[i].z );
  1034. }
  1035. for (int i = 0 ; i < index.Count() ; ++i )
  1036. {
  1037. Assert( index[i] == indexSlow[i] );
  1038. }
  1039. for (int i = 0 ; i < trianglePlaneIndices.Count() ; ++i )
  1040. {
  1041. Assert( trianglePlaneIndices[i] == trianglePlaneIndicesSlow[i] );
  1042. }
  1043. #endif
  1044. // Do we really need a mesh in our use case?
  1045. if ( verts.Count() )
  1046. {
  1047. pOutMesh->AllocateAndCopyMesh( verts.Count(), (float *)verts.Base(), index.Count(), index.Base(), 4, 0, 0 );
  1048. }
  1049. // If the caller wants the index of the originating plane of each triangle, put it in the output.
  1050. if( pTrianglePlaneIndices )
  1051. {
  1052. pTrianglePlaneIndices->Swap( trianglePlaneIndices );
  1053. }
  1054. }