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.

273 lines
8.6 KiB

  1. //=========== Copyright � Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Mesh clipping operations.
  4. //
  5. //===========================================================================//
  6. #include "mesh.h"
  7. #include "tier1/utlbuffer.h"
  8. void ClipTriangle( float *pBackOut, float *pFrontOut, int *pNumBackOut, int *pNumFrontOut, int nStrideFloats,
  9. float **ppVertsIn, Vector4D &vClipPlane )
  10. {
  11. int nBack = 0;
  12. int nFront = 0;
  13. Vector vPlaneNormal = vClipPlane.AsVector3D();
  14. const int nVerts = 3;
  15. for( int v=0; v<nVerts; ++v )
  16. {
  17. int nxtVert = ( v + 1 ) % nVerts;
  18. Vector vPos1 = *(Vector*)ppVertsIn[ v ];
  19. Vector vPos2 = *(Vector*)ppVertsIn[ nxtVert ];
  20. float flDot1 = DotProduct( vPos1, vPlaneNormal ) + vClipPlane.w;
  21. float flDot2 = DotProduct( vPos2, vPlaneNormal ) + vClipPlane.w;
  22. // Enforce that points that lie perfectly on the plane always go to the front
  23. if ( flDot1 == 0.0f )
  24. {
  25. flDot1 = 0.01f;
  26. }
  27. if ( flDot2 == 0.0f )
  28. {
  29. flDot2 = 0.01f;
  30. }
  31. if ( flDot1 < 0 )
  32. {
  33. CopyVertex( pBackOut + nBack * nStrideFloats, ppVertsIn[ v ], nStrideFloats );
  34. nBack ++;
  35. }
  36. else
  37. {
  38. CopyVertex( pFrontOut + nFront * nStrideFloats, ppVertsIn[ v ], nStrideFloats );
  39. nFront ++;
  40. }
  41. if ( flDot1 * flDot2 < 0 )
  42. {
  43. // Lerp verts
  44. float flLerp = -flDot1 / ( flDot2 - flDot1 );
  45. LerpVertex( pBackOut + nBack * nStrideFloats, ppVertsIn[ v ], ppVertsIn[ nxtVert ], flLerp, nStrideFloats );
  46. CopyVertex( pFrontOut + nFront * nStrideFloats, pBackOut + nBack * nStrideFloats, nStrideFloats );
  47. nBack ++;
  48. nFront++;
  49. }
  50. }
  51. *pNumBackOut = nBack;
  52. *pNumFrontOut = nFront;
  53. }
  54. // Clips a mesh against a plane and returns 2 meshes, one on each side of the plane. The caller must
  55. // check pMeshBack and pMeshFront for empty vertex or index sets implying that the mesh was entirely
  56. // on one side of the plane or the other.
  57. bool ClipMeshToHalfSpace( CMesh *pMeshBack, CMesh *pMeshFront, const CMesh &inputMesh, Vector4D &vClipPlane )
  58. {
  59. Assert( pMeshBack || pMeshFront );
  60. // NOTE: This assumes that position is the FIRST float3 in the buffer
  61. int nPosOffset = inputMesh.FindFirstAttributeOffset( VERTEX_ELEMENT_POSITION );
  62. if ( nPosOffset != 0 )
  63. return false;
  64. int nVertexStrideFloats = inputMesh.m_nVertexStrideFloats;
  65. int nIndexCount = inputMesh.m_nIndexCount;
  66. uint32 *pIndices = inputMesh.m_pIndices;
  67. float *pVertices = inputMesh.m_pVerts;
  68. // Allocate working space for the number of vertices out on each side of the plane.
  69. // This is a maximum of 4 vertices out when clipping a triangle to a plane.
  70. float *pBackOut = new float[ nVertexStrideFloats * 4 ];
  71. float *pFrontOut = new float[ nVertexStrideFloats * 4 ];
  72. CUtlBuffer backMeshVerts;
  73. CUtlBuffer frontMeshVerts;
  74. for ( int i=0; i<nIndexCount; i += 3 )
  75. {
  76. float *ppVerts[3];
  77. int nIndex0 = pIndices[ i ];
  78. int nIndex1 = pIndices[ i + 1 ];
  79. int nIndex2 = pIndices[ i + 2 ];
  80. ppVerts[0] = pVertices + nIndex0 * nVertexStrideFloats;
  81. ppVerts[1] = pVertices + nIndex1 * nVertexStrideFloats;
  82. ppVerts[2] = pVertices + nIndex2 * nVertexStrideFloats;
  83. int nBackOut = 0;
  84. int nFrontOut = 0;
  85. ClipTriangle( pBackOut, pFrontOut, &nBackOut, &nFrontOut, nVertexStrideFloats, ppVerts, vClipPlane );
  86. // reconstruct triangles out of the polygon
  87. int numBackTris = nBackOut - 2;
  88. for ( int t=0; t<numBackTris; ++t )
  89. {
  90. backMeshVerts.Put( pBackOut, nVertexStrideFloats * sizeof( float ) );
  91. backMeshVerts.Put( pBackOut + nVertexStrideFloats * ( t + 1 ), nVertexStrideFloats * sizeof( float ) );
  92. backMeshVerts.Put( pBackOut + nVertexStrideFloats * ( t + 2 ), nVertexStrideFloats * sizeof( float ) );
  93. }
  94. int numFrontTris = nFrontOut - 2;
  95. for ( int t=0; t<numFrontTris; ++t )
  96. {
  97. frontMeshVerts.Put( pFrontOut, nVertexStrideFloats * sizeof( float ) );
  98. frontMeshVerts.Put( pFrontOut + nVertexStrideFloats * ( t + 1 ), nVertexStrideFloats * sizeof( float ) );
  99. frontMeshVerts.Put( pFrontOut + nVertexStrideFloats * ( t + 2 ), nVertexStrideFloats * sizeof( float ) );
  100. }
  101. }
  102. delete []pBackOut;
  103. delete []pFrontOut;
  104. // Turn the utlbuffers into actual meshes
  105. if ( pMeshBack )
  106. {
  107. pMeshBack->m_nVertexCount = backMeshVerts.TellPut() / ( nVertexStrideFloats * sizeof( float ) );
  108. if ( pMeshBack->m_nVertexCount )
  109. {
  110. pMeshBack->AllocateMesh( pMeshBack->m_nVertexCount, pMeshBack->m_nVertexCount, nVertexStrideFloats, inputMesh.m_pAttributes, inputMesh.m_nAttributeCount );
  111. Q_memcpy( pMeshBack->m_pVerts, backMeshVerts.Base(), backMeshVerts.TellPut() );
  112. for ( int i=0; i<pMeshBack->m_nIndexCount; ++i )
  113. {
  114. pMeshBack->m_pIndices[i] = i;
  115. }
  116. }
  117. }
  118. if ( pMeshFront )
  119. {
  120. pMeshFront->m_nVertexCount = frontMeshVerts.TellPut() / ( nVertexStrideFloats * sizeof( float ) );
  121. if ( pMeshFront->m_nVertexCount )
  122. {
  123. pMeshFront->AllocateMesh( pMeshFront->m_nVertexCount, pMeshFront->m_nVertexCount, nVertexStrideFloats, inputMesh.m_pAttributes, inputMesh.m_nAttributeCount );
  124. Q_memcpy( pMeshFront->m_pVerts, frontMeshVerts.Base(), frontMeshVerts.TellPut() );
  125. for ( int i=0; i<pMeshFront->m_nIndexCount; ++i )
  126. {
  127. pMeshFront->m_pIndices[i] = i;
  128. }
  129. }
  130. }
  131. return true;
  132. }
  133. //--------------------------------------------------------------------------------------
  134. void CreateGridCellsForVolume( CUtlVector< GridVolume_t > &outputVolumes, const Vector &vTotalMinBounds, const Vector &vTotalMaxBounds, const Vector &vGridSize )
  135. {
  136. // First, determine if we need to be split
  137. Vector vDelta = vTotalMaxBounds - vTotalMinBounds;
  138. int nX = vDelta.x / vGridSize.x;
  139. int nY = vDelta.y / vGridSize.y;
  140. int nZ = vDelta.z / vGridSize.z;
  141. Vector vEpsilon( 0.1f, 0.1f, 0.1f );
  142. Vector vMinBounds = vTotalMinBounds - vEpsilon;
  143. Vector vMaxBounds = vTotalMaxBounds + vEpsilon;
  144. if ( nX * nY * nZ < 2 )
  145. {
  146. GridVolume_t newbounds;
  147. newbounds.m_vMinBounds = vMinBounds;
  148. newbounds.m_vMaxBounds = vMaxBounds;
  149. outputVolumes.AddToTail( newbounds );
  150. }
  151. else
  152. {
  153. Vector vStep;
  154. vStep.z = vDelta.z / nZ;
  155. vStep.y = vDelta.y / nY;
  156. vStep.x = vDelta.x / nX;
  157. // Create the split volumes
  158. outputVolumes.EnsureCount( nX * nY * nZ );
  159. int nVolumes = 0;
  160. Vector vStart = vMinBounds;
  161. for ( int z=0; z<nZ; ++z )
  162. {
  163. vStart.y = vMinBounds.y;
  164. for ( int y=0; y<nY; ++y )
  165. {
  166. vStart.x = vMinBounds.x;
  167. for ( int x=0; x<nX; ++x )
  168. {
  169. GridVolume_t newbounds;
  170. newbounds.m_vMinBounds = vStart;
  171. newbounds.m_vMaxBounds = vStart + vStep;
  172. if ( x == nX - 1 )
  173. newbounds.m_vMaxBounds.x = vMaxBounds.x;
  174. if ( y == nY - 1 )
  175. newbounds.m_vMaxBounds.y = vMaxBounds.y;
  176. if ( z == nZ - 1 )
  177. newbounds.m_vMaxBounds.z = vMaxBounds.z;
  178. outputVolumes[ nVolumes ] = newbounds;
  179. nVolumes ++;
  180. vStart.x += vStep.x;
  181. }
  182. vStart.y += vStep.y;
  183. }
  184. vStart.z += vStep.z;
  185. }
  186. }
  187. }
  188. //--------------------------------------------------------------------------------------
  189. // For a list of AABBs, find all indices in the mesh that belong to each AABB. Then
  190. // coalesce those indices into a single buffer in order of the input AABBs and return a
  191. // list of ranges of the indices in the output mesh that are needed in each input volume
  192. //--------------------------------------------------------------------------------------
  193. void CreatedGriddedIndexRangesFromMesh( CMesh *pOutputMesh, CUtlVector< IndexRange_t > &outputRanges, const CMesh &inputMesh, CUtlVector< GridVolume_t > &inputVolumes )
  194. {
  195. DuplicateMesh( pOutputMesh, inputMesh );
  196. int nVolumes = inputVolumes.Count();
  197. outputRanges.EnsureCount( nVolumes );
  198. int nFaces = inputMesh.m_nIndexCount / 3;
  199. uint32 *pInputIndices = inputMesh.m_pIndices;
  200. uint32 *pOutputIndices = pOutputMesh->m_pIndices;
  201. int nMeshIndices = 0;
  202. // Go though each volume and assign indices to its respective range
  203. for ( int v=0; v<nVolumes; ++v )
  204. {
  205. GridVolume_t &volume = inputVolumes[ v ];
  206. IndexRange_t &range = outputRanges[ v ];
  207. range.m_nStartIndex = nMeshIndices;
  208. for ( int f=0; f<nFaces; ++f )
  209. {
  210. uint32 i0 = pInputIndices[ f * 3 ];
  211. uint32 i1 = pInputIndices[ f * 3 + 1 ];
  212. uint32 i2 = pInputIndices[ f * 3 + 2 ];
  213. Vector vCenter = *(Vector*)inputMesh.GetVertex( i0 );
  214. vCenter += *(Vector*)inputMesh.GetVertex( i1 );
  215. vCenter += *(Vector*)inputMesh.GetVertex( i2 );
  216. vCenter /= 3.0f;
  217. if ( vCenter.x > volume.m_vMinBounds.x && vCenter.x <= volume.m_vMaxBounds.x &&
  218. vCenter.y > volume.m_vMinBounds.y && vCenter.y <= volume.m_vMaxBounds.y &&
  219. vCenter.z > volume.m_vMinBounds.z && vCenter.z <= volume.m_vMaxBounds.z )
  220. {
  221. // Add the whole triangle
  222. pOutputIndices[ nMeshIndices++ ] = i0;
  223. pOutputIndices[ nMeshIndices++ ] = i1;
  224. pOutputIndices[ nMeshIndices++ ] = i2;
  225. }
  226. }
  227. range.m_nIndexCount = nMeshIndices - range.m_nStartIndex;
  228. }
  229. Assert( nMeshIndices == inputMesh.m_nIndexCount );
  230. }