//========= 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 ;
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 ;
}
}
}
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
{
int iBrush = 0 ;
CUtlVector < Vector4D > Planes ;
float fStackPlanes [ 4 * 400 ] ; //400 is a crapload of planes in my opinion
while ( enginetrace - > GetBrushInfo ( iBrush , & Planes , NULL ) )
{
int iPlaneCount = Planes . Count ( ) ;
AssertMsg ( iPlaneCount ! = 0 , " A brush with no planes??????? " ) ;
const Vector4D * pReturnedPlanes = Planes . Base ( ) ;
CPolyhedron * pTempPolyhedron ;
if ( iPlaneCount > 400 )
{
// o_O, we'll have to get more memory to transform this brush
float * pNonstackPlanes = new float [ 4 * iPlaneCount ] ;
for ( int i = 0 ; i ! = iPlaneCount ; + + i )
{
pNonstackPlanes [ ( i * 4 ) + 0 ] = pReturnedPlanes [ i ] . x ;
pNonstackPlanes [ ( i * 4 ) + 1 ] = pReturnedPlanes [ i ] . y ;
pNonstackPlanes [ ( i * 4 ) + 2 ] = pReturnedPlanes [ i ] . z ;
pNonstackPlanes [ ( i * 4 ) + 3 ] = pReturnedPlanes [ i ] . w ;
}
pTempPolyhedron = GeneratePolyhedronFromPlanes ( pNonstackPlanes , iPlaneCount , 0.01f , true ) ;
delete [ ] pNonstackPlanes ;
}
else
{
for ( int i = 0 ; i ! = iPlaneCount ; + + i )
{
fStackPlanes [ ( i * 4 ) + 0 ] = pReturnedPlanes [ i ] . x ;
fStackPlanes [ ( i * 4 ) + 1 ] = pReturnedPlanes [ i ] . y ;
fStackPlanes [ ( i * 4 ) + 2 ] = pReturnedPlanes [ i ] . z ;
fStackPlanes [ ( i * 4 ) + 3 ] = pReturnedPlanes [ i ] . w ;
}
pTempPolyhedron = GeneratePolyhedronFromPlanes ( fStackPlanes , iPlaneCount , 0.01f , true ) ;
}
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 ;
}
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 ;
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 ) ;
}
}
}
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 ;
}