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.

398 lines
12 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "vbsp.h"
  7. #include "disp_vbsp.h"
  8. #include "builddisp.h"
  9. #include "disp_common.h"
  10. #include "ivp.h"
  11. #include "disp_ivp.h"
  12. #include "vphysics_interface.h"
  13. #include "vphysics/virtualmesh.h"
  14. #include "utlrbtree.h"
  15. #include "tier1/utlbuffer.h"
  16. #include "materialpatch.h"
  17. struct disp_grid_t
  18. {
  19. int gridIndex;
  20. CUtlVector<int> dispList;
  21. };
  22. static CUtlVector<disp_grid_t> gDispGridList;
  23. disp_grid_t &FindOrInsertGrid( int gridIndex )
  24. {
  25. // linear search is slow, but only a few grids will be present
  26. for ( int i = gDispGridList.Count()-1; i >= 0; i-- )
  27. {
  28. if ( gDispGridList[i].gridIndex == gridIndex )
  29. {
  30. return gDispGridList[i];
  31. }
  32. }
  33. int index = gDispGridList.AddToTail();
  34. gDispGridList[index].gridIndex = gridIndex;
  35. // must be empty
  36. Assert( gDispGridList[index].dispList.Count() == 0 );
  37. return gDispGridList[index];
  38. }
  39. // UNDONE: Tune these or adapt them to map size or triangle count?
  40. #define DISP_GRID_SIZEX 4096
  41. #define DISP_GRID_SIZEY 4096
  42. #define DISP_GRID_SIZEZ 8192
  43. int Disp_GridIndex( CCoreDispInfo *pDispInfo )
  44. {
  45. // quick hash the center into the grid and put the whole terrain in that grid
  46. Vector mins, maxs;
  47. pDispInfo->GetNode(0)->GetBoundingBox( mins, maxs );
  48. Vector center;
  49. center = 0.5 * (mins + maxs);
  50. // make sure it's positive
  51. center += Vector(MAX_COORD_INTEGER,MAX_COORD_INTEGER,MAX_COORD_INTEGER);
  52. int gridX = center.x / DISP_GRID_SIZEX;
  53. int gridY = center.y / DISP_GRID_SIZEY;
  54. int gridZ = center.z / DISP_GRID_SIZEZ;
  55. gridX &= 0xFF;
  56. gridY &= 0xFF;
  57. gridZ &= 0xFF;
  58. return MAKEID( gridX, gridY, gridZ, 0 );
  59. }
  60. void AddToGrid( int gridIndex, int dispIndex )
  61. {
  62. disp_grid_t &grid = FindOrInsertGrid( gridIndex );
  63. grid.dispList.AddToTail( dispIndex );
  64. }
  65. MaterialSystemMaterial_t GetMatIDFromDisp( mapdispinfo_t *pMapDisp )
  66. {
  67. texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
  68. dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
  69. MaterialSystemMaterial_t matID = FindOriginalMaterial( TexDataStringTable_GetString( pTexData->nameStringTableID ), NULL, true );
  70. return matID;
  71. }
  72. const char* GetMatNameFromDisp( mapdispinfo_t *pMapDisp )
  73. {
  74. texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
  75. dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
  76. return TexDataStringTable_GetString( pTexData->nameStringTableID );
  77. }
  78. // adds all displacement faces as a series of convex objects
  79. // UNDONE: Only add the displacements for this model?
  80. void Disp_AddCollisionModels( CUtlVector<CPhysCollisionEntry *> &collisionList, dmodel_t *pModel, int contentsMask)
  81. {
  82. int dispIndex;
  83. // Add each displacement to the grid hash
  84. for ( dispIndex = 0; dispIndex < g_CoreDispInfos.Count(); dispIndex++ )
  85. {
  86. CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
  87. mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
  88. // not solid for this pass
  89. if ( !(pMapDisp->contents & contentsMask) )
  90. continue;
  91. int gridIndex = Disp_GridIndex( pDispInfo );
  92. AddToGrid( gridIndex, dispIndex );
  93. }
  94. // now make a polysoup for the terrain in each grid
  95. for ( int grid = 0; grid < gDispGridList.Count(); grid++ )
  96. {
  97. int triCount = 0;
  98. CPhysPolysoup *pTerrainPhysics = physcollision->PolysoupCreate();
  99. // iterate the displacements in this grid
  100. for ( int listIndex = 0; listIndex < gDispGridList[grid].dispList.Count(); listIndex++ )
  101. {
  102. dispIndex = gDispGridList[grid].dispList[listIndex];
  103. CCoreDispInfo *pDispInfo = g_CoreDispInfos[ dispIndex ];
  104. mapdispinfo_t *pMapDisp = &mapdispinfo[ dispIndex ];
  105. // Get the material id.
  106. MaterialSystemMaterial_t matID = GetMatIDFromDisp( pMapDisp );
  107. // Build a triangle list. This shares the tesselation code with the engine.
  108. CUtlVector<unsigned short> indices;
  109. CVBSPTesselateHelper helper;
  110. helper.m_pIndices = &indices;
  111. helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
  112. helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
  113. ::TesselateDisplacement( &helper );
  114. Assert( indices.Count() > 0 );
  115. Assert( indices.Count() % 3 == 0 ); // Make sure indices are a multiple of 3.
  116. int nTriCount = indices.Count() / 3;
  117. triCount += nTriCount;
  118. if ( triCount >= 65536 )
  119. {
  120. // don't put more than 64K tris in any single collision model
  121. CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
  122. if ( pCollide )
  123. {
  124. collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
  125. }
  126. // Throw this polysoup away and start over for the remaining triangles
  127. physcollision->PolysoupDestroy( pTerrainPhysics );
  128. pTerrainPhysics = physcollision->PolysoupCreate();
  129. triCount = nTriCount;
  130. }
  131. Vector tmpVerts[3];
  132. for ( int iTri = 0; iTri < nTriCount; ++iTri )
  133. {
  134. float flTotalAlpha[3] = { 0,0,0 };
  135. for ( int iTriVert = 0; iTriVert < 3; ++iTriVert )
  136. {
  137. pDispInfo->GetVert( indices[iTri*3+iTriVert], tmpVerts[iTriVert] );
  138. if ( ( pDispInfo->GetFlags() & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
  139. {
  140. Vector4D vBlend;
  141. pDispInfo->GetMultiBlend( indices[iTri*3+iTriVert], vBlend );
  142. flTotalAlpha[0] += vBlend.y;
  143. flTotalAlpha[1] += vBlend.z;
  144. flTotalAlpha[2] += vBlend.w;
  145. }
  146. else
  147. {
  148. flTotalAlpha[0] += pDispInfo->GetAlpha( indices[iTri*3+iTriVert] );
  149. }
  150. }
  151. int nProp = g_SurfaceProperties[texinfo[pMapDisp->face.texinfo].texdata];
  152. if ( ( pDispInfo->GetFlags() & DISP_INFO_FLAG_HAS_MULTIBLEND ) != 0 )
  153. {
  154. if ( ( flTotalAlpha[2] > DISP_MULTIBLEND_PROP_THRESHOLD ) && ( flTotalAlpha[2] >= flTotalAlpha[1] ) && ( flTotalAlpha[2] >= flTotalAlpha[0] ) )
  155. {
  156. int nPropA = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop4" );
  157. if ( nPropA != -1 )
  158. {
  159. nProp = nPropA;
  160. }
  161. Msg("Picked prop4\n");
  162. }
  163. else if ( ( flTotalAlpha[1] > DISP_MULTIBLEND_PROP_THRESHOLD ) && ( flTotalAlpha[1] >= flTotalAlpha[0] ) )
  164. {
  165. int nPropA = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop3" );
  166. if ( nPropA != -1 )
  167. {
  168. nProp = nPropA;
  169. }
  170. Msg("Picked prop3\n");
  171. }
  172. else if ( flTotalAlpha[0] > DISP_MULTIBLEND_PROP_THRESHOLD )
  173. {
  174. int nPropA = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop2" );
  175. if ( nPropA != -1 )
  176. {
  177. nProp = nPropA;
  178. }
  179. Msg("Picked prop2\n");
  180. }
  181. }
  182. else
  183. {
  184. if ( flTotalAlpha[0] > DISP_ALPHA_PROP_DELTA )
  185. {
  186. int nProp2 = GetSurfaceProperties2( matID, GetMatNameFromDisp( pMapDisp ), "surfaceprop2" );
  187. if ( nProp2 != -1 )
  188. {
  189. nProp = nProp2;
  190. }
  191. }
  192. }
  193. int nMaterialIndex = RemapWorldMaterial( nProp );
  194. physcollision->PolysoupAddTriangle( pTerrainPhysics, tmpVerts[0], tmpVerts[1], tmpVerts[2], nMaterialIndex );
  195. }
  196. }
  197. // convert the whole grid's polysoup to a collide and store in the collision list
  198. CPhysCollide *pCollide = physcollision->ConvertPolysoupToCollide( pTerrainPhysics, false );
  199. if ( pCollide )
  200. {
  201. collisionList.AddToTail( new CPhysCollisionEntryStaticMesh( pCollide, NULL ) );
  202. }
  203. // now that we have the collide, we're done with the soup
  204. physcollision->PolysoupDestroy( pTerrainPhysics );
  205. }
  206. }
  207. class CDispMeshEvent : public IVirtualMeshEvent
  208. {
  209. public:
  210. CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo );
  211. virtual void GetVirtualMesh( void *userData, virtualmeshlist_t *pList );
  212. virtual void GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs );
  213. virtual void GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList );
  214. CUtlVector<Vector> m_verts;
  215. unsigned short *m_pIndices;
  216. int m_indexCount;
  217. };
  218. CDispMeshEvent::CDispMeshEvent( unsigned short *pIndices, int indexCount, CCoreDispInfo *pDispInfo )
  219. {
  220. m_pIndices = pIndices;
  221. m_indexCount = indexCount;
  222. int maxIndex = 0;
  223. for ( int i = 0; i < indexCount; i++ )
  224. {
  225. if ( pIndices[i] > maxIndex )
  226. {
  227. maxIndex = pIndices[i];
  228. }
  229. }
  230. for ( int i = 0; i < indexCount/2; i++ )
  231. {
  232. V_swap( pIndices[i], pIndices[(indexCount-i)-1] );
  233. }
  234. int count = maxIndex + 1;
  235. m_verts.SetCount( count );
  236. for ( int i = 0; i < count; i++ )
  237. {
  238. m_verts[i] = pDispInfo->GetVert(i);
  239. }
  240. }
  241. void CDispMeshEvent::GetVirtualMesh( void *userData, virtualmeshlist_t *pList )
  242. {
  243. Assert(userData==((void *)this));
  244. pList->pVerts = m_verts.Base();
  245. pList->indexCount = m_indexCount;
  246. pList->triangleCount = m_indexCount/3;
  247. pList->vertexCount = m_verts.Count();
  248. pList->surfacePropsIndex = 0; // doesn't matter here, reset at runtime
  249. pList->pHull = NULL;
  250. int indexMax = ARRAYSIZE(pList->indices);
  251. int indexCount = MIN(m_indexCount, indexMax);
  252. Assert(m_indexCount < indexMax);
  253. Q_memcpy( pList->indices, m_pIndices, sizeof(*m_pIndices) * indexCount );
  254. }
  255. void CDispMeshEvent::GetWorldspaceBounds( void *userData, Vector *pMins, Vector *pMaxs )
  256. {
  257. Assert(userData==((void *)this));
  258. ClearBounds( *pMins, *pMaxs );
  259. for ( int i = 0; i < m_verts.Count(); i++ )
  260. {
  261. AddPointToBounds( m_verts[i], *pMins, *pMaxs );
  262. }
  263. }
  264. void CDispMeshEvent::GetTrianglesInSphere( void *userData, const Vector &center, float radius, virtualmeshtrianglelist_t *pList )
  265. {
  266. Assert(userData==((void *)this));
  267. pList->triangleCount = m_indexCount/3;
  268. int indexMax = ARRAYSIZE(pList->triangleIndices);
  269. int indexCount = MIN(m_indexCount, indexMax);
  270. Assert(m_indexCount < MAX_VIRTUAL_TRIANGLES*3);
  271. Q_memcpy( pList->triangleIndices, m_pIndices, sizeof(*m_pIndices) * indexCount );
  272. }
  273. void Disp_BuildVirtualMesh( int contentsMask )
  274. {
  275. CUtlVector<CPhysCollide *> virtualMeshes;
  276. virtualMeshes.EnsureCount( g_CoreDispInfos.Count() );
  277. for ( int i = 0; i < g_CoreDispInfos.Count(); i++ )
  278. {
  279. CCoreDispInfo *pDispInfo = g_CoreDispInfos[ i ];
  280. mapdispinfo_t *pMapDisp = &mapdispinfo[ i ];
  281. virtualMeshes[i] = NULL;
  282. // not solid for this pass
  283. if ( !(pMapDisp->contents & contentsMask) )
  284. continue;
  285. // Build a triangle list. This shares the tesselation code with the engine.
  286. CUtlVector<unsigned short> indices;
  287. CVBSPTesselateHelper helper;
  288. helper.m_pIndices = &indices;
  289. helper.m_pActiveVerts = pDispInfo->GetAllowedVerts().Base();
  290. helper.m_pPowerInfo = pDispInfo->GetPowerInfo();
  291. ::TesselateDisplacement( &helper );
  292. // validate the collision data
  293. if ( 1 )
  294. {
  295. int triCount = indices.Count() / 3;
  296. for ( int j = 0; j < triCount; j++ )
  297. {
  298. int index = j * 3;
  299. Vector v0 = pDispInfo->GetVert( indices[index+0] );
  300. Vector v1 = pDispInfo->GetVert( indices[index+1] );
  301. Vector v2 = pDispInfo->GetVert( indices[index+2] );
  302. if ( v0 == v1 || v1 == v2 || v2 == v0 )
  303. {
  304. Warning( "Displacement %d has bad geometry near %.2f %.2f %.2f\n", i, v0.x, v0.y, v0.z );
  305. texinfo_t *pTexInfo = &texinfo[pMapDisp->face.texinfo];
  306. dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
  307. const char *pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
  308. Error( "Can't compile displacement physics, exiting. Texture is %s\n", pMatName );
  309. }
  310. }
  311. }
  312. CDispMeshEvent meshHandler( indices.Base(), indices.Count(), pDispInfo );
  313. virtualmeshparams_t params;
  314. params.buildOuterHull = true;
  315. params.pMeshEventHandler = &meshHandler;
  316. params.userData = &meshHandler;
  317. virtualMeshes[i] = physcollision->CreateVirtualMesh( params );
  318. }
  319. unsigned int totalSize = 0;
  320. CUtlBuffer buf;
  321. dphysdisp_t header;
  322. header.numDisplacements = g_CoreDispInfos.Count();
  323. buf.PutObjects( &header );
  324. CUtlVector<char> dispBuf;
  325. for ( int i = 0; i < header.numDisplacements; i++ )
  326. {
  327. if ( virtualMeshes[i] )
  328. {
  329. unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
  330. totalSize += testSize;
  331. buf.PutShort( testSize );
  332. }
  333. else
  334. {
  335. buf.PutShort( -1 );
  336. }
  337. }
  338. for ( int i = 0; i < header.numDisplacements; i++ )
  339. {
  340. if ( virtualMeshes[i] )
  341. {
  342. unsigned int testSize = physcollision->CollideSize( virtualMeshes[i] );
  343. dispBuf.RemoveAll();
  344. dispBuf.EnsureCount(testSize);
  345. unsigned int outSize = physcollision->CollideWrite( dispBuf.Base(), virtualMeshes[i], false );
  346. Assert( outSize == testSize );
  347. buf.Put( dispBuf.Base(), outSize );
  348. }
  349. }
  350. g_PhysDispSize = totalSize + sizeof(dphysdisp_t) + (sizeof(unsigned short) * header.numDisplacements);
  351. Assert( buf.TellMaxPut() == g_PhysDispSize );
  352. g_PhysDispSize = buf.TellMaxPut();
  353. g_pPhysDisp = new byte[g_PhysDispSize];
  354. Q_memcpy( g_pPhysDisp, buf.Base(), g_PhysDispSize );
  355. }