Team Fortress 2 Source Code as on 22/4/2020
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.

485 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=====================================================================================//
  7. #include "cbase.h"
  8. #include "StaticCollisionPolyhedronCache.h"
  9. #include "engine/IEngineTrace.h"
  10. #include "edict.h"
  11. #include "tier0/memdbgon.h"
  12. class CPolyhedron_LumpedMemory : public CPolyhedron //we'll be allocating one big chunk of memory for all our polyhedrons. No individual will own any memory.
  13. {
  14. public:
  15. virtual void Release( void ) { };
  16. static CPolyhedron_LumpedMemory *AllocateAt( void *pMemory, int iVertices, int iLines, int iIndices, int iPolygons )
  17. {
  18. #include "tier0/memdbgoff.h" //the following placement new doesn't compile with memory debugging
  19. CPolyhedron_LumpedMemory *pAllocated = new ( pMemory ) CPolyhedron_LumpedMemory;
  20. #include "tier0/memdbgon.h"
  21. pAllocated->iVertexCount = iVertices;
  22. pAllocated->iLineCount = iLines;
  23. pAllocated->iIndexCount = iIndices;
  24. pAllocated->iPolygonCount = iPolygons;
  25. pAllocated->pVertices = (Vector *)(pAllocated + 1); //start vertex memory at the end of the class
  26. pAllocated->pLines = (Polyhedron_IndexedLine_t *)(pAllocated->pVertices + iVertices);
  27. pAllocated->pIndices = (Polyhedron_IndexedLineReference_t *)(pAllocated->pLines + iLines);
  28. pAllocated->pPolygons = (Polyhedron_IndexedPolygon_t *)(pAllocated->pIndices + iIndices);
  29. return pAllocated;
  30. }
  31. };
  32. static uint8 *s_BrushPolyhedronMemory = NULL;
  33. static uint8 *s_StaticPropPolyhedronMemory = NULL;
  34. CStaticCollisionPolyhedronCache g_StaticCollisionPolyhedronCache;
  35. typedef ICollideable *ICollideablePtr; //needed for key comparison function syntax
  36. static bool CollideablePtr_KeyCompareFunc( const ICollideablePtr &a, const ICollideablePtr &b )
  37. {
  38. return a < b;
  39. };
  40. CStaticCollisionPolyhedronCache::CStaticCollisionPolyhedronCache( void )
  41. : m_CollideableIndicesMap( CollideablePtr_KeyCompareFunc )
  42. {
  43. }
  44. CStaticCollisionPolyhedronCache::~CStaticCollisionPolyhedronCache( void )
  45. {
  46. Clear();
  47. }
  48. void CStaticCollisionPolyhedronCache::LevelInitPreEntity( void )
  49. {
  50. // FIXME: Fast updates would be nice but this method doesn't work with the recent changes to standard containers.
  51. // For now we're going with the quick fix of always doing a full update. -Jeep
  52. // if( Q_stricmp( m_CachedMap, MapName() ) != 0 )
  53. // {
  54. // // New map or the last load was a transition, fully update the cache
  55. // m_CachedMap.Set( MapName() );
  56. Update();
  57. // }
  58. // else
  59. // {
  60. // // No need for a full update, but we need to remap static prop ICollideable's in the old system to the new system
  61. // for( int i = m_CollideableIndicesMap.Count(); --i >= 0; )
  62. // {
  63. //#ifdef _DEBUG
  64. // StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element(i);
  65. //#endif
  66. // m_CollideableIndicesMap.Reinsert( staticpropmgr->GetStaticPropByIndex( m_CollideableIndicesMap.Element(i).iStaticPropIndex ), i );
  67. //
  68. // Assert( (m_CollideableIndicesMap.Element(i).iStartIndex == cacheInfo.iStartIndex) &&
  69. // (m_CollideableIndicesMap.Element(i).iNumPolyhedrons == cacheInfo.iNumPolyhedrons) &&
  70. // (m_CollideableIndicesMap.Element(i).iStaticPropIndex == cacheInfo.iStaticPropIndex) ); //I'm assuming this doesn't cause a reindex of the unordered list, if it does then this needs to be rewritten
  71. // }
  72. // }
  73. }
  74. void CStaticCollisionPolyhedronCache::Shutdown( void )
  75. {
  76. Clear();
  77. }
  78. void CStaticCollisionPolyhedronCache::Clear( void )
  79. {
  80. //The uses one big lump of memory to store polyhedrons. No need to Release() the polyhedrons.
  81. //Brushes
  82. {
  83. m_BrushPolyhedrons.RemoveAll();
  84. if( s_BrushPolyhedronMemory != NULL )
  85. {
  86. delete []s_BrushPolyhedronMemory;
  87. s_BrushPolyhedronMemory = NULL;
  88. }
  89. }
  90. //Static props
  91. {
  92. m_CollideableIndicesMap.RemoveAll();
  93. m_StaticPropPolyhedrons.RemoveAll();
  94. if( s_StaticPropPolyhedronMemory != NULL )
  95. {
  96. delete []s_StaticPropPolyhedronMemory;
  97. s_StaticPropPolyhedronMemory = NULL;
  98. }
  99. }
  100. }
  101. void CStaticCollisionPolyhedronCache::Update( void )
  102. {
  103. Clear();
  104. //There's no efficient way to know exactly how much memory we'll need to cache off all these polyhedrons.
  105. //So we're going to allocated temporary workspaces as we need them and consolidate into one allocation at the end.
  106. const size_t workSpaceSize = 1024 * 1024; //1MB. Fairly arbitrary size for a workspace. Brushes usually use 1-3MB in the end. Static props usually use about half as much as brushes.
  107. uint8 *workSpaceAllocations[256];
  108. size_t usedSpaceInWorkspace[256];
  109. unsigned int workSpacesAllocated = 0;
  110. uint8 *pCurrentWorkSpace = new uint8 [workSpaceSize];
  111. size_t roomLeftInWorkSpace = workSpaceSize;
  112. workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace;
  113. usedSpaceInWorkspace[workSpacesAllocated] = 0;
  114. ++workSpacesAllocated;
  115. //brushes
  116. {
  117. int iBrush = 0;
  118. CUtlVector<Vector4D> Planes;
  119. float fStackPlanes[4 * 400]; //400 is a crapload of planes in my opinion
  120. while( enginetrace->GetBrushInfo( iBrush, &Planes, NULL ) )
  121. {
  122. int iPlaneCount = Planes.Count();
  123. AssertMsg( iPlaneCount != 0, "A brush with no planes???????" );
  124. const Vector4D *pReturnedPlanes = Planes.Base();
  125. CPolyhedron *pTempPolyhedron;
  126. if( iPlaneCount > 400 )
  127. {
  128. // o_O, we'll have to get more memory to transform this brush
  129. float *pNonstackPlanes = new float [4 * iPlaneCount];
  130. for( int i = 0; i != iPlaneCount; ++i )
  131. {
  132. pNonstackPlanes[(i * 4) + 0] = pReturnedPlanes[i].x;
  133. pNonstackPlanes[(i * 4) + 1] = pReturnedPlanes[i].y;
  134. pNonstackPlanes[(i * 4) + 2] = pReturnedPlanes[i].z;
  135. pNonstackPlanes[(i * 4) + 3] = pReturnedPlanes[i].w;
  136. }
  137. pTempPolyhedron = GeneratePolyhedronFromPlanes( pNonstackPlanes, iPlaneCount, 0.01f, true );
  138. delete []pNonstackPlanes;
  139. }
  140. else
  141. {
  142. for( int i = 0; i != iPlaneCount; ++i )
  143. {
  144. fStackPlanes[(i * 4) + 0] = pReturnedPlanes[i].x;
  145. fStackPlanes[(i * 4) + 1] = pReturnedPlanes[i].y;
  146. fStackPlanes[(i * 4) + 2] = pReturnedPlanes[i].z;
  147. fStackPlanes[(i * 4) + 3] = pReturnedPlanes[i].w;
  148. }
  149. pTempPolyhedron = GeneratePolyhedronFromPlanes( fStackPlanes, iPlaneCount, 0.01f, true );
  150. }
  151. if( pTempPolyhedron )
  152. {
  153. size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
  154. (sizeof( Vector ) * pTempPolyhedron->iVertexCount) +
  155. (sizeof( Polyhedron_IndexedLine_t ) * pTempPolyhedron->iLineCount) +
  156. (sizeof( Polyhedron_IndexedLineReference_t ) * pTempPolyhedron->iIndexCount) +
  157. (sizeof( Polyhedron_IndexedPolygon_t ) * pTempPolyhedron->iPolygonCount);
  158. Assert( memRequired < workSpaceSize );
  159. if( roomLeftInWorkSpace < memRequired )
  160. {
  161. usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
  162. pCurrentWorkSpace = new uint8 [workSpaceSize];
  163. roomLeftInWorkSpace = workSpaceSize;
  164. workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace;
  165. usedSpaceInWorkspace[workSpacesAllocated] = 0;
  166. ++workSpacesAllocated;
  167. }
  168. CPolyhedron *pWorkSpacePolyhedron = CPolyhedron_LumpedMemory::AllocateAt( pCurrentWorkSpace,
  169. pTempPolyhedron->iVertexCount,
  170. pTempPolyhedron->iLineCount,
  171. pTempPolyhedron->iIndexCount,
  172. pTempPolyhedron->iPolygonCount );
  173. pCurrentWorkSpace += memRequired;
  174. roomLeftInWorkSpace -= memRequired;
  175. memcpy( pWorkSpacePolyhedron->pVertices, pTempPolyhedron->pVertices, pTempPolyhedron->iVertexCount * sizeof( Vector ) );
  176. memcpy( pWorkSpacePolyhedron->pLines, pTempPolyhedron->pLines, pTempPolyhedron->iLineCount * sizeof( Polyhedron_IndexedLine_t ) );
  177. memcpy( pWorkSpacePolyhedron->pIndices, pTempPolyhedron->pIndices, pTempPolyhedron->iIndexCount * sizeof( Polyhedron_IndexedLineReference_t ) );
  178. memcpy( pWorkSpacePolyhedron->pPolygons, pTempPolyhedron->pPolygons, pTempPolyhedron->iPolygonCount * sizeof( Polyhedron_IndexedPolygon_t ) );
  179. m_BrushPolyhedrons.AddToTail( pWorkSpacePolyhedron );
  180. pTempPolyhedron->Release();
  181. }
  182. else
  183. {
  184. m_BrushPolyhedrons.AddToTail( NULL );
  185. }
  186. ++iBrush;
  187. }
  188. usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
  189. if( usedSpaceInWorkspace[0] != 0 ) //At least a little bit of memory was used.
  190. {
  191. //consolidate workspaces into a single memory chunk
  192. size_t totalMemoryNeeded = 0;
  193. for( unsigned int i = 0; i != workSpacesAllocated; ++i )
  194. {
  195. totalMemoryNeeded += usedSpaceInWorkspace[i];
  196. }
  197. uint8 *pFinalDest = new uint8 [totalMemoryNeeded];
  198. s_BrushPolyhedronMemory = pFinalDest;
  199. DevMsg( 2, "CStaticCollisionPolyhedronCache: Used %.2f KB to cache %d brush polyhedrons.\n", ((float)totalMemoryNeeded) / 1024.0f, m_BrushPolyhedrons.Count() );
  200. int iCount = m_BrushPolyhedrons.Count();
  201. for( int i = 0; i != iCount; ++i )
  202. {
  203. CPolyhedron_LumpedMemory *pSource = (CPolyhedron_LumpedMemory *)m_BrushPolyhedrons[i];
  204. if( pSource == NULL )
  205. continue;
  206. size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
  207. (sizeof( Vector ) * pSource->iVertexCount) +
  208. (sizeof( Polyhedron_IndexedLine_t ) * pSource->iLineCount) +
  209. (sizeof( Polyhedron_IndexedLineReference_t ) * pSource->iIndexCount) +
  210. (sizeof( Polyhedron_IndexedPolygon_t ) * pSource->iPolygonCount);
  211. CPolyhedron_LumpedMemory *pDest = (CPolyhedron_LumpedMemory *)pFinalDest;
  212. m_BrushPolyhedrons[i] = pDest;
  213. pFinalDest += memRequired;
  214. int memoryOffset = ((uint8 *)pDest) - ((uint8 *)pSource);
  215. memcpy( pDest, pSource, memRequired );
  216. //move all the pointers to their new location.
  217. pDest->pVertices = (Vector *)(((uint8 *)(pDest->pVertices)) + memoryOffset);
  218. pDest->pLines = (Polyhedron_IndexedLine_t *)(((uint8 *)(pDest->pLines)) + memoryOffset);
  219. pDest->pIndices = (Polyhedron_IndexedLineReference_t *)(((uint8 *)(pDest->pIndices)) + memoryOffset);
  220. pDest->pPolygons = (Polyhedron_IndexedPolygon_t *)(((uint8 *)(pDest->pPolygons)) + memoryOffset);
  221. }
  222. }
  223. }
  224. unsigned int iBrushWorkSpaces = workSpacesAllocated;
  225. workSpacesAllocated = 1;
  226. pCurrentWorkSpace = workSpaceAllocations[0];
  227. usedSpaceInWorkspace[0] = 0;
  228. roomLeftInWorkSpace = workSpaceSize;
  229. //static props
  230. {
  231. CUtlVector<ICollideable *> StaticPropCollideables;
  232. staticpropmgr->GetAllStaticProps( &StaticPropCollideables );
  233. if( StaticPropCollideables.Count() != 0 )
  234. {
  235. ICollideable **pCollideables = StaticPropCollideables.Base();
  236. ICollideable **pStop = pCollideables + StaticPropCollideables.Count();
  237. int iStaticPropIndex = 0;
  238. do
  239. {
  240. ICollideable *pProp = *pCollideables;
  241. vcollide_t *pCollide = modelinfo->GetVCollide( pProp->GetCollisionModel() );
  242. StaticPropPolyhedronCacheInfo_t cacheInfo;
  243. cacheInfo.iStartIndex = m_StaticPropPolyhedrons.Count();
  244. if( pCollide != NULL )
  245. {
  246. VMatrix matToWorldPosition = pProp->CollisionToWorldTransform();
  247. for( int i = 0; i != pCollide->solidCount; ++i )
  248. {
  249. CPhysConvex *ConvexesArray[1024];
  250. int iConvexes = physcollision->GetConvexesUsedInCollideable( pCollide->solids[i], ConvexesArray, 1024 );
  251. for( int j = 0; j != iConvexes; ++j )
  252. {
  253. CPolyhedron *pTempPolyhedron = physcollision->PolyhedronFromConvex( ConvexesArray[j], true );
  254. if( pTempPolyhedron )
  255. {
  256. for( int iPointCounter = 0; iPointCounter != pTempPolyhedron->iVertexCount; ++iPointCounter )
  257. pTempPolyhedron->pVertices[iPointCounter] = matToWorldPosition * pTempPolyhedron->pVertices[iPointCounter];
  258. for( int iPolyCounter = 0; iPolyCounter != pTempPolyhedron->iPolygonCount; ++iPolyCounter )
  259. pTempPolyhedron->pPolygons[iPolyCounter].polyNormal = matToWorldPosition.ApplyRotation( pTempPolyhedron->pPolygons[iPolyCounter].polyNormal );
  260. size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
  261. (sizeof( Vector ) * pTempPolyhedron->iVertexCount) +
  262. (sizeof( Polyhedron_IndexedLine_t ) * pTempPolyhedron->iLineCount) +
  263. (sizeof( Polyhedron_IndexedLineReference_t ) * pTempPolyhedron->iIndexCount) +
  264. (sizeof( Polyhedron_IndexedPolygon_t ) * pTempPolyhedron->iPolygonCount);
  265. Assert( memRequired < workSpaceSize );
  266. if( roomLeftInWorkSpace < memRequired )
  267. {
  268. usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
  269. if( workSpacesAllocated < iBrushWorkSpaces )
  270. {
  271. //re-use a workspace already allocated during brush polyhedron conversion
  272. pCurrentWorkSpace = workSpaceAllocations[workSpacesAllocated];
  273. usedSpaceInWorkspace[workSpacesAllocated] = 0;
  274. }
  275. else
  276. {
  277. //allocate a new workspace
  278. pCurrentWorkSpace = new uint8 [workSpaceSize];
  279. workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace;
  280. usedSpaceInWorkspace[workSpacesAllocated] = 0;
  281. }
  282. roomLeftInWorkSpace = workSpaceSize;
  283. ++workSpacesAllocated;
  284. }
  285. CPolyhedron *pWorkSpacePolyhedron = CPolyhedron_LumpedMemory::AllocateAt( pCurrentWorkSpace,
  286. pTempPolyhedron->iVertexCount,
  287. pTempPolyhedron->iLineCount,
  288. pTempPolyhedron->iIndexCount,
  289. pTempPolyhedron->iPolygonCount );
  290. pCurrentWorkSpace += memRequired;
  291. roomLeftInWorkSpace -= memRequired;
  292. memcpy( pWorkSpacePolyhedron->pVertices, pTempPolyhedron->pVertices, pTempPolyhedron->iVertexCount * sizeof( Vector ) );
  293. memcpy( pWorkSpacePolyhedron->pLines, pTempPolyhedron->pLines, pTempPolyhedron->iLineCount * sizeof( Polyhedron_IndexedLine_t ) );
  294. memcpy( pWorkSpacePolyhedron->pIndices, pTempPolyhedron->pIndices, pTempPolyhedron->iIndexCount * sizeof( Polyhedron_IndexedLineReference_t ) );
  295. memcpy( pWorkSpacePolyhedron->pPolygons, pTempPolyhedron->pPolygons, pTempPolyhedron->iPolygonCount * sizeof( Polyhedron_IndexedPolygon_t ) );
  296. m_StaticPropPolyhedrons.AddToTail( pWorkSpacePolyhedron );
  297. #ifdef _DEBUG
  298. CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pTempPolyhedron );
  299. AssertMsg( pConvex != NULL, "Conversion from Convex to Polyhedron was unreversable" );
  300. if( pConvex )
  301. {
  302. physcollision->ConvexFree( pConvex );
  303. }
  304. #endif
  305. pTempPolyhedron->Release();
  306. }
  307. }
  308. }
  309. cacheInfo.iNumPolyhedrons = m_StaticPropPolyhedrons.Count() - cacheInfo.iStartIndex;
  310. cacheInfo.iStaticPropIndex = iStaticPropIndex;
  311. Assert( staticpropmgr->GetStaticPropByIndex( iStaticPropIndex ) == pProp );
  312. m_CollideableIndicesMap.InsertOrReplace( pProp, cacheInfo );
  313. }
  314. ++iStaticPropIndex;
  315. ++pCollideables;
  316. } while( pCollideables != pStop );
  317. usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
  318. if( usedSpaceInWorkspace[0] != 0 ) //At least a little bit of memory was used.
  319. {
  320. //consolidate workspaces into a single memory chunk
  321. size_t totalMemoryNeeded = 0;
  322. for( unsigned int i = 0; i != workSpacesAllocated; ++i )
  323. {
  324. totalMemoryNeeded += usedSpaceInWorkspace[i];
  325. }
  326. uint8 *pFinalDest = new uint8 [totalMemoryNeeded];
  327. s_StaticPropPolyhedronMemory = pFinalDest;
  328. DevMsg( 2, "CStaticCollisionPolyhedronCache: Used %.2f KB to cache %d static prop polyhedrons.\n", ((float)totalMemoryNeeded) / 1024.0f, m_StaticPropPolyhedrons.Count() );
  329. int iCount = m_StaticPropPolyhedrons.Count();
  330. for( int i = 0; i != iCount; ++i )
  331. {
  332. CPolyhedron_LumpedMemory *pSource = (CPolyhedron_LumpedMemory *)m_StaticPropPolyhedrons[i];
  333. size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
  334. (sizeof( Vector ) * pSource->iVertexCount) +
  335. (sizeof( Polyhedron_IndexedLine_t ) * pSource->iLineCount) +
  336. (sizeof( Polyhedron_IndexedLineReference_t ) * pSource->iIndexCount) +
  337. (sizeof( Polyhedron_IndexedPolygon_t ) * pSource->iPolygonCount);
  338. CPolyhedron_LumpedMemory *pDest = (CPolyhedron_LumpedMemory *)pFinalDest;
  339. m_StaticPropPolyhedrons[i] = pDest;
  340. pFinalDest += memRequired;
  341. int memoryOffset = ((uint8 *)pDest) - ((uint8 *)pSource);
  342. memcpy( pDest, pSource, memRequired );
  343. //move all the pointers to their new location.
  344. pDest->pVertices = (Vector *)(((uint8 *)(pDest->pVertices)) + memoryOffset);
  345. pDest->pLines = (Polyhedron_IndexedLine_t *)(((uint8 *)(pDest->pLines)) + memoryOffset);
  346. pDest->pIndices = (Polyhedron_IndexedLineReference_t *)(((uint8 *)(pDest->pIndices)) + memoryOffset);
  347. pDest->pPolygons = (Polyhedron_IndexedPolygon_t *)(((uint8 *)(pDest->pPolygons)) + memoryOffset);
  348. }
  349. }
  350. }
  351. }
  352. if( iBrushWorkSpaces > workSpacesAllocated )
  353. workSpacesAllocated = iBrushWorkSpaces;
  354. for( unsigned int i = 0; i != workSpacesAllocated; ++i )
  355. {
  356. delete []workSpaceAllocations[i];
  357. }
  358. }
  359. const CPolyhedron *CStaticCollisionPolyhedronCache::GetBrushPolyhedron( int iBrushNumber )
  360. {
  361. Assert( iBrushNumber < m_BrushPolyhedrons.Count() );
  362. if( (iBrushNumber < 0) || (iBrushNumber >= m_BrushPolyhedrons.Count()) )
  363. return NULL;
  364. return m_BrushPolyhedrons[iBrushNumber];
  365. }
  366. int CStaticCollisionPolyhedronCache::GetStaticPropPolyhedrons( ICollideable *pStaticProp, CPolyhedron **pOutputPolyhedronArray, int iOutputArraySize )
  367. {
  368. unsigned short iPropIndex = m_CollideableIndicesMap.Find( pStaticProp );
  369. if( !m_CollideableIndicesMap.IsValidIndex( iPropIndex ) ) //static prop never made it into the cache for some reason (specifically no collision data when this workaround was written)
  370. return 0;
  371. StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element( iPropIndex );
  372. if( cacheInfo.iNumPolyhedrons < iOutputArraySize )
  373. iOutputArraySize = cacheInfo.iNumPolyhedrons;
  374. for( int i = cacheInfo.iStartIndex, iWriteIndex = 0; iWriteIndex != iOutputArraySize; ++i, ++iWriteIndex )
  375. {
  376. pOutputPolyhedronArray[iWriteIndex] = m_StaticPropPolyhedrons[i];
  377. }
  378. return iOutputArraySize;
  379. }