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

463 lines
12 KiB
C++

#include "fow.h"
#include "fow_radiusoccluder.h"
#include "fow_viewer.h"
#include "fow_2dplane.h"
#include "engine/IVDebugOverlay.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
extern IVDebugOverlay *debugoverlay;
//-----------------------------------------------------------------------------
// Purpose: constructor to init this occluder with the id
// Input : nID - the id of this occluder
//-----------------------------------------------------------------------------
CFoW_RadiusOccluder::CFoW_RadiusOccluder( int nID )
{
m_nID = nID;
m_flRadius = 0.0f;
m_vLocation.Zero();
m_nHeightGroup = 0;
m_bEnabled = true;
}
//-----------------------------------------------------------------------------
// Purpose: update the radius of this occluder
// Input : flRadius - the new radius size
//-----------------------------------------------------------------------------
void CFoW_RadiusOccluder::UpdateSize( float flRadius )
{
m_flRadius = flRadius;
}
//-----------------------------------------------------------------------------
// Purpose: update the location of this occluder
// Input : vLocation - the new location
//-----------------------------------------------------------------------------
void CFoW_RadiusOccluder::UpdateLocation( Vector &vLocation )
{
m_vLocation = vLocation;
}
//-----------------------------------------------------------------------------
// Purpose: update the height group of this occluder
// Input : nHeightGroup - the new height group
//-----------------------------------------------------------------------------
void CFoW_RadiusOccluder::UpdateHeightGroup( uint8 nHeightGroup )
{
m_nHeightGroup = nHeightGroup;
}
//-----------------------------------------------------------------------------
// Purpose: is the occluder within range of the viewer?
// Input : pViewer - the viewer to check against
// Output : returns true if the two circles intersect
//-----------------------------------------------------------------------------
bool CFoW_RadiusOccluder::IsInRange( CFoW_Viewer *pViewer )
{
if ( m_bEnabled == false )
{
return false;
}
Vector vDiff = pViewer->GetLocation() - m_vLocation;
float flLen = sqrt( ( vDiff.x * vDiff.x ) + ( vDiff.y * vDiff.y ) );
return ( flLen <= m_flRadius + pViewer->GetSize() );
}
//-----------------------------------------------------------------------------
// Purpose: obstruct the viewer by updating the local viewer grid
// Input : pFoW - the main FoW object
// pViewer - the viewer to obstruct
//-----------------------------------------------------------------------------
void CFoW_RadiusOccluder::ObstructViewerGrid( CFoW *pFoW, CFoW_Viewer *pViewer )
{
if ( m_bEnabled == false )
{
return;
}
Vector vViewerLoc = pViewer->GetLocation();
float flViewerRadius = pViewer->GetSize();
Vector vDelta = ( vViewerLoc - m_vLocation );
vDelta.z = 0.0f;
float flLength = vDelta.Length();
if ( flLength > ( flViewerRadius + m_flRadius ) )
{
return;
}
// if ( length <= m_flRadius || length > ViewerRadius )
if ( flLength <= m_flRadius )
{
return;
}
float flAngle = ( float )atan2( vDelta.y, vDelta.x );
float flTangentLen = sqrt( flLength * flLength - m_flRadius * m_flRadius );
float flTangentAngle = ( float )asin( m_flRadius / flLength );
// compute the two tangent angles
float flPos = flAngle + flTangentAngle;
float flNeg = flAngle - flTangentAngle;
float x[ 6 ], y[ 6 ];
// compute the two tangent points
x[ 0 ] = -( float )cos( flPos ) * flTangentLen + vViewerLoc.x;
y[ 0 ] = -( float )sin( flPos ) * flTangentLen + vViewerLoc.y;
x[ 5 ] = -( float )cos( flNeg ) * flTangentLen + vViewerLoc.x;
y[ 5 ] = -( float )sin( flNeg ) * flTangentLen + vViewerLoc.y;
// extend the tangent points to the viewer's edge
x[ 1 ] = -( float )cos( flPos ) * flViewerRadius + vViewerLoc.x;
y[ 1 ] = -( float )sin( flPos ) * flViewerRadius + vViewerLoc.y;
x[ 4 ] = -( float )cos( flNeg ) * flViewerRadius + vViewerLoc.x;
y[ 4 ] = -( float )sin( flNeg ) * flViewerRadius + vViewerLoc.y;
// compute the forward direction of the viewer's intersection through this blocker
float fx = -vDelta.x / flLength;
float fy = -vDelta.y / flLength;
// compute half the length between the viewer's tangent edges
float dx2 = x[ 4 ] - x[ 1 ];
float dy2 = y[ 4 ] - y[ 1 ];
float flHalflen = ( dx2 * dx2 + dy2 * dy2 ) / 4;
// compute the side of the triangle that forms from viewer's radius to half way across the viewer's tangent edges
float flLen2 = ( float )sqrt( flViewerRadius * flViewerRadius - flHalflen );
flLen2 = flViewerRadius - flLen2;
// compute the box extents to encompass the circle's bounds
x[ 2 ] = x[ 1 ] + ( fx * flLen2 );
y[ 2 ] = y[ 1 ] + ( fy * flLen2 );
x[ 3 ] = x[ 4 ] + ( fx * flLen2 );
y[ 3 ] = y[ 4 ] + ( fy * flLen2 );
CFOW_2DPlane Planes[ 6 ];
for (int i = 0; i < 6; i++)
{
Planes[ i ].Init( x[ i ], y[ i ], x[ ( i + 1 ) % 6 ], y[ ( i + 1 ) % 6 ] );
}
float flMinX = x[ 0 ], flMinY = y[ 0 ], flMaxX = x[ 0 ], flMaxY = y[ 0 ];
for ( int i = 1; i < 6; i++ )
{
if ( x[ i ] < flMinX )
{
flMinX = x[ i ];
}
if ( x[ i ] > flMaxX )
{
flMaxX = x[ i ];
}
if ( y[ i ] < flMinY )
{
flMinY = y[ i ];
}
if ( y[i] > flMaxY )
{
flMaxY = y[ i ];
}
}
float px, py, flStart_py, ex, ey;
int nGridX, nGridY, nStartGridY;
Vector2D vViewerStart, vViewerEnd;
int nGridSize = pFoW->GetHorizontalGridSize();
pViewer->GetStartPosition( vViewerStart );
pViewer->GetEndPosition( vViewerEnd );
if ( flMinX > vViewerStart.x )
{
nGridX = ( int )( flMinX - vViewerStart.x ) / nGridSize;
px = vViewerStart.x + ( int )( nGridSize * nGridX );
}
else
{
px = vViewerStart.x;
nGridX = 0;
}
if ( flMaxX < vViewerEnd.x )
{
ex = flMaxX;
}
else
{
ex = vViewerEnd.x;
}
if ( flMinY > vViewerStart.y )
{
nStartGridY = ( int )( flMinY - vViewerStart.y ) / nGridSize;
flStart_py = vViewerStart.y + ( int )( nGridSize * nStartGridY );
}
else
{
nStartGridY = 0;
flStart_py = vViewerStart.y;
}
if ( flMaxY < vViewerEnd.y )
{
ey = flMaxY;
}
else
{
ey = vViewerEnd.y;
}
byte *pLocalVisibility = pViewer->GetVisibility();
int nLocalGridUnits = pViewer->GetGridUnits();
// offset to center of grid
px += nGridSize / 2;
flStart_py += nGridSize / 2;
for ( ; px < ex; px += nGridSize, nGridX++)
{
for ( nGridY = nStartGridY, py = flStart_py; py < ey; py += nGridSize, nGridY++ )
{
byte *pPos = pLocalVisibility + ( nGridX * nLocalGridUnits ) + nGridY;
if ( ( ( *pPos ) & FOW_VG_IS_VISIBLE ) == 0 )
{
continue;
}
int i;
for ( i = 0; i < 6; i++ )
{
#if 0
// we don't need to check the bounding planes - these would be used to construct a stencil buffer though
if ( i == 1 || i == 2 || i == 3 )
{
continue;
}
#endif
if ( !Planes[ i ].PointInFront( px, py ) )
{
break;
}
}
if ( i == 6 )
{
( *pPos ) &= ~FOW_VG_IS_VISIBLE;
}
}
}
}
// #define SLOW_PATH 1
//-----------------------------------------------------------------------------
// Purpose: obstruct the viewer by updating the depth circle
// Input : pFoW - the main FoW object
// pViewer - the viewer to obstruct
//-----------------------------------------------------------------------------
void CFoW_RadiusOccluder::ObstructViewerRadius( CFoW *pFoW, CFoW_Viewer *pViewer )
{
if ( m_bEnabled == false )
{
return;
}
if ( m_flRadius <= 1.0f )
{
Warning( "FoW: Occluder %d has invalid radius\n", m_nID );
return;
}
int nViewerHeightGroup = pViewer->GetHeightGroup();
if ( nViewerHeightGroup >= 1 && m_nHeightGroup >= 1 && m_nHeightGroup < nViewerHeightGroup )
{ // both the viewer and the occluder have height groups and this occluder is under the viewer, then don't obstruct
return;
}
Vector vViewerLoc = pViewer->GetLocation();
float flViewerRadius = pViewer->GetSize();
Vector vDelta = ( vViewerLoc - m_vLocation );
vDelta.z = 0.0f;
float flLength = vDelta.Length();
if ( flLength > ( flViewerRadius + m_flRadius ) )
{
return;
}
// if ( length <= m_flRadius || length > ViewerRadius )
if ( flLength <= m_flRadius )
{
return;
}
float flAngle = ( float )atan2( vDelta.x, vDelta.y ) + DEG2RAD( 180.0f );
float flTangentLen = sqrt( flLength * flLength - m_flRadius * m_flRadius );
float flTangentAngle = ( float )asin( m_flRadius / flLength );
// compute the two tangent angles
float flPos = flAngle + flTangentAngle;
float flNeg = flAngle - flTangentAngle;
float x[ 6 ], y[ 6 ];
// compute the two tangent points
#ifdef SLOW_PATH
x[ 0 ] = ( float )sin( flPos ) * flTangentLen + vViewerLoc.x;
y[ 0 ] = ( float )cos( flPos ) * flTangentLen + vViewerLoc.y;
x[ 5 ] = ( float )sin( flNeg ) * flTangentLen + vViewerLoc.x;
y[ 5 ] = ( float )cos( flNeg ) * flTangentLen + vViewerLoc.y;
#else
x[ 0 ] = ( float )TableSin( flPos ) * flTangentLen + vViewerLoc.x;
y[ 0 ] = ( float )TableCos( flPos ) * flTangentLen + vViewerLoc.y;
x[ 5 ] = ( float )TableSin( flNeg ) * flTangentLen + vViewerLoc.x;
y[ 5 ] = ( float )TableCos( flNeg ) * flTangentLen + vViewerLoc.y;
#endif
// Msg( "%g, %g\n", RAD2DEG( flPos ), RAD2DEG( flNeg ) );
CFOW_2DPlane Plane;
Plane.Init( x[ 5 ], y[ 5 ], x[ 0 ], y[ 0 ] );
int nUnits = pViewer->GetRadiusUnits();
int *pVisibility = pViewer->GetVisibilityRadius();
Vector vCenterLocation = vViewerLoc;
float flDistance = Plane.DistanceFrom( vCenterLocation.x, vCenterLocation.y );
if ( flDistance < 0.0f )
{
return;
}
#ifdef SLOW_PATH
CFOW_2DPlane Edge1, Edge2;
Edge1.Init( vCenterLocation.x, vCenterLocation.y, x[ 0 ], y[ 0 ] );
Edge2.Init( x[ 5 ], y[ 5 ], vCenterLocation.x, vCenterLocation.y );
float flDegreeAmount = 360.0f / nUnits;
float flCurrentDegree = 0.0f;
for ( int i = 0; i < nUnits; i++, flCurrentDegree += flDegreeAmount )
{
Vector vLocation = vViewerLoc;
Vector vDelta;
vDelta.x = sin( DEG2RAD( flCurrentDegree ) );
vDelta.y = cos( DEG2RAD( flCurrentDegree ) );
vLocation += vDelta * flViewerRadius;
float flDistance = Plane.DistanceFromRay( vCenterLocation.x, vCenterLocation.y, vDelta.x, vDelta.y );
if ( flDistance >= 0.0f )
{
flDistance *= flDistance;
if ( flDistance >= 0.0f && flDistance < pVisibility[ i ] )
{
if ( Edge1.PointInFront( vLocation.x, vLocation.y ) && Edge2.PointInFront( vLocation.x, vLocation.y ) )
{
pVisibility[ i ] = flDistance;
}
}
}
}
#else
float flCurrentDegree, flFinishDegree;
int nStartIndex;
if ( flPos < flNeg )
{
flCurrentDegree = flPos;
flFinishDegree = flNeg;
}
else
{
flCurrentDegree = flNeg;
flFinishDegree = flPos;
}
float flDegreeAmount = 2.0f * M_PI_F / nUnits;
nStartIndex = ( int )( flCurrentDegree / flDegreeAmount ) % nUnits;
if ( nStartIndex < 0 )
{
nStartIndex += nUnits;
}
float flHorizontalGridSize = pFoW->GetHorizontalGridSize();
for ( int i = nStartIndex; flCurrentDegree < flFinishDegree; i++, flCurrentDegree += flDegreeAmount )
{
Vector vDelta;
if ( i >= nUnits )
{
i = 0;
}
vDelta.x = TableSin( flCurrentDegree );
vDelta.y = TableCos( flCurrentDegree );
float flDistance = Plane.DistanceFromRay( vCenterLocation.x, vCenterLocation.y, vDelta.x, vDelta.y );
if ( flDistance >= 0.0f )
{
flDistance += flHorizontalGridSize * 1.1f; // back off a bit
flDistance *= flDistance;
if ( flDistance < pVisibility[ i ] )
{
pVisibility[ i ] = flDistance;
}
}
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
//-----------------------------------------------------------------------------
void CFoW_RadiusOccluder::DrawDebugInfo( Vector &vLocation, float flViewRadius, unsigned nFlags )
{
if ( ( nFlags & FOW_DEBUG_SHOW_OCCLUDERS ) == 0 )
{
return;
}
Vector vDiff = vLocation - m_vLocation;
if ( vDiff.Length2D() > flViewRadius + m_flRadius )
{
return;
}
if ( ( nFlags & FOW_DEBUG_SHOW_OCCLUDERS ) != 0 )
{
debugoverlay->AddSphereOverlay( m_vLocation, m_flRadius, 10, 10, 255, 0, 0, 127, FOW_DEBUG_VIEW_TIME );
debugoverlay->AddBoxOverlay( m_vLocation, Vector( -16.0f, -16.0f, -16.0f ), Vector( 16.0f, 16.0f, 16.0f ), QAngle( 0, 0, 0 ), 255, 0, 0, 127, FOW_DEBUG_VIEW_TIME );
}
}
#include <tier0/memdbgoff.h>