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.

665 lines
24 KiB

  1. //============ Copyright (c) Valve Corporation, All rights reserved. ============
  2. //
  3. // Utility functions for polygon simplification / convex decomposition.
  4. //
  5. //===============================================================================
  6. #include "polygon.h"
  7. #include "quadric.h"
  8. // Assumes that points are ordered clockwise when looking at the face of the polygon
  9. void SimplifyPolygon( CUtlVector< Vector > *pPoints, const Vector &vNormal, float flMaximumDeviation )
  10. {
  11. if ( pPoints->Count() < 3 )
  12. {
  13. return;
  14. }
  15. int nVertices = pPoints->Count();
  16. Vector vPrevious = pPoints->Element( nVertices - 1 );
  17. Vector vCurrent = pPoints->Element( 0 );
  18. Vector vNext;
  19. // Walk around the polygon removing redundant vertices
  20. for ( int i = 0; i < nVertices; ++ i )
  21. {
  22. vNext = pPoints->Element( ( i + 1 ) % nVertices );
  23. // Is the current vertex redundant?
  24. Vector vCurrentToNext = vNext - vCurrent;
  25. Vector vCurrentToPrevious = vPrevious - vCurrent;
  26. // Compute the area of of the error triangle added (if negative) or removed (if positive) with the removal of this vertex
  27. float flAreaDeviation = 0.5f * vCurrentToPrevious.Cross( vCurrentToNext ).Dot( vNormal );
  28. // Only consider a point for removal if it would decrease the polygon area (be conservative)
  29. if ( flAreaDeviation >= 0.0f && flAreaDeviation < flMaximumDeviation )
  30. {
  31. // redundant
  32. pPoints->Remove( i );
  33. -- nVertices;
  34. -- i;
  35. }
  36. else
  37. {
  38. vPrevious = vCurrent;
  39. }
  40. vCurrent = vNext;
  41. }
  42. }
  43. static void ComputeQuadric( const CUtlVector< Vector > &points, int nVertex, const Vector &vNormal, CQuadricError *pError )
  44. {
  45. int nVertices = points.Count();
  46. Vector vPrevious = points[( nVertex + nVertices - 1 ) % nVertices];
  47. Vector vCurrent = points[nVertex];
  48. Vector vNext = points[( nVertex + 1 ) % nVertices];
  49. Vector vAbove = vCurrent + vNormal;
  50. pError->InitFromPlane( vNormal, -DotProduct( vNormal, vCurrent ), 1.0f );
  51. CQuadricError line1, line2;
  52. line1.InitFromTriangle( vPrevious, vCurrent, vAbove, 0.0f );
  53. *pError += line1;
  54. line2.InitFromTriangle( vNext, vCurrent, vAbove, 0.0f );
  55. *pError += line2;
  56. }
  57. void SimplifyPolygonQEM( CUtlVector< Vector > *pPoints, const Vector &vNormal, float flMaximumSquaredError, bool bUseOptimalPointPlacement )
  58. {
  59. if ( pPoints->Count() < 3 )
  60. {
  61. return;
  62. }
  63. CUtlVector< CQuadricError > quadrics;
  64. quadrics.EnsureCapacity( pPoints->Count() );
  65. int nVertices = pPoints->Count();
  66. Vector vPrevious = pPoints->Element( nVertices - 1 );
  67. Vector vCurrent = pPoints->Element( 0 );
  68. Vector vNext;
  69. // Compute quadric error functions for each vertex
  70. for ( int i = 0; i < nVertices; ++ i )
  71. {
  72. vNext = pPoints->Element( ( i + 1 ) % nVertices );
  73. Vector vAbove = vCurrent + vNormal;
  74. quadrics.AddToTail();
  75. quadrics[i].InitFromPlane( vNormal, -DotProduct( vNormal, vCurrent ), 1.0f );
  76. CQuadricError line1, line2;
  77. line1.InitFromTriangle( vPrevious, vCurrent, vAbove, 0.0f );
  78. quadrics[i] += line1;
  79. line2.InitFromTriangle( vNext, vCurrent, vAbove, 0.0f );
  80. quadrics[i] += line2;
  81. vPrevious = vCurrent;
  82. vCurrent = vNext;
  83. }
  84. // @TODO: use a sorted heap instead of pseudo-bubble-sort
  85. // We like quadrilaterals...don't try to go simpler than that
  86. while ( pPoints->Count() > 4 )
  87. {
  88. float flLowestError = flMaximumSquaredError;
  89. Vector vBestCollapse;
  90. int nCollapseIndex = -1;
  91. for ( int i = 0; i < nVertices; ++ i )
  92. {
  93. int nNextIndex = ( i + 1 ) % nVertices;
  94. CQuadricError sum = quadrics[i] + quadrics[nNextIndex];
  95. if ( bUseOptimalPointPlacement )
  96. {
  97. // Solve for optimal point and collapse to it
  98. Vector vOptimalPoint = sum.SolveForMinimumError();
  99. float flError = sum.ComputeError( vOptimalPoint );
  100. if ( flError < flLowestError )
  101. {
  102. flLowestError = flError;
  103. vBestCollapse = vOptimalPoint;
  104. nCollapseIndex = i;
  105. }
  106. }
  107. else
  108. {
  109. // Only collapse to endpoints
  110. Vector vA = pPoints->Element( i );
  111. float flErrorA = sum.ComputeError( vA );
  112. if ( flErrorA < flLowestError )
  113. {
  114. flLowestError = flErrorA;
  115. vBestCollapse = vA;
  116. nCollapseIndex = i;
  117. }
  118. Vector vB = pPoints->Element( nNextIndex );
  119. float flErrorB = sum.ComputeError( vB );
  120. if ( flErrorB < flLowestError )
  121. {
  122. flLowestError = flErrorB;
  123. vBestCollapse = vB;
  124. nCollapseIndex = i;
  125. }
  126. }
  127. }
  128. if ( nCollapseIndex != -1 )
  129. {
  130. pPoints->Element( nCollapseIndex ) = vBestCollapse;
  131. int nNextIndex = ( nCollapseIndex + 1 ) % nVertices;
  132. pPoints->Remove( nNextIndex );
  133. quadrics.Remove( nNextIndex );
  134. -- nVertices;
  135. if ( nNextIndex < nCollapseIndex )
  136. -- nCollapseIndex;
  137. int nPrevIndex = ( nCollapseIndex + nVertices - 1 ) % nVertices;
  138. nNextIndex = ( nCollapseIndex + 1 ) % nVertices;
  139. ComputeQuadric( *pPoints, nPrevIndex, vNormal, &quadrics[nPrevIndex] );
  140. ComputeQuadric( *pPoints, nCollapseIndex, vNormal, &quadrics[nCollapseIndex] );
  141. ComputeQuadric( *pPoints, nNextIndex, vNormal, &quadrics[nNextIndex] );
  142. }
  143. else
  144. {
  145. // we're done
  146. break;
  147. }
  148. }
  149. }
  150. bool IsConcave( const Vector &v0, const Vector &v1, const Vector &v2, const Vector &vNormal )
  151. {
  152. Vector vRay1 = v2 - v1;
  153. Vector vRay2 = v0 - v1;
  154. float flSign = vRay2.Cross( vRay1 ).Dot( vNormal );
  155. return ( flSign < 0 );
  156. }
  157. bool IsConcave( const Vector *pPolygonPoints, int nPointCount, int nVertex, const Vector &vNormal )
  158. {
  159. Assert( nPointCount >= 3 );
  160. int nPrevVertex = ( nVertex + nPointCount - 1 ) % nPointCount;
  161. int nNextVertex = ( nVertex + 1 ) % nPointCount;
  162. return IsConcave( pPolygonPoints[nPrevVertex], pPolygonPoints[nVertex], pPolygonPoints[nNextVertex], vNormal );
  163. }
  164. bool IsConcave( const Vector *pPolygonPoints, int nPointCount, const Vector &vNormal )
  165. {
  166. Assert( nPointCount >= 3 );
  167. Vector vPrevVertex = pPolygonPoints[nPointCount - 2];
  168. Vector vVertex = pPolygonPoints[nPointCount - 1];
  169. Vector vNextVertex;
  170. for ( int i = 0; i < nPointCount; ++ i )
  171. {
  172. vNextVertex = pPolygonPoints[i];
  173. if ( IsConcave( vPrevVertex, vVertex, vNextVertex, vNormal ) )
  174. {
  175. return true;
  176. }
  177. vPrevVertex = vVertex;
  178. vVertex = vNextVertex;
  179. }
  180. return false;
  181. }
  182. static bool IsPointInPolygon( const CUtlVector< Vector > &polygonPoints, const SubPolygon_t &convexRegion, const Vector &vNormal, const Vector &vPoint )
  183. {
  184. int nIndex = convexRegion.m_Indices[convexRegion.m_Indices.Count() - 1];
  185. Vector v0 = SubPolygon_t::GetPoint( polygonPoints, nIndex );
  186. for ( int i = 0; i < convexRegion.m_Indices.Count(); ++ i )
  187. {
  188. nIndex = convexRegion.m_Indices[i];
  189. Vector v1 = SubPolygon_t::GetPoint( polygonPoints, nIndex );
  190. Vector vRay = v1 - v0;
  191. Vector vRight = vRay.Cross( vNormal );
  192. if ( vRight.Dot( vPoint - v0 ) < -POINT_IN_POLYGON_EPSILON )
  193. {
  194. return false;
  195. }
  196. v0 = v1;
  197. }
  198. return true;
  199. }
  200. static bool PointsInsideConvexArea( const CUtlVector< Vector > &polygonPoints, const SubPolygon_t &originalPolygon, const Vector &vNormal, int nFirstIndex, int nLastIndex, const SubPolygon_t &convexRegion )
  201. {
  202. nFirstIndex %= originalPolygon.m_Indices.Count();
  203. nLastIndex %= originalPolygon.m_Indices.Count();
  204. if ( nFirstIndex < 0 ) nFirstIndex += originalPolygon.m_Indices.Count();
  205. if ( nLastIndex < 0 ) nLastIndex += originalPolygon.m_Indices.Count();
  206. for ( int i = nFirstIndex; i != nLastIndex; i = ( i + 1 ) % originalPolygon.m_Indices.Count() )
  207. {
  208. int nVertex = originalPolygon.GetVertexIndex( i );
  209. Vector vPoint = SubPolygon_t::GetPoint( polygonPoints, nVertex );
  210. if ( IsPointInPolygon( polygonPoints, convexRegion, vNormal, vPoint ) )
  211. {
  212. bool bIsDoubleVertex = false;
  213. // Allow points on the boundary of the convex region if they are coincident with one of the points of the region itself
  214. for ( int j = 0; j < convexRegion.m_Indices.Count(); ++ j )
  215. {
  216. Vector vConvexRegionTestPoint = SubPolygon_t::GetPoint( polygonPoints, convexRegion.GetVertexIndex( j ) );
  217. if ( VectorsAreEqual( vConvexRegionTestPoint, vPoint, POINT_IN_POLYGON_EPSILON ) )
  218. {
  219. bIsDoubleVertex = true;
  220. break;
  221. }
  222. }
  223. if ( !bIsDoubleVertex )
  224. {
  225. return true;
  226. }
  227. }
  228. }
  229. return false;
  230. }
  231. static const float LINE_INTERSECT_EPSILON = 1e-3f;
  232. // superceded by CalcLineToLineIntersectionSegment
  233. // bool LineSegmentsIntersect( const Vector &vNormal, const Vector &v1a, const Vector &v1b, const Vector &v2a, const Vector &v2b, float flEpsilon, float *pTimeOfIntersection1, float *pTimeOfIntersection2 )
  234. // {
  235. // Vector vDir1 = v1b - v1a;
  236. // Vector vDir2 = v2b - v2a;
  237. // Vector v1Perpendicular = vDir1.Cross( vNormal );
  238. // Vector vStartDiff = v1a - v2a;
  239. // float flNumerator = vStartDiff.Dot( v1Perpendicular );
  240. // float flDenominator = vDir2.Dot( v1Perpendicular );
  241. // // @TODO: we should probably use a different epsilon since the denominator is in different units, but this works well so far
  242. // if ( fabsf( flDenominator ) < flEpsilon )
  243. // {
  244. // return false;
  245. // }
  246. // float t2 = flNumerator / flDenominator;
  247. // if ( t2 >= -flEpsilon && t2 <= ( 1.0f + flEpsilon ) )
  248. // {
  249. // Vector vClosestPoint2 = t2 * vDir2 + v2a;
  250. // float flLength1 = vDir1.NormalizeInPlace();
  251. // // Can't be 0 otherwise flDenominator would have been 0
  252. // float t1 = ( vClosestPoint2 - v1a ).Dot( vDir1 ) / flLength1;
  253. //
  254. // if ( t1 >= -flEpsilon && t1 <= ( 1.0f + flEpsilon ) )
  255. // {
  256. // *pTimeOfIntersection1 = t1;
  257. // *pTimeOfIntersection2 = t2;
  258. // return true;
  259. // }
  260. // }
  261. // return false;
  262. // }
  263. //-----------------------------------------------------------------------------
  264. // Note: this is not a general purpose intersection test;
  265. // it makes some assumptions that the winding of the polygon is
  266. // counter-clockwise (because it's expected to be a hole) and that,
  267. // if the line segment vA-vB intersects a line segment in the hole
  268. // (denoted by vHoleA-vHoleB, in counter-clockwise ordering),
  269. // then the line segment vA-vHoleB must not intersect any other part of
  270. // the hole polygon.
  271. //-----------------------------------------------------------------------------
  272. static bool LineSegmentIntersectsPolygon( const CUtlVector< Vector > &polygonPoints, const Vector &vNormal, const Vector &vA, const Vector &vB, const SubPolygon_t &hole, float *pLowestTimeOfIntersection, Vector *pA, Vector *pB, int *pHoleVertexIndex )
  273. {
  274. *pLowestTimeOfIntersection = 2.0f; // a valid time is between 0 and 1, anything outside the range is considered invalid
  275. float flTimeOfIntersection;
  276. Vector vPrev = SubPolygon_t::GetPoint( polygonPoints, hole.m_Indices[hole.m_Indices.Count() - 1] );
  277. bool bIntersect = false;
  278. Vector vInside = ( vB - vA ).Cross( vNormal );
  279. for ( int i = 0; i < hole.m_Indices.Count(); ++ i )
  280. {
  281. float flOtherTimeOfIntersection;
  282. Vector vCurrent = SubPolygon_t::GetPoint( polygonPoints, hole.m_Indices[i] );
  283. // @TODO: make sure the replacement is ok before deleting
  284. //if ( LineSegmentsIntersect( vNormal, vA, vB, vPrev, vCurrent, &flTimeOfIntersection, &flOtherTimeOfIntersection ) )
  285. CalcLineToLineIntersectionSegment( vA, vB, vPrev, vCurrent, NULL, NULL, &flTimeOfIntersection, &flOtherTimeOfIntersection );
  286. if ( flTimeOfIntersection >= -LINE_INTERSECT_EPSILON && flTimeOfIntersection <= 1.0f + LINE_INTERSECT_EPSILON && flOtherTimeOfIntersection >= -LINE_INTERSECT_EPSILON && flTimeOfIntersection <= 1.0f + LINE_INTERSECT_EPSILON )
  287. {
  288. // If the line segment intersection occurs right at the beginning of the hole line segment, ignore it because we'll catch it as an intersection at the end of
  289. // another hole line segment.
  290. // This is required because we want to guarantee that a line segment from vA to vCurrent does not intersect the polygon at any point.
  291. if ( flTimeOfIntersection < *pLowestTimeOfIntersection && flOtherTimeOfIntersection > LINE_INTERSECT_EPSILON )
  292. {
  293. *pLowestTimeOfIntersection = flTimeOfIntersection;
  294. *pA = vPrev;
  295. *pB = vCurrent;
  296. float flPrevInsideDistance = ( vPrev - vA ).Dot( vInside );
  297. float flCurrentInsideDistance = ( vCurrent - vA ).Dot( vInside );
  298. if ( flCurrentInsideDistance > flPrevInsideDistance )
  299. {
  300. *pHoleVertexIndex = i;
  301. }
  302. else
  303. {
  304. *pHoleVertexIndex = ( i + hole.m_Indices.Count() - 1 ) % hole.m_Indices.Count();
  305. }
  306. bIntersect = true;
  307. }
  308. }
  309. vPrev = vCurrent;
  310. }
  311. return bIntersect;
  312. }
  313. void FindLineSegmentIntersectingDiagonal( const CUtlVector< Vector > &polygonPoints, const Vector &vNormal, const CUtlVector< SubPolygon_t > &holes, const Vector &vA, const Vector &vB, Vector *pHoleSegmentA, Vector *pHoleSegmentB, int *pHoleIndex, int *pHoleVertexIndex )
  314. {
  315. float flLowestTimeOfIntersection = 2.0f;
  316. int nHoleVertexIndex;
  317. *pHoleIndex = -1;
  318. // Test for holes
  319. for ( int i = 0; i < holes.Count(); ++ i )
  320. {
  321. float flTimeOfIntersection;
  322. Vector vTempA, vTempB;
  323. if ( LineSegmentIntersectsPolygon( polygonPoints, vNormal, vA, vB, holes[i], &flTimeOfIntersection, &vTempA, &vTempB, &nHoleVertexIndex ) )
  324. {
  325. if ( flTimeOfIntersection < flLowestTimeOfIntersection )
  326. {
  327. flLowestTimeOfIntersection = flTimeOfIntersection;
  328. *pHoleSegmentA = vTempA;
  329. *pHoleSegmentB = vTempB;
  330. *pHoleIndex = i;
  331. *pHoleVertexIndex = nHoleVertexIndex;
  332. }
  333. }
  334. }
  335. }
  336. void DecomposePolygon_Step( const CUtlVector< Vector > &polygonPoints, const Vector &vNormal, CUtlVector< SubPolygon_t > *pHoles, SubPolygon_t *pNewPartition, SubPolygon_t *pOriginalPolygon, int *pFirstIndex )
  337. {
  338. Assert( *pFirstIndex >= 0 && *pFirstIndex < pOriginalPolygon->m_Indices.Count() );
  339. // Always start decomposition on a notch
  340. Vector vPrev = SubPolygon_t::GetPoint( polygonPoints, pOriginalPolygon->GetVertexIndex( *pFirstIndex - 1 ) );
  341. Vector vCurrent = SubPolygon_t::GetPoint( polygonPoints, pOriginalPolygon->GetVertexIndex( *pFirstIndex ) );
  342. Vector vNext = SubPolygon_t::GetPoint( polygonPoints, pOriginalPolygon->GetVertexIndex( *pFirstIndex + 1 ) );
  343. for ( int i = 0; i < pOriginalPolygon->m_Indices.Count(); ++ i )
  344. {
  345. if ( IsConcave( vPrev, vCurrent, vNext, vNormal) )
  346. {
  347. *pFirstIndex = ( *pFirstIndex + i ) % pOriginalPolygon->m_Indices.Count();
  348. break;
  349. }
  350. else
  351. {
  352. vPrev = vCurrent;
  353. vCurrent = vNext;
  354. vNext = SubPolygon_t::GetPoint( polygonPoints, pOriginalPolygon->GetVertexIndex( *pFirstIndex + i + 2 ) );
  355. }
  356. }
  357. // On termination of the loop, pOriginalPolygon->m_Indices[*pFirstIndex] is the vertex index (in polygonPoints) from
  358. // which to begin decomposition
  359. // Attempt decomposition (first clockwise, then counter-clockwise if that fails)
  360. for ( int i = 0; i < 2; ++ i )
  361. {
  362. bool bClockwise = ( i == 0 );
  363. pNewPartition->m_Indices.RemoveAll();
  364. // Grab the first 2 vertices from the remaining polygon and add to the new potential convex partition
  365. int nFirstVertex = pOriginalPolygon->GetVertexIndex( *pFirstIndex );
  366. int nSecondVertex = pOriginalPolygon->GetVertexIndex( *pFirstIndex + ( bClockwise ? 1 : -1 ) );
  367. if ( bClockwise )
  368. {
  369. pNewPartition->m_Indices.AddToTail( nFirstVertex );
  370. pNewPartition->m_Indices.AddToTail( nSecondVertex );
  371. }
  372. else
  373. {
  374. pNewPartition->m_Indices.AddToTail( nSecondVertex );
  375. pNewPartition->m_Indices.AddToTail( nFirstVertex );
  376. }
  377. Vector vFirst = SubPolygon_t::GetPoint( polygonPoints, nFirstVertex );
  378. Vector vSecond = SubPolygon_t::GetPoint( polygonPoints, nSecondVertex );
  379. Vector vPrevPrev = vFirst;
  380. vPrev = vSecond;
  381. int nNextIndex = *pFirstIndex + ( bClockwise ? 2 : -2 );
  382. int nNextVertex = pOriginalPolygon->GetVertexIndex( nNextIndex );
  383. // At the start of each iteration, *pFirstIndex refers to the index of the first vertex from the original polygon that is in the partition
  384. // and nNextIndex refers to 1 past the index of the last vertex from the original polygon that is in the partition.
  385. // If clockwise, you can find the new convex partition by iterating indices in original polygon from [*pFirstIndex, nNextIndex-1],
  386. // if counter-clockwise, iterate from [nNextIndex+1, *pFirstIndex].
  387. while ( pNewPartition->m_Indices.Count() < pOriginalPolygon->m_Indices.Count() )
  388. {
  389. vCurrent = SubPolygon_t::GetPoint( polygonPoints, nNextVertex );
  390. bool bConcave;
  391. if ( bClockwise )
  392. {
  393. bConcave = IsConcave( vPrevPrev, vPrev, vCurrent, vNormal ) || IsConcave( vPrev, vCurrent, vFirst, vNormal ) || IsConcave( vCurrent, vFirst, vSecond, vNormal );
  394. }
  395. else
  396. {
  397. bConcave = IsConcave( vCurrent, vPrev, vPrevPrev, vNormal ) || IsConcave( vFirst, vCurrent, vPrev, vNormal ) || IsConcave( vSecond, vFirst, vCurrent, vNormal );
  398. }
  399. if ( bConcave )
  400. {
  401. // Shape is no longer convex with the addition of vCurrent
  402. break;
  403. }
  404. else
  405. {
  406. if ( bClockwise )
  407. {
  408. pNewPartition->m_Indices.AddToTail( nNextVertex );
  409. ++ nNextIndex;
  410. }
  411. else
  412. {
  413. pNewPartition->m_Indices.AddToHead( nNextVertex );
  414. -- nNextIndex;
  415. }
  416. nNextVertex = pOriginalPolygon->GetVertexIndex( nNextIndex );
  417. vPrevPrev = vPrev;
  418. vPrev = vCurrent;
  419. }
  420. }
  421. int nFirstIndexInRemainingPolygon = bClockwise ? nNextIndex : *pFirstIndex + 1;
  422. int nLastIndexInRemainingPolygon = bClockwise ? *pFirstIndex : nNextIndex + 1;
  423. // Test to see if any points in the remaining polygon are within the bounds of the convex polygon we're about to peel off.
  424. while ( pNewPartition->m_Indices.Count() >= 3 && PointsInsideConvexArea( polygonPoints, *pOriginalPolygon, vNormal, nFirstIndexInRemainingPolygon, nLastIndexInRemainingPolygon, *pNewPartition ) )
  425. {
  426. if ( bClockwise )
  427. {
  428. pNewPartition->m_Indices.RemoveMultipleFromTail( 1 );
  429. -- nNextIndex;
  430. }
  431. else
  432. {
  433. pNewPartition->m_Indices.RemoveMultipleFromHead( 1 );
  434. ++ nNextIndex;
  435. }
  436. }
  437. // Found a convex chunk of the original concave polygon, now check for
  438. // holes or concavities which intersect this convex chunk.
  439. if ( pNewPartition->m_Indices.Count() >= 3 )
  440. {
  441. Vector vA = SubPolygon_t::GetPoint( polygonPoints, pNewPartition->m_Indices[pNewPartition->m_Indices.Count() - 1] );
  442. Vector vB = SubPolygon_t::GetPoint( polygonPoints, pNewPartition->m_Indices[0] );
  443. Vector vHoleSegmentA, vHoleSegmentB;
  444. int nHoleIndex = -1;
  445. int nLastHoleIndex;
  446. int nHoleVertexIndex;
  447. // See if the diagonal which closes this convex region intersects any holes
  448. FindLineSegmentIntersectingDiagonal( polygonPoints, vNormal, *pHoles, vA, vB, &vHoleSegmentA, &vHoleSegmentB, &nHoleIndex, &nHoleVertexIndex );
  449. // If there was an intersection, keep refining the diagonal until it no longer changes
  450. if ( nHoleIndex != -1 )
  451. {
  452. do
  453. {
  454. nLastHoleIndex = nHoleIndex;
  455. vB = SubPolygon_t::GetPoint( polygonPoints, pHoles->Element( nHoleIndex ).GetVertexIndex( nHoleVertexIndex ) );
  456. FindLineSegmentIntersectingDiagonal( polygonPoints, vNormal, *pHoles, vA, vB, &vHoleSegmentA, &vHoleSegmentB, &nHoleIndex, &nHoleVertexIndex );
  457. Assert( nHoleIndex != -1 );
  458. } while ( nHoleIndex != nLastHoleIndex );
  459. }
  460. // If there was no intersection, check to see if this convex region completely encloses any holes
  461. if ( nHoleIndex == -1 )
  462. {
  463. Vector vHolePoint;
  464. int nHoleInRegionIndex = -1;
  465. for ( int i = 0; i < pHoles->Count(); ++ i )
  466. {
  467. vHolePoint = SubPolygon_t::GetPoint( polygonPoints, pHoles->Element( i ).m_Indices[0] );
  468. if ( IsPointInPolygon( polygonPoints, *pNewPartition, vNormal, vHolePoint ) )
  469. {
  470. // hole is within the region
  471. nHoleInRegionIndex = i;
  472. break;
  473. }
  474. }
  475. if ( nHoleInRegionIndex != -1 )
  476. {
  477. // If any holes are enclosed, "fix" the diagonal to connect to an arbitrary vertex on one of the enclosed holes
  478. vB = vHolePoint;
  479. // Now test to see if this new diagonal intersects any holes
  480. FindLineSegmentIntersectingDiagonal( polygonPoints, vNormal, *pHoles, vA, vB, &vHoleSegmentA, &vHoleSegmentB, &nHoleIndex, &nHoleVertexIndex );
  481. // If there was an intersection, keep refining the diagonal until it no longer changes
  482. if ( nHoleIndex != -1 )
  483. {
  484. do
  485. {
  486. nLastHoleIndex = nHoleIndex;
  487. vB = SubPolygon_t::GetPoint( polygonPoints, pHoles->Element( nHoleIndex ).GetVertexIndex( nHoleVertexIndex ) );
  488. FindLineSegmentIntersectingDiagonal( polygonPoints, vNormal, *pHoles, vA, vB, &vHoleSegmentA, &vHoleSegmentB, &nHoleIndex, &nHoleVertexIndex );
  489. Assert( nHoleIndex != -1 );
  490. } while ( nHoleIndex != nLastHoleIndex );
  491. }
  492. }
  493. }
  494. // At this point, we should either have the original convex region which contains no holes and intersects with nothing,
  495. // or a refined diagonal which connects to a hole without intersecting any other holes or convex region points
  496. if ( nHoleIndex != -1 )
  497. {
  498. // We have a refined diagonal; absorb the hole into the original polygon.
  499. // Reject this partition but add the hole's vertices to the original polygon.
  500. int nInsertAfterIndex = ( bClockwise ? nNextIndex + pOriginalPolygon->m_Indices.Count() - 1 : *pFirstIndex ) % pOriginalPolygon->m_Indices.Count();
  501. const SubPolygon_t *pHolePolygon = &pHoles->Element( nHoleIndex );
  502. int nConnectBackToVertex = pOriginalPolygon->m_Indices[nInsertAfterIndex];
  503. for ( int i = 0; i < pHolePolygon->m_Indices.Count(); ++ i )
  504. {
  505. pOriginalPolygon->m_Indices.InsertAfter( nInsertAfterIndex, pHolePolygon->m_Indices[( i + nHoleVertexIndex ) % pHolePolygon->m_Indices.Count()] );
  506. ++ nInsertAfterIndex;
  507. }
  508. pOriginalPolygon->m_Indices.InsertAfter( nInsertAfterIndex, pHolePolygon->m_Indices[nHoleVertexIndex] );
  509. ++ nInsertAfterIndex;
  510. pOriginalPolygon->m_Indices.InsertAfter( nInsertAfterIndex, nConnectBackToVertex );
  511. pNewPartition->m_Indices.RemoveAll();
  512. pHoles->Remove( nHoleIndex );
  513. }
  514. else
  515. {
  516. // We have the original, valid diagonal.
  517. // Remove the corresponding indices from the original polygon to peel off the new convex region
  518. int nIndexToRemove = ( ( bClockwise ? *pFirstIndex : nNextIndex + 1 ) + 1 ) % pOriginalPolygon->m_Indices.Count();
  519. if ( nIndexToRemove < 0 ) nIndexToRemove += pOriginalPolygon->m_Indices.Count();
  520. for ( int i = 1; i < pNewPartition->m_Indices.Count() - 1; ++ i )
  521. {
  522. nIndexToRemove = nIndexToRemove % pOriginalPolygon->m_Indices.Count();
  523. Assert( pOriginalPolygon->m_Indices[nIndexToRemove] == pNewPartition->m_Indices[i] );
  524. pOriginalPolygon->m_Indices.Remove( nIndexToRemove );
  525. }
  526. *pFirstIndex = nIndexToRemove % pOriginalPolygon->m_Indices.Count();
  527. }
  528. // Done!
  529. return;
  530. }
  531. }
  532. // Couldn't find a match either clockwise or counter-clockwise
  533. *pFirstIndex = ( *pFirstIndex + 1 ) % pOriginalPolygon->m_Indices.Count();
  534. }
  535. void DecomposePolygon( const CUtlVector< Vector > &polygonPoints, const Vector &vNormal, SubPolygon_t *pOriginalPolygon, CUtlVector< SubPolygon_t > *pHoles, CUtlVector< SubPolygon_t > *pPartitions )
  536. {
  537. int nFirstIndex = 0; // The Nth vertex in the remaining polygon
  538. SubPolygon_t *pNewPartition = NULL;
  539. while ( pOriginalPolygon->m_Indices.Count() >= 3 )
  540. {
  541. if ( !pNewPartition )
  542. {
  543. pNewPartition = &pPartitions->Element( pPartitions->AddToTail() );
  544. }
  545. DecomposePolygon_Step( polygonPoints, vNormal, pHoles, pNewPartition, pOriginalPolygon, &nFirstIndex );
  546. if ( pNewPartition->m_Indices.Count() >= 3 )
  547. {
  548. pNewPartition = NULL;
  549. }
  550. else
  551. {
  552. pNewPartition->m_Indices.RemoveAll();
  553. }
  554. }
  555. }
  556. bool IsPointInPolygonPrism( const Vector *pPolygonPoints, int nPointCount, const Vector &vPoint, float flThreshold, float *pHeight )
  557. {
  558. Assert( nPointCount >= 3 );
  559. Vector vNormal = ( pPolygonPoints[0] - pPolygonPoints[1] ).Cross( pPolygonPoints[2] - pPolygonPoints[1] );
  560. Vector vPrev = pPolygonPoints[nPointCount - 1];
  561. for ( int nVertexIndex = 0; nVertexIndex < nPointCount; ++ nVertexIndex )
  562. {
  563. Vector vCurrent = pPolygonPoints[nVertexIndex];
  564. Vector vAbove = vPrev + vNormal;
  565. Vector vOutwardPlaneNormal = ( vPrev - vCurrent ).Cross( vAbove - vCurrent );
  566. if ( ( vPoint - vCurrent ).Dot( vOutwardPlaneNormal ) > flThreshold )
  567. {
  568. // Outside the prism
  569. return false;
  570. }
  571. vPrev = vCurrent;
  572. }
  573. if ( pHeight != NULL )
  574. {
  575. vNormal.NormalizeInPlace();
  576. *pHeight = ( vPoint - pPolygonPoints[0] ).Dot( vNormal );
  577. }
  578. return true;
  579. }