|
|
#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>
|