source-engine/game/shared/portal/StaticCollisionPolyhedronCache.cpp
tupoy-ya 881d4a91c9
fix(EngineTrace)!: Update Engine Trace to CSGO version.
Fixes portals getting stuck in objects.

fix(Portal Trace Filter): Can shoot through cubes now.

refactor(Soundscape)!: Updated to the CSGO Version.

chore(Copyright): Remove dates and unicode characters.
2023-09-30 15:41:04 +05:00

506 lines
18 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=====================================================================================//
#include "cbase.h"
#include "StaticCollisionPolyhedronCache.h"
#include "engine/IEngineTrace.h"
#include "edict.h"
#include "tier0/memdbgon.h"
class CPolyhedron_LumpedMemory : public CPolyhedron //we'll be allocating one big chunk of memory for all our polyhedrons. No individual will own any memory.
{
public:
virtual void Release( void ) { };
static CPolyhedron_LumpedMemory *AllocateAt( void *pMemory, int iVertices, int iLines, int iIndices, int iPolygons )
{
#include "tier0/memdbgoff.h" //the following placement new doesn't compile with memory debugging
CPolyhedron_LumpedMemory *pAllocated = new ( pMemory ) CPolyhedron_LumpedMemory;
#include "tier0/memdbgon.h"
pAllocated->iVertexCount = iVertices;
pAllocated->iLineCount = iLines;
pAllocated->iIndexCount = iIndices;
pAllocated->iPolygonCount = iPolygons;
pAllocated->pVertices = (Vector *)(pAllocated + 1); //start vertex memory at the end of the class
pAllocated->pLines = (Polyhedron_IndexedLine_t *)(pAllocated->pVertices + iVertices);
pAllocated->pIndices = (Polyhedron_IndexedLineReference_t *)(pAllocated->pLines + iLines);
pAllocated->pPolygons = (Polyhedron_IndexedPolygon_t *)(pAllocated->pIndices + iIndices);
return pAllocated;
}
};
static uint8 *s_BrushPolyhedronMemory = NULL;
static uint8 *s_StaticPropPolyhedronMemory = NULL;
CStaticCollisionPolyhedronCache g_StaticCollisionPolyhedronCache;
ConVar sv_portal_staticcollisioncache_cachebrushes( "sv_portal_staticcollisioncache_cachebrushes", IsPS3() ? "0" : "1", FCVAR_REPLICATED, "Cache all solid brushes as polyhedrons on level load" );
typedef ICollideable *ICollideablePtr; //needed for key comparison function syntax
static bool CollideablePtr_KeyCompareFunc( const ICollideablePtr &a, const ICollideablePtr &b )
{
return a < b;
};
CStaticCollisionPolyhedronCache::CStaticCollisionPolyhedronCache( void )
: m_CollideableIndicesMap( CollideablePtr_KeyCompareFunc )
{
}
CStaticCollisionPolyhedronCache::~CStaticCollisionPolyhedronCache( void )
{
Clear();
}
void CStaticCollisionPolyhedronCache::LevelInitPreEntity( void )
{
// FIXME: Fast updates would be nice but this method doesn't work with the recent changes to standard containers.
// For now we're going with the quick fix of always doing a full update. -Jeep
// if( Q_stricmp( m_CachedMap, MapName() ) != 0 )
// {
// // New map or the last load was a transition, fully update the cache
// m_CachedMap.Set( MapName() );
Update();
// }
// else
// {
// // No need for a full update, but we need to remap static prop ICollideable's in the old system to the new system
// for( int i = m_CollideableIndicesMap.Count(); --i >= 0; )
// {
//#ifdef _DEBUG
// StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element(i);
//#endif
// m_CollideableIndicesMap.Reinsert( staticpropmgr->GetStaticPropByIndex( m_CollideableIndicesMap.Element(i).iStaticPropIndex ), i );
//
// Assert( (m_CollideableIndicesMap.Element(i).iStartIndex == cacheInfo.iStartIndex) &&
// (m_CollideableIndicesMap.Element(i).iNumPolyhedrons == cacheInfo.iNumPolyhedrons) &&
// (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
// }
// }
}
void CStaticCollisionPolyhedronCache::Shutdown( void )
{
Clear();
}
void CStaticCollisionPolyhedronCache::Clear( void )
{
//The uses one big lump of memory to store polyhedrons. No need to Release() the polyhedrons.
//Brushes
{
m_BrushPolyhedrons.RemoveAll();
if( s_BrushPolyhedronMemory != NULL )
{
delete []s_BrushPolyhedronMemory;
s_BrushPolyhedronMemory = NULL;
}
}
//Static props
{
m_CollideableIndicesMap.RemoveAll();
m_StaticPropPolyhedrons.RemoveAll();
if( s_StaticPropPolyhedronMemory != NULL )
{
delete []s_StaticPropPolyhedronMemory;
s_StaticPropPolyhedronMemory = NULL;
}
}
}
static CPolyhedron *ConvertBrushToPolyhedron( int iBrushNumber, int iContentsMask, bool bTempPolyhedron )
{
int iBrushContents = 0;
int iPlanesNeeded = -enginetrace->GetBrushInfo( iBrushNumber, iBrushContents, NULL, 0 );
if( (iPlanesNeeded == 0) || ((iContentsMask & iBrushContents) == 0) )
return NULL;
uint8 *pMemory;
void *pDeleteMemory;
float *fStackPlanes;
BrushSideInfo_t *brushSides;
size_t iMemoryNeeded = iPlanesNeeded * ((sizeof( float ) * 4) + sizeof( BrushSideInfo_t ));
if( iMemoryNeeded < (64 * 1024) )
{
//use stack memory
pMemory = (uint8 *)stackalloc( iMemoryNeeded );
pDeleteMemory = NULL;
}
else
{
pMemory = new uint8 [iMemoryNeeded];
pDeleteMemory = pMemory;
}
fStackPlanes = (float *)pMemory;
brushSides = (BrushSideInfo_t *)(pMemory + (iPlanesNeeded * (sizeof( float ) * 4)));
int iPlaneCount = enginetrace->GetBrushInfo( iBrushNumber, iBrushContents, brushSides, iPlanesNeeded );
CPolyhedron *pRetVal = NULL;
Assert( iPlaneCount == iPlanesNeeded );
if( iPlaneCount == iPlanesNeeded )
{
for( int i = 0; i != iPlaneCount; ++i )
{
fStackPlanes[(i * 4) + 0] = brushSides[i].plane.normal.x;
fStackPlanes[(i * 4) + 1] = brushSides[i].plane.normal.y;
fStackPlanes[(i * 4) + 2] = brushSides[i].plane.normal.z;
fStackPlanes[(i * 4) + 3] = brushSides[i].plane.dist;
}
pRetVal = GeneratePolyhedronFromPlanes( fStackPlanes, iPlaneCount, (1.0f/16.0f), bTempPolyhedron );
}
if( pDeleteMemory )
{
delete []pDeleteMemory;
}
return pRetVal;
}
void CStaticCollisionPolyhedronCache::Update( void )
{
Clear();
//There's no efficient way to know exactly how much memory we'll need to cache off all these polyhedrons.
//So we're going to allocated temporary workspaces as we need them and consolidate into one allocation at the end.
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.
uint8 *workSpaceAllocations[256];
size_t usedSpaceInWorkspace[256];
unsigned int workSpacesAllocated = 0;
uint8 *pCurrentWorkSpace = new uint8 [workSpaceSize];
size_t roomLeftInWorkSpace = workSpaceSize;
workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace;
usedSpaceInWorkspace[workSpacesAllocated] = 0;
++workSpacesAllocated;
//brushes
if( sv_portal_staticcollisioncache_cachebrushes.GetBool() )
{
int iBrush = 0;
int iBrushContents;
CPolyhedron *pTempPolyhedron = ConvertBrushToPolyhedron( iBrush, MASK_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, true );
while( (pTempPolyhedron != NULL) || (enginetrace->GetBrushInfo( iBrush, iBrushContents, NULL, 0 ) != 0) )
{
if( pTempPolyhedron )
{
size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
(sizeof( Vector ) * pTempPolyhedron->iVertexCount) +
(sizeof( Polyhedron_IndexedLine_t ) * pTempPolyhedron->iLineCount) +
(sizeof( Polyhedron_IndexedLineReference_t ) * pTempPolyhedron->iIndexCount) +
(sizeof( Polyhedron_IndexedPolygon_t ) * pTempPolyhedron->iPolygonCount);
Assert( memRequired < workSpaceSize );
if( roomLeftInWorkSpace < memRequired )
{
usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
pCurrentWorkSpace = new uint8 [workSpaceSize];
roomLeftInWorkSpace = workSpaceSize;
workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace;
usedSpaceInWorkspace[workSpacesAllocated] = 0;
++workSpacesAllocated;
}
CPolyhedron *pWorkSpacePolyhedron = CPolyhedron_LumpedMemory::AllocateAt( pCurrentWorkSpace,
pTempPolyhedron->iVertexCount,
pTempPolyhedron->iLineCount,
pTempPolyhedron->iIndexCount,
pTempPolyhedron->iPolygonCount );
pCurrentWorkSpace += memRequired;
roomLeftInWorkSpace -= memRequired;
memcpy( pWorkSpacePolyhedron->pVertices, pTempPolyhedron->pVertices, pTempPolyhedron->iVertexCount * sizeof( Vector ) );
memcpy( pWorkSpacePolyhedron->pLines, pTempPolyhedron->pLines, pTempPolyhedron->iLineCount * sizeof( Polyhedron_IndexedLine_t ) );
memcpy( pWorkSpacePolyhedron->pIndices, pTempPolyhedron->pIndices, pTempPolyhedron->iIndexCount * sizeof( Polyhedron_IndexedLineReference_t ) );
memcpy( pWorkSpacePolyhedron->pPolygons, pTempPolyhedron->pPolygons, pTempPolyhedron->iPolygonCount * sizeof( Polyhedron_IndexedPolygon_t ) );
m_BrushPolyhedrons.AddToTail( pWorkSpacePolyhedron );
pTempPolyhedron->Release();
}
else
{
m_BrushPolyhedrons.AddToTail( NULL );
}
++iBrush;
pTempPolyhedron = ConvertBrushToPolyhedron( iBrush, MASK_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP, true );
}
usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
if( usedSpaceInWorkspace[0] != 0 ) //At least a little bit of memory was used.
{
//consolidate workspaces into a single memory chunk
size_t totalMemoryNeeded = 0;
for( unsigned int i = 0; i != workSpacesAllocated; ++i )
{
totalMemoryNeeded += usedSpaceInWorkspace[i];
}
uint8 *pFinalDest = new uint8 [totalMemoryNeeded];
s_BrushPolyhedronMemory = pFinalDest;
DevMsg( 2, "CStaticCollisionPolyhedronCache: Used %.2f KB to cache %d brush polyhedrons.\n", ((float)totalMemoryNeeded) / 1024.0f, m_BrushPolyhedrons.Count() );
int iCount = m_BrushPolyhedrons.Count();
for( int i = 0; i != iCount; ++i )
{
CPolyhedron_LumpedMemory *pSource = (CPolyhedron_LumpedMemory *)m_BrushPolyhedrons[i];
if( pSource == NULL )
continue;
size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
(sizeof( Vector ) * pSource->iVertexCount) +
(sizeof( Polyhedron_IndexedLine_t ) * pSource->iLineCount) +
(sizeof( Polyhedron_IndexedLineReference_t ) * pSource->iIndexCount) +
(sizeof( Polyhedron_IndexedPolygon_t ) * pSource->iPolygonCount);
CPolyhedron_LumpedMemory *pDest = (CPolyhedron_LumpedMemory *)pFinalDest;
m_BrushPolyhedrons[i] = pDest;
pFinalDest += memRequired;
int memoryOffset = ((uint8 *)pDest) - ((uint8 *)pSource);
memcpy( pDest, pSource, memRequired );
//move all the pointers to their new location.
pDest->pVertices = (Vector *)(((uint8 *)(pDest->pVertices)) + memoryOffset);
pDest->pLines = (Polyhedron_IndexedLine_t *)(((uint8 *)(pDest->pLines)) + memoryOffset);
pDest->pIndices = (Polyhedron_IndexedLineReference_t *)(((uint8 *)(pDest->pIndices)) + memoryOffset);
pDest->pPolygons = (Polyhedron_IndexedPolygon_t *)(((uint8 *)(pDest->pPolygons)) + memoryOffset);
}
}
}
unsigned int iBrushWorkSpaces = workSpacesAllocated;
workSpacesAllocated = 1;
pCurrentWorkSpace = workSpaceAllocations[0];
usedSpaceInWorkspace[0] = 0;
roomLeftInWorkSpace = workSpaceSize;
//static props
{
CUtlVector<ICollideable *> StaticPropCollideables;
staticpropmgr->GetAllStaticProps( &StaticPropCollideables );
if( StaticPropCollideables.Count() != 0 )
{
ICollideable **pCollideables = StaticPropCollideables.Base();
ICollideable **pStop = pCollideables + StaticPropCollideables.Count();
int iStaticPropIndex = 0;
do
{
ICollideable *pProp = *pCollideables;
vcollide_t *pCollide = modelinfo->GetVCollide( pProp->GetCollisionModel() );
StaticPropPolyhedronCacheInfo_t cacheInfo;
cacheInfo.iStartIndex = m_StaticPropPolyhedrons.Count();
if( pCollide != NULL )
{
VMatrix matToWorldPosition = pProp->CollisionToWorldTransform();
for( int i = 0; i != pCollide->solidCount; ++i )
{
CPhysConvex *ConvexesArray[1024];
int iConvexes = physcollision->GetConvexesUsedInCollideable( pCollide->solids[i], ConvexesArray, 1024 );
for( int j = 0; j != iConvexes; ++j )
{
CPolyhedron *pTempPolyhedron = physcollision->PolyhedronFromConvex( ConvexesArray[j], true );
if( pTempPolyhedron )
{
for( int iPointCounter = 0; iPointCounter != pTempPolyhedron->iVertexCount; ++iPointCounter )
pTempPolyhedron->pVertices[iPointCounter] = matToWorldPosition * pTempPolyhedron->pVertices[iPointCounter];
for( int iPolyCounter = 0; iPolyCounter != pTempPolyhedron->iPolygonCount; ++iPolyCounter )
pTempPolyhedron->pPolygons[iPolyCounter].polyNormal = matToWorldPosition.ApplyRotation( pTempPolyhedron->pPolygons[iPolyCounter].polyNormal );
size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
(sizeof( Vector ) * pTempPolyhedron->iVertexCount) +
(sizeof( Polyhedron_IndexedLine_t ) * pTempPolyhedron->iLineCount) +
(sizeof( Polyhedron_IndexedLineReference_t ) * pTempPolyhedron->iIndexCount) +
(sizeof( Polyhedron_IndexedPolygon_t ) * pTempPolyhedron->iPolygonCount);
Assert( memRequired < workSpaceSize );
if( roomLeftInWorkSpace < memRequired )
{
usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
if( workSpacesAllocated < iBrushWorkSpaces )
{
//re-use a workspace already allocated during brush polyhedron conversion
pCurrentWorkSpace = workSpaceAllocations[workSpacesAllocated];
usedSpaceInWorkspace[workSpacesAllocated] = 0;
}
else
{
//allocate a new workspace
pCurrentWorkSpace = new uint8 [workSpaceSize];
workSpaceAllocations[workSpacesAllocated] = pCurrentWorkSpace;
usedSpaceInWorkspace[workSpacesAllocated] = 0;
}
roomLeftInWorkSpace = workSpaceSize;
++workSpacesAllocated;
}
CPolyhedron *pWorkSpacePolyhedron = CPolyhedron_LumpedMemory::AllocateAt( pCurrentWorkSpace,
pTempPolyhedron->iVertexCount,
pTempPolyhedron->iLineCount,
pTempPolyhedron->iIndexCount,
pTempPolyhedron->iPolygonCount );
pCurrentWorkSpace += memRequired;
roomLeftInWorkSpace -= memRequired;
memcpy( pWorkSpacePolyhedron->pVertices, pTempPolyhedron->pVertices, pTempPolyhedron->iVertexCount * sizeof( Vector ) );
memcpy( pWorkSpacePolyhedron->pLines, pTempPolyhedron->pLines, pTempPolyhedron->iLineCount * sizeof( Polyhedron_IndexedLine_t ) );
memcpy( pWorkSpacePolyhedron->pIndices, pTempPolyhedron->pIndices, pTempPolyhedron->iIndexCount * sizeof( Polyhedron_IndexedLineReference_t ) );
memcpy( pWorkSpacePolyhedron->pPolygons, pTempPolyhedron->pPolygons, pTempPolyhedron->iPolygonCount * sizeof( Polyhedron_IndexedPolygon_t ) );
m_StaticPropPolyhedrons.AddToTail( pWorkSpacePolyhedron );
#ifdef _DEBUG
CPhysConvex *pConvex = physcollision->ConvexFromConvexPolyhedron( *pTempPolyhedron );
AssertMsg( pConvex != NULL, "Conversion from Convex to Polyhedron was unreversable" );
if( pConvex )
{
physcollision->ConvexFree( pConvex );
}
#endif
pTempPolyhedron->Release();
}
}
}
cacheInfo.iNumPolyhedrons = m_StaticPropPolyhedrons.Count() - cacheInfo.iStartIndex;
cacheInfo.iStaticPropIndex = iStaticPropIndex;
Assert( staticpropmgr->GetStaticPropByIndex( iStaticPropIndex ) == pProp );
m_CollideableIndicesMap.InsertOrReplace( pProp, cacheInfo );
}
++iStaticPropIndex;
++pCollideables;
} while( pCollideables != pStop );
usedSpaceInWorkspace[workSpacesAllocated - 1] = workSpaceSize - roomLeftInWorkSpace;
if( usedSpaceInWorkspace[0] != 0 ) //At least a little bit of memory was used.
{
//consolidate workspaces into a single memory chunk
size_t totalMemoryNeeded = 0;
for( unsigned int i = 0; i != workSpacesAllocated; ++i )
{
totalMemoryNeeded += usedSpaceInWorkspace[i];
}
uint8 *pFinalDest = new uint8 [totalMemoryNeeded];
s_StaticPropPolyhedronMemory = pFinalDest;
DevMsg( 2, "CStaticCollisionPolyhedronCache: Used %.2f KB to cache %d static prop polyhedrons.\n", ((float)totalMemoryNeeded) / 1024.0f, m_StaticPropPolyhedrons.Count() );
int iCount = m_StaticPropPolyhedrons.Count();
for( int i = 0; i != iCount; ++i )
{
CPolyhedron_LumpedMemory *pSource = (CPolyhedron_LumpedMemory *)m_StaticPropPolyhedrons[i];
size_t memRequired = (sizeof( CPolyhedron_LumpedMemory )) +
(sizeof( Vector ) * pSource->iVertexCount) +
(sizeof( Polyhedron_IndexedLine_t ) * pSource->iLineCount) +
(sizeof( Polyhedron_IndexedLineReference_t ) * pSource->iIndexCount) +
(sizeof( Polyhedron_IndexedPolygon_t ) * pSource->iPolygonCount);
CPolyhedron_LumpedMemory *pDest = (CPolyhedron_LumpedMemory *)pFinalDest;
m_StaticPropPolyhedrons[i] = pDest;
pFinalDest += memRequired;
intp memoryOffset = ((uint8 *)pDest) - ((uint8 *)pSource);
memcpy( pDest, pSource, memRequired );
//move all the pointers to their new location.
pDest->pVertices = (Vector *)(((uint8 *)(pDest->pVertices)) + memoryOffset);
pDest->pLines = (Polyhedron_IndexedLine_t *)(((uint8 *)(pDest->pLines)) + memoryOffset);
pDest->pIndices = (Polyhedron_IndexedLineReference_t *)(((uint8 *)(pDest->pIndices)) + memoryOffset);
pDest->pPolygons = (Polyhedron_IndexedPolygon_t *)(((uint8 *)(pDest->pPolygons)) + memoryOffset);
}
}
}
}
if( iBrushWorkSpaces > workSpacesAllocated )
workSpacesAllocated = iBrushWorkSpaces;
for( unsigned int i = 0; i != workSpacesAllocated; ++i )
{
delete []workSpaceAllocations[i];
}
}
const CPolyhedron *CStaticCollisionPolyhedronCache::GetBrushPolyhedron( int iBrushNumber )
{
Assert( iBrushNumber < m_BrushPolyhedrons.Count() );
if( (iBrushNumber < 0) || (iBrushNumber >= m_BrushPolyhedrons.Count()) )
return NULL;
return m_BrushPolyhedrons[iBrushNumber];
}
int CStaticCollisionPolyhedronCache::GetStaticPropPolyhedrons( ICollideable *pStaticProp, CPolyhedron **pOutputPolyhedronArray, int iOutputArraySize )
{
unsigned short iPropIndex = m_CollideableIndicesMap.Find( pStaticProp );
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)
return 0;
StaticPropPolyhedronCacheInfo_t cacheInfo = m_CollideableIndicesMap.Element( iPropIndex );
if( cacheInfo.iNumPolyhedrons < iOutputArraySize )
iOutputArraySize = cacheInfo.iNumPolyhedrons;
for( int i = cacheInfo.iStartIndex, iWriteIndex = 0; iWriteIndex != iOutputArraySize; ++i, ++iWriteIndex )
{
pOutputPolyhedronArray[iWriteIndex] = m_StaticPropPolyhedrons[i];
}
return iOutputArraySize;
}