csgo-2018-source/fow/fow.cpp
2021-07-24 21:11:47 -07:00

1774 lines
53 KiB
C++

#include "fow.h"
#include "fow_radiusoccluder.h"
#include "fow_viewer.h"
#include "fow_trisoup.h"
#include "fow_horizontalslice.h"
#include "keyvalues.h"
#include "utlbuffer.h"
#include "filesystem.h"
#include "vstdlib/jobthread.h"
#include "vphysics_interface.h"
#include "gametrace.h"
#include "vprof.h"
#include "engine/IVDebugOverlay.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
extern IVDebugOverlay *debugoverlay;
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CFoW::CFoW( ) :
m_Occluders( 100, 100 ),
m_Viewers( 100, 100 ),
m_TriSoupCollection( 50, 50 ),
m_RadiusTables( 0, 0, DefLessFunc( float ) )
{
m_nNumberOfTeams = 0;
for ( int i = 0; i < MAX_FOW_TEAMS; i++ )
{
m_pVisibilityGridFlags[ i ] = NULL;
m_pVisibilityGridDegree[ i ] = NULL;
m_pVisibilityFadeTimer[ i ] = NULL;
}
m_pHorizontalSlices = NULL;
m_pVerticalLevels = NULL;
m_flDegreeFadeRate = 2.0;
m_nGridZUnits = 0;
m_bInitialized = false;
m_bDebugVisible = false;
m_nDebugFlags = 0;
m_nHorizontalGridAllocationSize = m_nVerticalGridAllocationSize = 0;
m_nRadiusTableSize = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
CFoW::~CFoW( )
{
ClearState();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CFoW::ClearState( )
{
for ( int i = 0; i < MAX_FOW_TEAMS; i++ )
{
if ( m_pVisibilityGridFlags[ i ] )
{
free( m_pVisibilityGridFlags[ i ] );
m_pVisibilityGridFlags[ i ] = NULL;
free( m_pVisibilityGridDegree[ i ] );
m_pVisibilityGridDegree[ i ] = NULL;
free( m_pVisibilityFadeTimer[ i ] );
m_pVisibilityFadeTimer[ i ] = NULL;
}
}
if ( m_pHorizontalSlices )
{
for ( int i = 0; i < m_nGridZUnits; i++ )
{
delete m_pHorizontalSlices[ i ];
}
delete m_pHorizontalSlices;
m_pHorizontalSlices = NULL;
}
m_nGridZUnits = 0;
if ( m_pVerticalLevels )
{
delete m_pVerticalLevels;
m_pVerticalLevels = NULL;
}
m_Occluders.PurgeAndDeleteElements();
m_Viewers.PurgeAndDeleteElements();
m_TriSoupCollection.PurgeAndDeleteElements();
for( unsigned int i = 0; i < m_RadiusTables.Count(); i++ )
{
if ( m_RadiusTables.IsValidIndex( i ) == false )
{
continue;
}
free( m_RadiusTables.Element( i ) );
}
m_RadiusTables.RemoveAll();
m_ViewerTree.Purge();
m_OccluderTree.Purge();
m_nHorizontalGridAllocationSize = m_nVerticalGridAllocationSize = 0;
m_nRadiusTableSize = 0;
m_bInitialized = false;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the number of viewer teams
// Input : nCount - the maximum number of teams
//-----------------------------------------------------------------------------
void CFoW::SetNumberOfTeams( int nCount )
{
Assert( nCount > 0 && nCount <= MAX_FOW_TEAMS );
m_nNumberOfTeams = nCount;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the world mins/maxs and how big the grid sizes should be
// Input : vWorldMins - the world minimums
// vWorldMaxs - the world maximums
// nHorizontalGridSize - the horizontal size the world should be chopped up by ( world xy size / this value ) rounded up
// nVerticalGridSize - the vertical size the world should be chopped up by ( world z size / this value ) rounded up
//-----------------------------------------------------------------------------
void CFoW::SetSize( Vector &vWorldMins, Vector &vWorldMaxs, int nHorizontalGridSize, int nVerticalGridSize )
{
Assert( m_nNumberOfTeams > 0 );
m_nHorizontalGridAllocationSize = m_nVerticalGridAllocationSize = 0;
m_vWorldMins = vWorldMins;
m_vWorldMaxs = vWorldMaxs;
m_nHorizontalGridSize = nHorizontalGridSize;
m_nVerticalGridSize = nVerticalGridSize;
m_nGridXUnits = ( ( m_vWorldMaxs.x - m_vWorldMins.x ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
m_nGridYUnits = ( ( m_vWorldMaxs.y - m_vWorldMins.y ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
m_vWorldMaxs.x = m_vWorldMins.x + ( m_nGridXUnits * m_nHorizontalGridSize );
m_vWorldMaxs.y = m_vWorldMins.y + ( m_nGridYUnits * m_nHorizontalGridSize );
m_nTotalHorizontalUnits = m_nGridXUnits * m_nGridYUnits;
int nDegreeAllocationSize = sizeof( *m_pVisibilityGridDegree[ 0 ] ) * m_nTotalHorizontalUnits;
int nFlagAllocationSize = sizeof( *m_pVisibilityGridFlags[ 0 ] ) * m_nTotalHorizontalUnits;
for ( int i = 0; i < m_nNumberOfTeams; i++ )
{
m_pVisibilityGridFlags[ i ] = ( byte * )malloc( nFlagAllocationSize );
memset( m_pVisibilityGridFlags[ i ], 0, nFlagAllocationSize );
m_nHorizontalGridAllocationSize += nFlagAllocationSize;
m_pVisibilityGridDegree[ i ] = ( float * )malloc( nDegreeAllocationSize );
m_nHorizontalGridAllocationSize += nDegreeAllocationSize;
m_pVisibilityFadeTimer[ i ] = ( float * )malloc( nDegreeAllocationSize );
m_nHorizontalGridAllocationSize += nDegreeAllocationSize;
// memset( m_pVisibilityGridDegree[ i ], 0, nDegreeAllocationSize );
for( int j = 0; j < m_nTotalHorizontalUnits; j++ )
{
m_pVisibilityGridDegree[ i ][ j ] = 0.0f;
m_pVisibilityFadeTimer[ i ][ j ] = 0.0f;
}
}
if ( m_nVerticalGridSize > 0 )
{
m_nGridZUnits = ( ( m_vWorldMaxs.z - m_vWorldMins.z ) + m_nVerticalGridSize - 1 ) / m_nVerticalGridSize;
m_pVerticalLevels = ( float * )malloc( sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits );
m_pHorizontalSlices = ( CFoW_HorizontalSlice ** )malloc( sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits );
for ( int i = 0; i < m_nGridZUnits; i++ )
{
m_pHorizontalSlices[ i ] = new CFoW_HorizontalSlice();
m_nVerticalGridAllocationSize += sizeof( CFoW_HorizontalSlice );
m_pVerticalLevels[ i ] = m_vWorldMins.z + ( ( i + 0.75f ) * m_nVerticalGridSize );
}
m_nVerticalGridAllocationSize += sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits;
m_nVerticalGridAllocationSize += sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits;
}
m_bInitialized = true;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
//-----------------------------------------------------------------------------
void CFoW::SetCustomVerticalLevels( float *pflHeightLevels, int nCount )
{
m_nGridZUnits = nCount;
m_nVerticalGridSize = -1;
m_pVerticalLevels = ( float * )malloc( sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits );
m_pHorizontalSlices = ( CFoW_HorizontalSlice ** )malloc( sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits );
for ( int i = 0; i < m_nGridZUnits; i++ )
{
m_pHorizontalSlices[ i ] = new CFoW_HorizontalSlice();
m_nVerticalGridAllocationSize += sizeof( CFoW_HorizontalSlice );
m_pVerticalLevels[ i ] = pflHeightLevels[ i ];
}
m_nVerticalGridAllocationSize += sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits;
m_nVerticalGridAllocationSize += sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits;
}
//-----------------------------------------------------------------------------
// Purpose: get the world size of the FoW
// Output : vWorldMins - the world minimums
// vWorldMaxs - the world maximums
//-----------------------------------------------------------------------------
void CFoW::GetSize( Vector &vWorldMins, Vector &vWorldMaxs )
{
vWorldMins = m_vWorldMins;
vWorldMaxs = m_vWorldMaxs;
}
//-----------------------------------------------------------------------------
// Purpose: get the lower vertical coord, the grid size, and grid units
// Output : nBottomZ - the world minimum z value
// nGridSize - the size the world is chopped up by
// nGridUnits - the number of units the world has been chopped into
//-----------------------------------------------------------------------------
void CFoW::GetVerticalGridInfo( int &nBottomZ, int &nGridSize, int &nGridUnits, float **pVerticalLevels )
{
nBottomZ = m_vWorldMins.z;
nGridSize = m_nVerticalGridSize;
nGridUnits = m_nGridZUnits;
*pVerticalLevels = m_pVerticalLevels;
}
//-----------------------------------------------------------------------------
// Purpose: snap the x/y coordinates to the grid
// Input : vIn - the input coordinates
// bGoLower - should we snap to the left/bottom or right/top of the grid
// Output : vOut - the output coordinates snapped to the grid. z is unsnapped.
//-----------------------------------------------------------------------------
void CFoW::SnapCoordsToGrid( Vector &vIn, Vector &vOut, bool bGoLower )
{
if ( bGoLower )
{
vOut.x = m_vWorldMins.x + ( floor( ( vIn.x - m_vWorldMins.x ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
vOut.y = m_vWorldMins.y + ( floor( ( vIn.y - m_vWorldMins.y ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
vOut.z = vIn.z;
}
else
{
vOut.x = m_vWorldMins.x + ( ceil( ( vIn.x - m_vWorldMins.x ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
vOut.y = m_vWorldMins.y + ( ceil( ( vIn.y - m_vWorldMins.y ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
vOut.z = vIn.z;
}
vOut.x = clamp( vOut.x, m_vWorldMins.x, m_vWorldMaxs.x );
vOut.y = clamp( vOut.y, m_vWorldMins.y, m_vWorldMaxs.y );
}
//-----------------------------------------------------------------------------
// Purpose: return how visible a cell is ( 0.0 = not currently visible, 1.0 = fully visible )
// Input : nXLoc - the world x location
// nYLoc - the world y location
// nTeam - which team to look up
// Output : returns the visibility degree
//-----------------------------------------------------------------------------
float CFoW::LookupVisibilityDegree( int nXLoc, int nYLoc, int nTeam )
{
int x = ( nXLoc - m_vWorldMins.x ) / m_nHorizontalGridSize;
int y = ( nYLoc - m_vWorldMins.y ) / m_nHorizontalGridSize;
x = clamp( x, 0, m_nGridXUnits - 1 );
y = clamp( y, 0, m_nGridYUnits - 1 );
float flValue = m_pVisibilityGridDegree[ nTeam ][ ( x * m_nGridYUnits ) + y ];
return ( flValue > 1.0f ? 1.0f : flValue );
}
//-----------------------------------------------------------------------------
// Purpose: creates or returns a grid to radius table
// Input : flRadius - the size of the radius
// Output : returns the visibility table to go from grid to radius
//-----------------------------------------------------------------------------
int *CFoW::FindRadiusTable( float flRadius )
{
int nIndex = m_RadiusTables.Find( flRadius );
if ( m_RadiusTables.IsValidIndex( nIndex ) )
{
return m_RadiusTables[ nIndex ];
}
int nGridUnits = ( ( flRadius * 2 ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
nGridUnits |= 1; // always make it odd, so that we have a true center
int nSize = sizeof( int ) * nGridUnits * nGridUnits;
int *pVisibilityData = ( int * )malloc( nSize );
memset( pVisibilityData, -1, nSize );
m_nRadiusTableSize += nSize;
int nOffset = ( nGridUnits / 2 ) * m_nHorizontalGridSize;
int nRadiusUnits = ( ( 2 * M_PI * flRadius ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
int *pVisibility = pVisibilityData;
for ( int x = 0, xPos = -nOffset; x < nGridUnits; x++, xPos += m_nHorizontalGridSize )
{
for ( int y = 0, yPos = -nOffset; y < nGridUnits; y++, yPos += m_nHorizontalGridSize, pVisibility++ )
{
float flDist = sqrt( ( float )( ( xPos * xPos ) + ( yPos * yPos ) ) );
if ( flDist > flRadius )
{
*pVisibility = -1;
continue;
}
float nx = xPos / flDist;
float ny = yPos / flDist;
float flAngle = ( 0.0f * nx ) + ( 1.0f * ny );
float flRealAngle = RAD2DEG( acos( flAngle ) );
if ( nx < 0.0f )
{
flRealAngle = 360 - flRealAngle;
}
flRealAngle = ( flRealAngle / 360.0f ) * nRadiusUnits;
*pVisibility = ( int )flRealAngle;
}
}
m_RadiusTables.Insert( flRadius, pVisibilityData );
return pVisibilityData;
}
//-----------------------------------------------------------------------------
// Purpose: adds a new viewer to the system
// Input : nViewerTeam - the team the viewer is on
// Output : returns the id of the new viewer
//-----------------------------------------------------------------------------
int CFoW::AddViewer( unsigned nViewerTeam )
{
int nSlotID = -1;
// optimize this!
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] == NULL )
{
nSlotID = i;
break;
}
}
if ( nSlotID == -1 )
{
nSlotID = m_Viewers.Count();
m_Viewers.AddToTail( NULL );
}
CFoW_Viewer *pViewer = new CFoW_Viewer( nSlotID, nViewerTeam );
m_Viewers[ nSlotID ] = pViewer;
InsertViewerIntoTree( nSlotID );
return nSlotID;
}
//-----------------------------------------------------------------------------
// Purpose: removes a viewer from the system
// Input : nID - the id of the viewer
//-----------------------------------------------------------------------------
void CFoW::RemoveViewer( int nID )
{
if ( m_Viewers[ nID ] != NULL )
{
RemoveViewerFromTree( nID );
delete m_Viewers[ nID ];
m_Viewers[ nID ] = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: updates the viewer's location
// Input : nID - the id of the viewer
// vLocation - the new location of the viewer
//-----------------------------------------------------------------------------
void CFoW::UpdateViewerLocation( int nID, const Vector &vLocation )
{
Vector vOldLocation;
Assert( m_Viewers[ nID ] );
#ifdef FOW_SAFETY_DANCE
if ( m_Viewers[ nID ] == NULL )
{
Warning( "CFoW: UpdateViewerLocation( %d, ( %g, %g %g ) ) has missing viewer\n", nID, vLocation.x, vLocation.y, vLocation.z );
return;
}
#endif
if ( m_Viewers[ nID ]->UpdateLocation( this, vLocation, &vOldLocation ) == true )
{
RemoveViewerFromTree( nID, &vOldLocation );
InsertViewerIntoTree( nID );
}
}
//-----------------------------------------------------------------------------
// Purpose: updates the viewer's seeing radius
// Input : nID - the id of the viewer
// flRadius - the new radius of the viewer
//-----------------------------------------------------------------------------
void CFoW::UpdateViewerSize( int nID, float flRadius )
{
Assert( m_Viewers[ nID ] );
#ifdef FOW_SAFETY_DANCE
if ( m_Viewers[ nID ] == NULL )
{
Warning( "CFoW: UpdateViewerSize( %d, %g ) has missing viewer\n", nID, flRadius );
return;
}
#endif
RemoveViewerFromTree( nID );
m_Viewers[ nID ]->UpdateSize( this, flRadius );
InsertViewerIntoTree( nID );
}
//-----------------------------------------------------------------------------
// Purpose: updates the viewer's seeing radius
// Input : nID - the id of the viewer
// nHeightGroup - the new height group of the viewer
//-----------------------------------------------------------------------------
void CFoW::UpdateViewerHeightGroup( int nID, uint8 nHeightGroup )
{
Assert( m_Viewers[ nID ] );
#ifdef FOW_SAFETY_DANCE
if ( m_Viewers[ nID ] == NULL )
{
Warning( "CFoW: UpdateViewerHeightGroup( %d, %d ) has missing viewer\n", nID, ( int )nHeightGroup );
return;
}
#endif
m_Viewers[ nID ]->UpdateHeightGroup( nHeightGroup );
}
//-----------------------------------------------------------------------------
// Purpose: adds a new radius occluder to the system
// Input : nPermanent - unused
// Output : returns the id of the new occluder
//-----------------------------------------------------------------------------
int CFoW::AddOccluder( bool nPermanent )
{
int nSlotID = -1;
// optimize this!
for ( int i = 0; i < m_Occluders.Count(); i++ )
{
if ( m_Occluders[ i ] == NULL )
{
nSlotID = i;
break;
}
}
if ( nSlotID == -1 )
{
nSlotID = m_Occluders.Count();
m_Occluders.AddToTail( NULL );
}
CFoW_RadiusOccluder *pOccluder = new CFoW_RadiusOccluder( nSlotID );
m_Occluders[ nSlotID ] = pOccluder;
InsertOccluderIntoTree( nSlotID );
return nSlotID;
}
//-----------------------------------------------------------------------------
// Purpose: removes an occluder from the system
// Input : nID - the id of the occluder
//-----------------------------------------------------------------------------
void CFoW::RemoveOccluder( int nID )
{
if ( m_Occluders[ nID ] != NULL )
{
RemoveOccluderFromTree( nID );
DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
delete m_Occluders[ nID ];
m_Occluders[ nID ] = NULL;
// RepopulateOccluders(); // CUtlSphereTree has no delete function for now
}
}
void CFoW::EnableOccluder( int nID, bool bEnable )
{
#ifdef FOW_SAFETY_DANCE
if ( m_Occluders[ nID ] == NULL )
{
Warning( "CFoW: EnableOccluder( %d, %s ) has missing occluder\n", nID, bEnable ? "true" : "false" );
return;
}
#endif
m_Occluders[ nID ]->SetEnable( bEnable );
DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
}
//-----------------------------------------------------------------------------
// Purpose: update an occluder's location
// Input : nID - the id of the occluder
// vLocation - the new location of the occluder
//-----------------------------------------------------------------------------
void CFoW::UpdateOccluderLocation( int nID, Vector &vLocation )
{
Assert( m_Occluders[ nID ] );
#ifdef FOW_SAFETY_DANCE
if ( m_Occluders[ nID ] == NULL )
{
Warning( "CFoW: UpdateOccluderLocation( %d, ( %g, %g, %g ) ) has missing occluder\n", nID, vLocation.x, vLocation.y, vLocation.z );
return;
}
#endif
RemoveOccluderFromTree( nID );
DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
m_Occluders[ nID ]->UpdateLocation( vLocation );
// RepopulateOccluders(); // CUtlSphereTree has no delete function for now
InsertOccluderIntoTree( nID );
DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
}
//-----------------------------------------------------------------------------
// Purpose: update an occluder's size
// Input : nID - the id of the occluder
// flRadius - the new radius of the occluder
//-----------------------------------------------------------------------------
void CFoW::UpdateOccluderSize( int nID, float flRadius )
{
Assert( m_Occluders[ nID ] );
#ifdef FOW_SAFETY_DANCE
if ( m_Occluders[ nID ] == NULL )
{
Warning( "CFoW: UpdateOccluderSize( %d, %g ) has missing occluder\n", nID, flRadius );
return;
}
#endif
// RepopulateOccluders(); // CUtlSphereTree has no delete function for now
RemoveOccluderFromTree( nID );
DirtyViewers( m_Occluders[ nID ]->GetLocation(), ( m_Occluders[ nID ]->GetSize() > flRadius ? m_Occluders[ nID ]->GetSize() : flRadius ) );
m_Occluders[ nID ]->UpdateSize( flRadius );
InsertOccluderIntoTree( nID );
}
//-----------------------------------------------------------------------------
// Purpose: updates the occluder's height group
// Input : nID - the id of the occluder
// nHeightGroup - the new height group of the occluder
//-----------------------------------------------------------------------------
void CFoW::UpdateOccluderHeightGroup( int nID, uint8 nHeightGroup )
{
Assert( m_Occluders[ nID ] );
#ifdef FOW_SAFETY_DANCE
if ( m_Occluders[ nID ] == NULL )
{
Warning( "CFoW: UpdateOccluderHeightGroup( %d, %d ) has missing occluder\n", nID, ( int )nHeightGroup );
return;
}
#endif
// RepopulateOccluders(); // CUtlSphereTree has no delete function for now
DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
m_Occluders[ nID ]->UpdateHeightGroup( nHeightGroup );
}
//-----------------------------------------------------------------------------
// Purpose: internal function called by viewers to radius occlude nearby objects
// Input : nViewerID - the id of the viewer to obstruct
//-----------------------------------------------------------------------------
void CFoW::ObstructOccludersNearViewer( int nViewerID )
{
Assert( m_Viewers[ nViewerID ] );
CFoW_Viewer *pViewer = m_Viewers[ nViewerID ];
Sphere_t TestSphere( pViewer->GetLocation().x, pViewer->GetLocation().y, pViewer->GetLocation().z, pViewer->GetSize() );
CFoW_RadiusOccluder *FixedPointerArray[ FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK ];
CUtlVector< void * > FoundOccluders( ( void ** )FixedPointerArray, FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK );
int RealCount = m_OccluderTree.IntersectWithSphere( TestSphere, true, FoundOccluders, FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK );
if ( RealCount > FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK )
{
// we overflowed, what should we do?
Assert( 0 );
}
// Msg( "Slice Counts: %d / %d\n", FoundOccluders.Count(), RealCount );
for ( int i = 0; i < FoundOccluders.Count(); i++ )
{
FixedPointerArray[ i ]->ObstructViewerRadius( this, pViewer );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
//-----------------------------------------------------------------------------
void CFoW::SetWorldCollision( CPhysCollide *pCollideable, IPhysicsCollision *pPhysCollision )
{
for( int x = 0; x < m_nGridXUnits; x++ )
{
float flXPos = ( x * m_nHorizontalGridSize ) + m_vWorldMins.x + ( m_nHorizontalGridSize / 2.0f );
for( int y = 0; y < m_nGridYUnits; y++ )
{
float flYPos = ( y * m_nHorizontalGridSize ) + m_vWorldMins.y + ( m_nHorizontalGridSize / 2.0f );
Vector vStart( flXPos, flYPos, 99999.0f ), vEnd( flXPos, flYPos, -99999.0f );
Vector vResultOrigin;
QAngle vResultAngles;
trace_t TraceResult;
pPhysCollision->TraceBox( vStart, vEnd, vec3_origin, vec3_origin, pCollideable, vResultOrigin, vResultAngles, &TraceResult );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: adds a tri soup collection to the system
// Output : returns the id of the new tri soup collection
//-----------------------------------------------------------------------------
int CFoW::AddTriSoup( )
{
int nSlotID = -1;
// optimize this!
for ( int i = 0; i < m_TriSoupCollection.Count(); i++ )
{
if ( m_TriSoupCollection[ i ] == NULL )
{
nSlotID = i;
break;
}
}
if ( nSlotID == -1 )
{
nSlotID = m_TriSoupCollection.Count();
m_TriSoupCollection.AddToTail( NULL );
}
CFoW_TriSoupCollection *pTriSoup = new CFoW_TriSoupCollection( nSlotID );
m_TriSoupCollection[ nSlotID ] = pTriSoup;
return nSlotID;
}
//-----------------------------------------------------------------------------
// Purpose: removes a tri soup collection from the system
// Input : nID - the id of the tri soup
//-----------------------------------------------------------------------------
void CFoW::RemoveTriSoup( int nID )
{
if ( m_TriSoupCollection[ nID ] != NULL )
{
delete m_TriSoupCollection[ nID ];
m_TriSoupCollection[ nID ] = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: clears all entries from the collection ( useful for hammer editing only )
// Input : nID - the id of the tri soup
//-----------------------------------------------------------------------------
void CFoW::ClearTriSoup( int nID )
{
Assert( m_TriSoupCollection[ nID ] );
m_TriSoupCollection[ nID ]->Clear();
for ( int i = 0; i < m_nGridZUnits; i++ )
{
m_pHorizontalSlices[ i ]->Clear();
}
for ( int i = 0; i < m_TriSoupCollection.Count(); i++ )
{
if ( m_TriSoupCollection[ i ] )
{
m_TriSoupCollection[ i ]->RepopulateOccluders( this );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: adds a tri to the collection. this is immediately split up into the horizontal slices. very slow!
// Input : nID - the id of the tri soup
// vPointA - a point on the tri
// vPointB - a point on the tri
// vPointC - a point on the tri
//-----------------------------------------------------------------------------
void CFoW::AddTri( int nID, Vector &vPointA, Vector &vPointB, Vector &vPointC )
{
Assert( m_TriSoupCollection[ nID ] );
m_TriSoupCollection[ nID ]->AddTri( this, vPointA, vPointB, vPointC );
}
//-----------------------------------------------------------------------------
// Purpose: get access to a tri soup collection object
// Input : nID - the id of the tri soup
// Output : returns the tri soup collection object
//-----------------------------------------------------------------------------
CFoW_TriSoupCollection *CFoW::GetTriSoup( int nID )
{
Assert( m_TriSoupCollection[ nID ] );
return m_TriSoupCollection[ nID ];
}
//-----------------------------------------------------------------------------
// Purpose: add a line occulder from a horizontal slice
// Input : pOccluder - the line occluder to add
// nSliceNum - which slice to add the occluder on
//-----------------------------------------------------------------------------
void CFoW::AddTriSoupOccluder( CFoW_LineOccluder *pOccluder, int nSliceNum )
{
m_pHorizontalSlices[ nSliceNum ]->AddHorizontalOccluder( pOccluder );
}
//-----------------------------------------------------------------------------
// Purpose: get the slice index given the vertical position
// Input : flZPos - the world z position to find the slice for
// Output : returns the slice index or -1 if the position is out of range
//-----------------------------------------------------------------------------
int CFoW::GetHorizontalSlice( float flZPos )
{
if ( m_nVerticalGridSize == 0 || m_pHorizontalSlices == NULL )
{
return -1;
}
#if 0
int nIndex = ( int )( ( flZPos - m_vWorldMins.z ) / m_nVerticalGridSize );
if ( nIndex < 0 )
{ // we are getting a z position outside of our world size - potentially bad
return 0;
}
else if ( nIndex >= m_nGridZUnits )
{ // we are getting a z position outside of our world size - potentially bad
return m_nGridZUnits - 1;
}
#endif
for ( int nSlice = 0; nSlice < m_nGridZUnits; nSlice++ )
{
if ( flZPos < m_pVerticalLevels[ nSlice ] )
{
return nSlice;
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void PrepVisibilityThreaded( CFoW *pFoW )
{
pFoW->PrepVisibility();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CalcLocalizedVisibilityThreaded( CFoW *pFoW, CFoW_Viewer *pViewer )
{
pViewer->CalcLocalizedVisibility( pFoW );
}
// #define TIME_ME 1
//-----------------------------------------------------------------------------
// Purpose: solve the visibility for all teams and all viewers - slow!
// Input : flFrameTime - the time since the last visibility solve. The amount
// of change in the visibility degree is dependent upon this value.
//-----------------------------------------------------------------------------
void CFoW::SolveVisibility( float flFrameTime )
{
VPROF_BUDGET( "CFoW::SolveVisibility", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED );
#if 1
#ifdef TIME_ME
uint32 nThreadedTime = Plat_MSTime();
#endif // #ifdef TIME_ME
int nRealCount = 1;
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] == NULL )
{
continue;
}
#ifdef FOW_SAFETY_DANCE
if ( m_Viewers[ i ]->GetSize() <= 1.0f )
{
Warning( "CFoW: Viewer %d has invalid radius!\n", i );
continue;
}
#endif
if ( m_Viewers[ i ] != NULL && m_Viewers[ i ]->IsDirty() == true )
{
nRealCount++;
}
}
CJob **pJobs = ( CJob ** )stackalloc( sizeof( CJob * ) * nRealCount );
CThreadEvent **pHandles = ( CThreadEvent ** )stackalloc( sizeof( CThreadEvent * ) * nRealCount );
nRealCount = 0;
pJobs[ nRealCount ] = new CFunctorJob( CreateFunctor( ::PrepVisibilityThreaded, this ) );
pJobs[ nRealCount ]->SetFlags( JF_QUEUE );
g_pThreadPool->AddJob( pJobs[ nRealCount ] );
pHandles[ nRealCount ] = pJobs[ nRealCount ]->AccessEvent();
nRealCount++;
// PrepVisibilityThreaded( this );
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] == NULL )
continue;
if ( m_Viewers[ i ]->GetSize() <= 1.0f )
{
continue;
}
if ( m_Viewers[ i ] != NULL && m_Viewers[ i ]->IsDirty() == true )
{
pJobs[ nRealCount ] = new CFunctorJob( CreateFunctor( ::CalcLocalizedVisibilityThreaded, this, m_Viewers[ i ] ) );
pJobs[ nRealCount ]->SetFlags( JF_QUEUE );
g_pThreadPool->AddJob( pJobs[ nRealCount ] );
pHandles[ nRealCount ] = pJobs[ nRealCount ]->AccessEvent();
nRealCount++;
// CalcLocalizedVisibilityThreaded( this, m_Viewers[ i ] );
}
}
g_pThreadPool->YieldWait( pHandles, nRealCount, true, TT_INFINITE );
#ifdef TIME_ME
uint32 nMergeTime = Plat_MSTime();
#endif // #ifdef TIME_ME
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] )
{
MergeViewerVisibility( i );
}
}
#ifdef TIME_ME
uint32 nUpdateTime = Plat_MSTime();
#endif // #ifdef TIME_ME
UpdateVisibleAmounts( flFrameTime );
#ifdef TIME_ME
uint32 nFinishTime = Plat_MSTime();
Msg( "Thread: %d, Merge: %d, Update: %d, Total %d\n", nMergeTime - nThreadedTime, nUpdateTime - nMergeTime, nFinishTime - nUpdateTime, nFinishTime - nThreadedTime );
#endif // #ifdef TIME_ME
#else
uint32 Time1 = Plat_MSTime();
PrepVisibility();
uint32 Time2 = Plat_MSTime();
double LocalTime = 0.0;
double MergeTime = 0.0;
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] )
{
double t1 = Plat_FloatTime();
m_Viewers[ i ]->CalcLocalizedVisibility( this );
double t2 = Plat_FloatTime();
MergeViewerVisibility( i );
double t3 = Plat_FloatTime();
LocalTime += ( t2 - t1 );
MergeTime += ( t3 - t2 );
}
}
uint32 Time3 = Plat_MSTime();
UpdateVisibleAmounts( flFrameTime );
uint32 Time4 = Plat_MSTime();
Msg( "Prep: %d, Local %lg, Merge: %lg, Update: %d, Total: %d\n", Time2-Time1, LocalTime * 1000, MergeTime * 1000, Time4-Time3, Time4 - Time1 );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: returns the visibility info of a location to a team
// Input : nViewerTeam - the team that is doing the viewing of this location
// vLocation - the world location to get the results
// Output : returns bits associated with the visilbity of the grid location
//-----------------------------------------------------------------------------
uint8 CFoW::GetLocationInfo( unsigned nViewerTeam, const Vector &vLocation )
{
int nIndex = GetGridIndex( vLocation, 0.0f, 0.0f, true );
#ifdef FOW_SAFETY_DANCE
if ( nIndex < 0 || nIndex >= m_nTotalHorizontalUnits )
{
Warning( "CFoW: GetLocationInfo() called with invalid view location of %g, %g, %g\n", vLocation.x, vLocation.y, vLocation.z );
return FOW_VG_INVALID;
}
#endif
byte *pDestPos = m_pVisibilityGridFlags[ nViewerTeam ] + nIndex;
return ( *pDestPos );
}
//-----------------------------------------------------------------------------
// Purpose: returns the visibility info of a location to a team
// Input : nViewerTeam - the team that is doing the viewing of this location
// vLocation - the world location to get the results
// Output : returns bits associated with the visilbity of the grid location
//-----------------------------------------------------------------------------
float CFoW::GetLocationVisibilityDegree( unsigned nViewerTeam, const Vector &vLocation, float flRadius )
{
if ( flRadius <= 1.0f )
{
int nGridX, nGridY;
GetGridUnits( vLocation, 0.0f, 0.0f, true, nGridX, nGridY );
int nIndex = ( ( nGridX * m_nGridYUnits ) + nGridY );
#ifdef FOW_SAFETY_DANCE
if ( nIndex < 0 || nIndex >= m_nTotalHorizontalUnits )
{
Warning( "CFoW: GetLocationVisibilityDegree() called with invalid view location of %g, %g, %g\n", vLocation.x, vLocation.y, vLocation.z );
return 0.0f;
}
#endif
float *pDestPos = m_pVisibilityGridDegree[ nViewerTeam ] + nIndex;
return ( ( *pDestPos ) > 1.0f ? 1.0f : ( *pDestPos ) );
}
float flBestDegree = 0.0f;
int nMinGridX, nMinGridY;
int nMaxGridX, nMaxGridY;
Vector vDelta( flRadius + 16.0f, flRadius + 16.0f, 0 );
Vector vMin = vLocation - vDelta;
Vector vMax = vLocation + vDelta;
GetGridUnits( vMin, 0.0f, 0.0f, true, nMinGridX, nMinGridY );
GetGridUnits( vMax, 0.0f, 0.0f, true, nMaxGridX, nMaxGridY );
int nCount = 0;
float flTotal = 0.0f;
for( int nXOffset = nMinGridX; nXOffset <= nMaxGridX; nXOffset++ )
{
if ( nXOffset < 0 || nXOffset >= m_nGridXUnits )
{
continue;
}
for( int nYOffset = nMinGridY; nYOffset <= nMaxGridY; nYOffset++ )
{
if ( nYOffset < 0 || nYOffset >= m_nGridYUnits )
{
continue;
}
int nLocation = ( ( nXOffset * m_nGridYUnits ) + nYOffset );
float *pDestPos = m_pVisibilityGridDegree[ nViewerTeam ] + nLocation;
flTotal += ( ( *pDestPos ) > 1.0f ? 1.0f : ( *pDestPos ) );
nCount++;
#if 0
if ( ( *pDestPos ) > flBestDegree )
{
flBestDegree = ( *pDestPos );
if ( flBestDegree == 1.0f )
{
return 1.0f;
}
}
#endif
}
}
if ( nCount > 0 )
{
flBestDegree = flTotal / nCount;
}
else
{
#ifdef FOW_SAFETY_DANCE
Warning( "CFoW: GetLocationVisibilityDegree() called with invalid view location of %g, %g, %g\n", vLocation.x, vLocation.y, vLocation.z );
#endif // #ifdef FOW_SAFETY_DANCE
}
return flBestDegree;
}
//-----------------------------------------------------------------------------
// Purpose: adds an viewer to the sphere tree
// Input: nIndex - the index of the viewer
//-----------------------------------------------------------------------------
void CFoW::InsertViewerIntoTree( int nIndex )
{
if ( m_Viewers[ nIndex ] != NULL )
{
Sphere_t Bounds;
Bounds.AsVector3D() = m_Viewers[ nIndex ]->GetLocation();
Bounds.w = m_Viewers[ nIndex ]->GetSize();
m_ViewerTree.Insert( (void *)m_Viewers[ nIndex ], &Bounds );
}
}
//-----------------------------------------------------------------------------
// Purpose: removes an viewer from the sphere tree
// Input: nIndex - the index of the viewer
//-----------------------------------------------------------------------------
void CFoW::RemoveViewerFromTree( int nIndex, Vector *pvOldLocation )
{
if ( m_Viewers[ nIndex ] != NULL )
{
Sphere_t Bounds;
if ( pvOldLocation != NULL )
{
Bounds.AsVector3D() = *pvOldLocation;
}
else
{
Bounds.AsVector3D() = m_Viewers[ nIndex ]->GetLocation();
}
Bounds.w = m_Viewers[ nIndex ]->GetSize();
m_ViewerTree.Remove( (void *)m_Viewers[ nIndex ], &Bounds );
}
}
void CFoW::DirtyViewers( Vector &vLocation, float flRadius )
{
// CFoW_RadiusOccluder *pOcculder = m_Occluders[ nOccluderID ];
Sphere_t TestSphere( vLocation.x, vLocation.y, vLocation.z, flRadius );
CFoW_Viewer *FixedPointerArray[ FOW_MAX_VIEWERS_TO_CHECK ];
CUtlVector< void * > FoundViewers( ( void ** )FixedPointerArray, FOW_MAX_VIEWERS_TO_CHECK );
int RealCount = m_ViewerTree.IntersectWithSphere( TestSphere, true, FoundViewers, FOW_MAX_VIEWERS_TO_CHECK );
if ( RealCount > FOW_MAX_VIEWERS_TO_CHECK )
{
// we overflowed, what should we do?
Assert( 0 );
}
for ( int i = 0; i < FoundViewers.Count(); i++ )
{
FixedPointerArray[ i ]->Dirty();
}
}
//-----------------------------------------------------------------------------
// Purpose: adds all occluders back into the visibility tree
//-----------------------------------------------------------------------------
void CFoW::RepopulateOccluders( )
{
m_OccluderTree.RemoveAll();
for ( int i = 0; i < m_Occluders.Count(); i++ )
{
if ( m_Occluders[ i ] != NULL )
{
Sphere_t Bounds;
Bounds.AsVector3D() = m_Occluders[ i ]->GetLocation();
Bounds.w = m_Occluders[ i ]->GetSize();
m_OccluderTree.Insert( (void *)m_Occluders[ i ], &Bounds );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: adds an occluder to the sphere tree
// Input: nIndex - the index of the occluder
//-----------------------------------------------------------------------------
void CFoW::InsertOccluderIntoTree( int nIndex )
{
if ( m_Occluders[ nIndex ] != NULL )
{
Sphere_t Bounds;
Bounds.AsVector3D() = m_Occluders[ nIndex ]->GetLocation();
Bounds.w = m_Occluders[ nIndex ]->GetSize();
m_OccluderTree.Insert( (void *)m_Occluders[ nIndex ], &Bounds );
}
}
//-----------------------------------------------------------------------------
// Purpose: removes an occluder from the sphere tree
// Input: nIndex - the index of the occluder
//-----------------------------------------------------------------------------
void CFoW::RemoveOccluderFromTree( int nIndex )
{
if ( m_Occluders[ nIndex ] != NULL )
{
Sphere_t Bounds;
Bounds.AsVector3D() = m_Occluders[ nIndex ]->GetLocation();
Bounds.w = m_Occluders[ nIndex ]->GetSize();
m_OccluderTree.Remove( (void *)m_Occluders[ nIndex ], &Bounds );
}
}
//-----------------------------------------------------------------------------
// Purpose: defaults the viewing grids
//-----------------------------------------------------------------------------
void CFoW::PrepVisibility( )
{
int nSize = m_nGridXUnits * m_nGridYUnits;
for ( int i = 0; i < m_nNumberOfTeams; i++ )
{
byte *pFlagPtr = m_pVisibilityGridFlags[ i ];
byte *pFlagEndPtr = pFlagPtr + nSize;
for ( ; pFlagPtr < pFlagEndPtr; pFlagPtr++ )
{
if ( ( ( *pFlagPtr ) & ( FOW_VG_IS_VISIBLE ) ) == FOW_VG_IS_VISIBLE )
{
( *pFlagPtr ) &= ~FOW_VG_IS_VISIBLE;
if ( ( ( *pFlagPtr ) & ( FOW_VG_WAS_VISIBLE ) ) == 0 )
{
( *pFlagPtr ) |= FOW_VG_WAS_VISIBLE;
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: updates the viewer grids
// Input : flFrameTime - the time since the last visibility solve. The amount
// of change in the visibility degree is dependent upon this value.
//-----------------------------------------------------------------------------
void CFoW::UpdateVisibleAmounts( float flFrameTime )
{
int nSize = m_nGridXUnits * m_nGridYUnits;
flFrameTime /= m_flDegreeFadeRate;
for ( int i = 0; i < m_nNumberOfTeams; i++ )
{
byte *pFlagPtr = m_pVisibilityGridFlags[ i ];
byte *pFlagEndPtr = pFlagPtr + nSize;
float *pDegreePtr = m_pVisibilityGridDegree[ i ];
float *pFadeTimePtr = m_pVisibilityFadeTimer[ i ];
for ( ; pFlagPtr < pFlagEndPtr; pFlagPtr++, pDegreePtr++, pFadeTimePtr++ )
{
if ( ( ( *pFlagPtr ) & FOW_VG_IS_VISIBLE ) != 0 )
{
if ( ( *pDegreePtr ) < FOW_OVER_VISIBILITY )
{
( *pDegreePtr ) += flFrameTime;
if ( ( *pDegreePtr ) > FOW_OVER_VISIBILITY )
{
( *pDegreePtr ) = FOW_OVER_VISIBILITY;
}
}
( *pFadeTimePtr ) = FOW_FADE_DELAY;
}
else
{
if ( ( *pFadeTimePtr ) > 0.0f )
{
// worry about going negative and using up the remainder?
( *pFadeTimePtr ) -= flFrameTime;
}
else if ( ( *pDegreePtr ) > 0.0f )
{
( *pDegreePtr ) -= flFrameTime;
if ( ( *pDegreePtr ) < 0.0f )
{
( *pDegreePtr ) = 0.0f;
}
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: given the coords and an offset to move BACK, finds the grid index
// Input : vCoords - the world coordinates ( z is ignored )
// flXOffset - the x offset to SUBTRACT from the world coordinates
// flXOffset - the y offset to SUBTRACT from the world coordinates
// bGoLower - should we snap to the left/bottom or right/top of the grid
// Output : returns the index into the grids. returns -1 if it is invalid.
//-----------------------------------------------------------------------------
int CFoW::GetGridIndex( const Vector &vCoords, float flXOffset, float flYOffset, bool bGoLower )
{
int nGridX, nGridY;
GetGridUnits( vCoords, flXOffset, flYOffset, bGoLower, nGridX, nGridY );
#if 0
if ( nGridX < 0 || nGridX >= m_nGridXUnits ||
nGridY < 0 || nGridY >= m_nGridYUnits )
{
return -1;
}
#endif
return ( nGridX * m_nGridYUnits ) + nGridY;
}
//-----------------------------------------------------------------------------
// Purpose: given the coords and an offset to move BACK, finds the grid location
// Input : vCoords - the world coordinates ( z is ignored )
// flXOffset - the x offset to SUBTRACT from the world coordinates
// flXOffset - the y offset to SUBTRACT from the world coordinates
// bGoLower - should we snap to the left/bottom or right/top of the grid
// Output : nGridX - the x grid location
// nGridY - the y grid location
//-----------------------------------------------------------------------------
void CFoW::GetGridUnits( const Vector &vCoords, float flXOffset, float flYOffset, bool bGoLower, int &nGridX, int &nGridY )
{
if ( bGoLower )
{
nGridX = floor( ( vCoords.x - flXOffset - m_vWorldMins.x ) / m_nHorizontalGridSize );
nGridY = floor( ( vCoords.y - flYOffset - m_vWorldMins.y ) / m_nHorizontalGridSize );
}
else
{
nGridX = ceil( ( vCoords.x - flXOffset - m_vWorldMins.x ) / m_nHorizontalGridSize );
nGridY = ceil( ( vCoords.y - flYOffset - m_vWorldMins.y ) / m_nHorizontalGridSize );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::CenterCoordToGrid( Vector &vCoords )
{
int nGridX, nGridY;
GetGridUnits( vCoords, 0.0f, 0.0f, true, nGridX, nGridY );
vCoords.x = m_vWorldMins.x + ( nGridX * m_nHorizontalGridSize ) + ( m_nHorizontalGridSize / 2.0f );
vCoords.y = m_vWorldMins.y + ( nGridY * m_nHorizontalGridSize ) + ( m_nHorizontalGridSize / 2.0f );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::SetDebugVisibility( bool bVisible )
{
m_bDebugVisible = bVisible;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::EnableDebugFlags( unsigned nFlags )
{
m_nDebugFlags |= nFlags;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::DisableDebugFlags( unsigned nFlags )
{
m_nDebugFlags &= ~nFlags;
}
//-----------------------------------------------------------------------------
// Purpose: merge a local viewer's visibility to the global grid
// Input : nID - the id of the viewer to merge in
//-----------------------------------------------------------------------------
void CFoW::MergeViewerVisibility( int nID )
{
int nStartX, nStartY, nEndX, nEndY, nWidth, nHeight;
CFoW_Viewer *pViewer = m_Viewers[ nID ];
if ( !pViewer )
{
return;
}
nWidth = nHeight = pViewer->GetGridUnits();
nStartX = 0;
nStartY = 0;
GetGridUnits( pViewer->GetLocation(), pViewer->GetSize(), pViewer->GetSize(), true, nStartX, nStartY );
nEndX = nStartX + nWidth;
nEndY = nStartY + nHeight;
byte *pSrcPos = pViewer->GetVisibility();
int nIndex = GetGridIndex( pViewer->GetLocation(), pViewer->GetSize(), pViewer->GetSize(), true );
byte *pDestPos = m_pVisibilityGridFlags[ pViewer->GetTeam() ] + nIndex;
int nYDestStride = m_nGridYUnits - nHeight;
int nYSrcStride = 0;
if ( nStartX < 0 )
{
pDestPos += ( -nStartX ) * m_nGridYUnits;
pSrcPos += ( -nStartX ) * nHeight;
nStartX = 0;
}
if ( nEndX > m_nGridXUnits )
{
nEndX -= ( nEndX - m_nGridXUnits );
}
if ( nStartY < 0 )
{
pDestPos += ( -nStartY );
pSrcPos += ( -nStartY );
nYDestStride += ( -nStartY );
nYSrcStride += ( -nStartY );
nStartY = 0;
}
if ( nEndY > m_nGridYUnits )
{
nYDestStride += ( nEndY - m_nGridYUnits );
nYSrcStride += ( nEndY - m_nGridYUnits );
nEndY -= ( nEndY - m_nGridYUnits );
}
uint8 nViewerHeightGroup = pViewer->GetHeightGroup();
int nCount = 0;
for ( int x = nStartX; x < nEndX; x++, pDestPos += nYDestStride, pSrcPos += nYSrcStride)
{
for ( int y = nStartY; y < nEndY; y++, pSrcPos++, pDestPos++ )
{
nCount++;
*pDestPos |= *pSrcPos;
if ( ( ( *pSrcPos ) & FOW_VG_IS_VISIBLE ) != 0 && ( ( *pDestPos ) & FOW_VG_MAX_HEIGHT_GROUP ) < nViewerHeightGroup )
{
( *pDestPos ) = ( ( *pDestPos ) & ( ~FOW_VG_MAX_HEIGHT_GROUP ) ) | nViewerHeightGroup;
}
// handle height group in bits
}
}
#ifdef FOW_SAFETY_DANCE
if ( nCount == 0 )
{ // either radius that is too small ( or invalid ) or the location is off the grid
Warning( "CFoW: MergeViewerVisibility() Viewer %d has no contribution at location of %g, %g, %g\n", nID, pViewer->GetLocation().x, pViewer->GetLocation().y, pViewer->GetLocation().z );
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::DrawDebugInfo( Vector &vLocation, float flViewRadius )
{
if ( m_bDebugVisible == false )
{
return;
}
// debugoverlay->AddBoxOverlay( Vector( 0.0f, 0.0f, 0.0f ), Vector( -512.0f, -512.0f, -512.0f ), Vector( 512.0f, 512.0f, 512.0f ), QAngle( 0, 0, 0 ), 0, 255, 0, 16, 0 );
// debugoverlay->AddSphereOverlay( Vector( 0.0f, 0.0f, 0.0f ), 512.0f, 10, 10, 255, 0, 0, 127, 0 );
if ( ( m_nDebugFlags & ( FOW_DEBUG_SHOW_GRID ) ) != 0 )
{
const float flGridOffset = 4.0f;
for( int x = 0; x < m_nGridXUnits; x++ )
{
float flRealStartX = ( x * m_nHorizontalGridSize ) + m_vWorldMins.x + flGridOffset;
float flRealEndX = flRealStartX + m_nHorizontalGridSize - flGridOffset - flGridOffset;
float flCenterX = ( flRealStartX + flRealEndX ) / 2.0f;
for( int y = 0; y < m_nGridYUnits; y++ )
{
float flRealStartY = ( y * m_nHorizontalGridSize ) + m_vWorldMins.y + flGridOffset;
float flRealEndY = flRealStartY + m_nHorizontalGridSize - flGridOffset - flGridOffset;
float flCenterY = ( flRealStartY + flRealEndY ) / 2.0f;
Vector vDiff = Vector( flCenterX, flCenterY, 0.0f ) - vLocation;
if ( vDiff.Length2D() > flViewRadius + m_nHorizontalGridSize )
{
continue;
}
int r, g, b;
int nLocation = ( ( x * m_nGridYUnits ) + y );
float *pDestPos = m_pVisibilityGridDegree[ 0 ] + nLocation;
float flValue = ( ( *pDestPos ) > 1.0f ? 1.0f : ( *pDestPos ) );
g = 255 * flValue;
r = 255 - g;
b = 0;
debugoverlay->AddLineOverlay( Vector( flRealStartX, flRealStartY, 0.0f ), Vector( flRealEndX, flRealStartY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
debugoverlay->AddLineOverlay( Vector( flRealEndX, flRealStartY, 0.0f ), Vector( flRealEndX, flRealEndY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
debugoverlay->AddLineOverlay( Vector( flRealEndX, flRealEndY, 0.0f ), Vector( flRealStartX, flRealEndY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
debugoverlay->AddLineOverlay( Vector( flRealStartX, flRealEndY, 0.0f ), Vector( flRealStartX, flRealStartY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
}
}
}
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] == NULL )
{
continue;
}
m_Viewers[ i ]->DrawDebugInfo( vLocation, flViewRadius, m_nDebugFlags );
}
for ( int i = 0; i < m_Occluders.Count(); i++ )
{
if ( m_Occluders[ i ] == NULL )
{
continue;
}
m_Occluders[ i ]->DrawDebugInfo( vLocation, flViewRadius, m_nDebugFlags );
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::PrintStats( )
{
int nNumViewers = 0;
size_t nViewerSize = 0;
int nNumOccluders = 0;
size_t nOccluderSize = 0;
int nNumTriSoupCollections = 0;
size_t nTriSoupCollectionSize = 0;
int nNumLineOccluders = 0;
size_t nLineOccluderSize = 0;
size_t nTotal = 0;
for ( int i = 0; i < m_Viewers.Count(); i++ )
{
if ( m_Viewers[ i ] == NULL )
{
continue;
}
nNumViewers++;
nViewerSize += sizeof( CFoW_Viewer );
nViewerSize += m_Viewers[ i ]->GetAllocatedMemory();
}
for ( int i = 0; i < m_Occluders.Count(); i++ )
{
if ( m_Occluders[ i ] == NULL )
{
continue;
}
nNumOccluders++;
nOccluderSize += sizeof( CFoW_RadiusOccluder );
}
for( int i = 0; i < m_TriSoupCollection.Count(); i++ )
{
nNumTriSoupCollections++;
nTriSoupCollectionSize += sizeof( CFoW_TriSoupCollection );
for( int j = 0; j < m_TriSoupCollection[ i ]->GetNumOccluders(); j++ )
{
// CFoW_LineOccluder *pOccluder = m_TriSoupCollection[ i ]->GetOccluder( j );
nNumLineOccluders++;
nLineOccluderSize += sizeof( CFoW_LineOccluder );
}
}
Msg( "FoW Stats\n" );
Msg( " Num Active Viewers: %d\n", nNumViewers );
Msg( " Num Active Radius Occluders: %d\n", nNumOccluders );
Msg( " Num Tri Soup Collections: %d\n", nNumTriSoupCollections );
Msg( " Num Active Line Occluders: %d\n", nNumLineOccluders );
Msg( "FoW Memory\n" );
Msg( " Horizontal Grid Allocation Size: %d\n", m_nHorizontalGridAllocationSize );
Msg( " Veritcal Grid Allocation Size: %d\n", m_nVerticalGridAllocationSize );
Msg( " Radius Table Size: %d\n", m_nRadiusTableSize );
Msg( " Viewers Size: %d\n", nViewerSize );
Msg( " Radius Occluders Size: %d\n", nOccluderSize );
Msg( " Tri Soup Collection Size: %d\n", nTriSoupCollectionSize );
Msg( " Line Occluders Size: %d\n", nLineOccluderSize );
nTotal = m_nHorizontalGridAllocationSize + m_nVerticalGridAllocationSize + m_nRadiusTableSize + nViewerSize + nOccluderSize + nTriSoupCollectionSize + nLineOccluderSize;
Msg( " --------------------------------------\n");
Msg( " Approximate Total Size: %d\n", nTotal );
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CFoW::GenerateVMF( IFileSystem *pFileSystem, const char *pszFileName )
{
KeyValues *kv = new KeyValues( NULL );
char temp[ 128 ];
int nCount = 1;
KeyValues *pWorldKV = new KeyValues( "world" );
pWorldKV->SetInt( "id", nCount );
nCount++;
pWorldKV->SetInt( "mapversion", 22 );
pWorldKV->SetString( "classname", "worldspawn" );
pWorldKV->SetInt( "fow", 1 );
sprintf( temp, "%g %g %g", m_vWorldMins.x, m_vWorldMins.y, m_vWorldMins.z );
pWorldKV->SetString( "m_vWorldMins", temp );
sprintf( temp, "%g %g %g", m_vWorldMaxs.x, m_vWorldMaxs.y, m_vWorldMaxs.z );
pWorldKV->SetString( "m_vWorldMaxs", temp );
pWorldKV->SetInt( "m_nHorizontalGridSize", m_nHorizontalGridSize );
pWorldKV->SetInt( "m_nVerticalGridSize", m_nVerticalGridSize );
pWorldKV->SetInt( "m_nGridXUnits", m_nGridXUnits );
pWorldKV->SetInt( "m_nGridYUnits", m_nGridYUnits );
pWorldKV->SetInt( "m_nGridZUnits", m_nGridZUnits );
for ( int i = 0; i < m_nGridZUnits; i++ )
{
sprintf( temp, "m_pVerticalLevels_%d", i );
pWorldKV->SetFloat( temp, m_pVerticalLevels[ i ] );
}
kv->AddSubKey( pWorldKV );
for( int i = 0; i < m_Viewers.Count(); i++ )
{
CFoW_Viewer *pViewer = m_Viewers[ i ];
if ( pViewer )
{
KeyValues *pViewerKV = new KeyValues( "entity" );
pViewerKV->SetInt( "id", nCount );
nCount++;
pViewerKV->SetString( "classname", "env_viewer" );
pViewerKV->SetFloat( "radius", pViewer->GetSize() );
sprintf( temp, "%g %g %g", pViewer->GetLocation().x, pViewer->GetLocation().y, pViewer->GetLocation().z );
pViewerKV->SetString( "origin", temp );
pViewerKV->SetInt( "height_group", ( int )pViewer->GetHeightGroup() );
pViewerKV->SetInt( "team", ( int )pViewer->GetTeam() );
kv->AddSubKey( pViewerKV );
}
}
for( int i = 0; i < m_Occluders.Count(); i++ )
{
CFoW_RadiusOccluder *pOccluder = m_Occluders[ i ];
if ( pOccluder )
{
KeyValues *pOccluderKV = new KeyValues( "entity" );
pOccluderKV->SetInt( "id", nCount );
nCount++;
pOccluderKV->SetString( "classname", "env_occluder" );
pOccluderKV->SetFloat( "radius", pOccluder->GetSize() );
sprintf( temp, "%g %g %g", pOccluder->GetLocation().x, pOccluder->GetLocation().y, pOccluder->GetLocation().z );
pOccluderKV->SetString( "origin", temp );
pOccluderKV->SetInt( "height_group", ( int )pOccluder->GetHeightGroup() );
kv->AddSubKey( pOccluderKV );
}
}
for( int i = 0; i < m_TriSoupCollection.Count(); i++ )
{
for( int j = 0; j < m_TriSoupCollection[ i ]->GetNumOccluders(); j++ )
{
CFoW_LineOccluder *pOccluder = m_TriSoupCollection[ i ]->GetOccluder( j );
if ( pOccluder )
{
KeyValues *pOccluderKV = new KeyValues( "entity" );
pOccluderKV->SetInt( "id", nCount );
nCount++;
pOccluderKV->SetString( "classname", "env_line_occluder" );
sprintf( temp, "%g %g", pOccluder->GetStart().x, pOccluder->GetStart().y );
pOccluderKV->SetString( "start", temp );
sprintf( temp, "%g %g", pOccluder->GetEnd().x, pOccluder->GetEnd().y );
pOccluderKV->SetString( "end", temp );
sprintf( temp, "%g %g %g", pOccluder->GetPlaneNormal().x, pOccluder->GetPlaneNormal().y, pOccluder->GetPlaneDistance() );
pOccluderKV->SetString( "plane", temp );
pOccluderKV->SetInt( "slice_num", pOccluder->GetSliceNum() );
kv->AddSubKey( pOccluderKV );
}
}
}
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
for ( KeyValues *pWriteKV = kv->GetFirstSubKey(); pWriteKV != NULL; pWriteKV = pWriteKV->GetNextKey() )
{
pWriteKV->RecursiveSaveToFile( buf, 0 );
}
pFileSystem->WriteFile( pszFileName, NULL, buf );
}
#include <tier0/memdbgoff.h>
/*
RJ Optimization Ideas:
1. void CFoW_RadiusOccluder::ObstructViewerGrid( CFoW *FoW, CFoW_Viewer *Viewer )
Don't need the extending sides for point in front of plane checking ( see commented out section )
2. void CFoW_Viewer::DefaultViewingArea( CFoW *FoW )
Do this only initially, set the flag FOW_VG_DEFAULT_VISIBLE, then use the flag from that point on.
3. for line blocker, calc start and end angles and sweep between ( rather than doing 360 degree sweep )
4. only recalc if an item has moved to a new grid location
5. 360 entry tables for cos / acos lookups
6. multithread main calc, or sub thread it further? the float grid update can be easily slit up
7. obvious radius square calcs to avoid sqrt()
DONE
tree to only check radius of things near by
*/