You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
623 lines
18 KiB
623 lines
18 KiB
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "render_pch.h"
|
|
#include "client.h"
|
|
#include "debug_leafvis.h"
|
|
#include "con_nprint.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "r_areaportal.h"
|
|
#include "cmodel_engine.h"
|
|
#include "con_nprint.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
ConVar r_ClipAreaPortals( "r_ClipAreaPortals", "1", FCVAR_CHEAT );
|
|
ConVar r_DrawPortals( "r_DrawPortals", "0", FCVAR_CHEAT );
|
|
ConVar r_ClipAreaFrustums( "r_ClipAreaFrustums", "1", FCVAR_CHEAT );
|
|
|
|
CUtlVector<CPortalRect> g_PortalRects;
|
|
bool g_bViewerInSolidSpace = false;
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// Classes.
|
|
// ------------------------------------------------------------------------------------ //
|
|
|
|
|
|
#define MAX_PORTAL_VERTS 32
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// Globals.
|
|
// ------------------------------------------------------------------------------------ //
|
|
|
|
// Visible areas from the client DLL + occluded areas using area portals.
|
|
#if defined(_PS3)
|
|
unsigned char g_RenderAreaBits[32] ALIGN16;
|
|
#else
|
|
unsigned char g_RenderAreaBits[32];
|
|
#endif
|
|
|
|
// Used to prevent it from coming back into portals while flowing through them.
|
|
static unsigned char g_AreaStack[32];
|
|
static uint32 g_AreaCounter[MAX_MAP_AREAS];
|
|
static CPortalRect g_AreaRect[MAX_MAP_AREAS];
|
|
|
|
// Frustums for each area for the current frame. Used to cull out leaves.
|
|
CUtlVector< Frustum_t, CUtlMemoryAligned< Frustum_t,16 > > g_AreaFrustum;
|
|
|
|
// List of areas marked visible this frame.
|
|
static unsigned short g_VisibleAreas[MAX_MAP_AREAS];
|
|
static int g_nVisibleAreas;
|
|
|
|
// Tied to g_AreaCounter.
|
|
static uint32 g_GlobalCounter = 1;
|
|
|
|
// ------------------------------------------------------------------------------------ //
|
|
// Functions.
|
|
// ------------------------------------------------------------------------------------ //
|
|
void R_Areaportal_LevelInit()
|
|
{
|
|
g_AreaFrustum.SetCount( host_state.worldbrush->m_nAreas );
|
|
V_memset( g_AreaCounter, 0, sizeof(g_AreaCounter) );
|
|
g_GlobalCounter = 1;
|
|
}
|
|
|
|
void R_Areaportal_LevelShutdown()
|
|
{
|
|
g_AreaFrustum.Purge();
|
|
g_PortalRects.Purge();
|
|
}
|
|
|
|
static inline void R_SetBit( unsigned char *pBits, int bit )
|
|
{
|
|
pBits[bit>>3] |= (1 << (bit&7));
|
|
}
|
|
|
|
static inline void R_ClearBit( unsigned char *pBits, int bit )
|
|
{
|
|
pBits[bit>>3] &= ~(1 << (bit&7));
|
|
}
|
|
|
|
static inline unsigned char R_TestBit( unsigned char *pBits, int bit )
|
|
{
|
|
return pBits[bit>>3] & (1 << (bit&7));
|
|
}
|
|
|
|
struct portalclip_t
|
|
{
|
|
portalclip_t()
|
|
{
|
|
lists[0] = v0;
|
|
lists[1] = v1;
|
|
}
|
|
Vector v0[MAX_PORTAL_VERTS];
|
|
Vector v1[MAX_PORTAL_VERTS];
|
|
Vector *lists[2];
|
|
};
|
|
|
|
// Transforms and clips the portal's verts to the view frustum. Returns false
|
|
// if the verts lie outside the frustum.
|
|
static inline bool GetPortalScreenExtents( dareaportal_t *pPortal,
|
|
portalclip_t * RESTRICT clip, CPortalRect &portalRect , float *pReflectionWaterHeight, VPlane *pFrustumPlanes )
|
|
{
|
|
portalRect.left = portalRect.bottom = 1e24;
|
|
portalRect.right = portalRect.top = -1e24;
|
|
bool bValidExtents = false;
|
|
worldbrushdata_t *pBrushData = host_state.worldbrush;
|
|
|
|
int nStartVerts = MIN( pPortal->m_nClipPortalVerts, MAX_PORTAL_VERTS );
|
|
|
|
// NOTE: We need two passes to deal with reflection. We need to compute
|
|
// the screen extents for both the reflected + non-reflected area portals
|
|
// and make bounds that surrounds them both.
|
|
int nPassCount = ( pReflectionWaterHeight != NULL ) ? 2 : 1;
|
|
for ( int j = 0; j < nPassCount; ++j )
|
|
{
|
|
int i;
|
|
for( i=0; i < nStartVerts; i++ )
|
|
{
|
|
clip->v0[i] = pBrushData->m_pClipPortalVerts[pPortal->m_FirstClipPortalVert+i];
|
|
|
|
// 2nd pass is to compute the reflected areaportal position
|
|
if ( j == 1 )
|
|
{
|
|
clip->v0[i].z = 2.0f * ( *pReflectionWaterHeight ) - clip->v0[i].z;
|
|
}
|
|
}
|
|
|
|
int iCurList = 0;
|
|
bool bAllClipped = false;
|
|
for( int iPlane=0; iPlane < 4; iPlane++ )
|
|
{
|
|
Vector *pIn = clip->lists[iCurList];
|
|
Vector *pOut = clip->lists[!iCurList];
|
|
|
|
int nOutVerts = 0;
|
|
int iPrev = nStartVerts - 1;
|
|
float flPrevDot = pFrustumPlanes[iPlane].m_Normal.Dot( pIn[iPrev] ) - pFrustumPlanes[iPlane].m_Dist;
|
|
for( int iCur=0; iCur < nStartVerts; iCur++ )
|
|
{
|
|
float flCurDot = pFrustumPlanes[iPlane].m_Normal.Dot( pIn[iCur] ) - pFrustumPlanes[iPlane].m_Dist;
|
|
|
|
if( (flCurDot > 0) != (flPrevDot > 0) )
|
|
{
|
|
if( nOutVerts < MAX_PORTAL_VERTS )
|
|
{
|
|
// Add the vert at the intersection.
|
|
float t = flPrevDot / (flPrevDot - flCurDot);
|
|
VectorLerp( pIn[iPrev], pIn[iCur], t, pOut[nOutVerts] );
|
|
|
|
++nOutVerts;
|
|
}
|
|
}
|
|
|
|
// Add this vert?
|
|
if( flCurDot > 0 )
|
|
{
|
|
if( nOutVerts < MAX_PORTAL_VERTS )
|
|
{
|
|
pOut[nOutVerts] = pIn[iCur];
|
|
++nOutVerts;
|
|
}
|
|
}
|
|
|
|
flPrevDot = flCurDot;
|
|
iPrev = iCur;
|
|
}
|
|
|
|
if( nOutVerts == 0 )
|
|
{
|
|
// If they're all behind, then this portal is clipped out.
|
|
bAllClipped = true;
|
|
break;
|
|
}
|
|
|
|
nStartVerts = nOutVerts;
|
|
iCurList = !iCurList;
|
|
}
|
|
|
|
if ( bAllClipped )
|
|
continue;
|
|
|
|
// Project all the verts and figure out the screen extents.
|
|
Vector screenPos;
|
|
Assert( iCurList == 0 );
|
|
for( i=0; i < nStartVerts; i++ )
|
|
{
|
|
Vector &point = clip->v0[i];
|
|
|
|
g_EngineRenderer->ClipTransform( point, &screenPos );
|
|
|
|
portalRect.left = fpmin( screenPos.x, portalRect.left );
|
|
portalRect.bottom = fpmin( screenPos.y, portalRect.bottom );
|
|
portalRect.top = fpmax( screenPos.y, portalRect.top );
|
|
portalRect.right = fpmax( screenPos.x, portalRect.right );
|
|
}
|
|
bValidExtents = true;
|
|
}
|
|
|
|
if ( !bValidExtents )
|
|
{
|
|
portalRect.left = portalRect.bottom = 0;
|
|
portalRect.right = portalRect.top = 0;
|
|
}
|
|
|
|
return bValidExtents;
|
|
}
|
|
|
|
|
|
// Fill in the intersection between the two rectangles.
|
|
inline bool GetRectIntersection( CPortalRect const *pRect1, CPortalRect const *pRect2, CPortalRect *pOut )
|
|
{
|
|
pOut->left = fpmax( pRect1->left, pRect2->left );
|
|
pOut->right = fpmin( pRect1->right, pRect2->right );
|
|
if( pOut->left >= pOut->right )
|
|
return false;
|
|
|
|
pOut->bottom = fpmax( pRect1->bottom, pRect2->bottom );
|
|
pOut->top = fpmin( pRect1->top, pRect2->top );
|
|
if( pOut->bottom >= pOut->top )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void R_FlowThroughArea( int area, const Vector &vecVisOrigin, const CPortalRect *pClipRect,
|
|
const VisOverrideData_t* pVisData, float *pReflectionWaterHeight )
|
|
{
|
|
#ifndef DEDICATED
|
|
// Update this area's frustum.
|
|
if( g_AreaCounter[area] != g_GlobalCounter )
|
|
{
|
|
g_VisibleAreas[g_nVisibleAreas] = area;
|
|
++g_nVisibleAreas;
|
|
|
|
g_AreaCounter[area] = g_GlobalCounter;
|
|
g_AreaRect[area] = *pClipRect;
|
|
}
|
|
else
|
|
{
|
|
// Expand the areaportal's rectangle to include the new cliprect.
|
|
CPortalRect *pFrustumRect = &g_AreaRect[area];
|
|
pFrustumRect->left = fpmin( pFrustumRect->left, pClipRect->left );
|
|
pFrustumRect->bottom = fpmin( pFrustumRect->bottom, pClipRect->bottom );
|
|
pFrustumRect->top = fpmax( pFrustumRect->top, pClipRect->top );
|
|
pFrustumRect->right = fpmax( pFrustumRect->right, pClipRect->right );
|
|
}
|
|
|
|
// Mark this area as visible.
|
|
R_SetBit( g_RenderAreaBits, area );
|
|
|
|
// Set that we're in this area on the stack.
|
|
R_SetBit( g_AreaStack, area );
|
|
|
|
worldbrushdata_t *pBrushData = host_state.worldbrush;
|
|
|
|
Assert( area < host_state.worldbrush->m_nAreas );
|
|
darea_t *pArea = &host_state.worldbrush->m_pAreas[area];
|
|
// temp buffer for clipping
|
|
portalclip_t clipTmp;
|
|
VPlane frustumPlanes[FRUSTUM_NUMPLANES];
|
|
g_Frustum.GetPlanes( frustumPlanes );
|
|
// Check all areas that connect to this area.
|
|
for( int iAreaPortal=0; iAreaPortal < pArea->numareaportals; iAreaPortal++ )
|
|
{
|
|
Assert( pArea->firstareaportal + iAreaPortal < pBrushData->m_nAreaPortals );
|
|
dareaportal_t *pAreaPortal = &pBrushData->m_pAreaPortals[ pArea->firstareaportal + iAreaPortal ];
|
|
|
|
// Don't flow back into a portal on the stack.
|
|
if( R_TestBit( g_AreaStack, pAreaPortal->otherarea ) )
|
|
continue;
|
|
|
|
// If this portal is closed, don't go through it.
|
|
if ( !R_TestBit( GetBaseLocalClient().m_chAreaPortalBits, pAreaPortal->m_PortalKey ) )
|
|
continue;
|
|
|
|
// Make sure the viewer is on the right side of the portal to see through it.
|
|
cplane_t *pPlane = &pBrushData->planes[ pAreaPortal->planenum ];
|
|
// Use the specified vis origin to test backface culling, or the main view if none was specified
|
|
float flDist = pPlane->normal.Dot( vecVisOrigin ) - pPlane->dist;
|
|
if( flDist < -0.1f )
|
|
continue;
|
|
|
|
// If the client doesn't want this area visible, don't try to flow into it.
|
|
if( !R_TestBit( GetBaseLocalClient().m_chAreaBits, pAreaPortal->otherarea ) )
|
|
continue;
|
|
|
|
CPortalRect portalRect;
|
|
bool portalVis = true;
|
|
|
|
// don't try to clip portals if the viewer is practically in the plane
|
|
float fDistTolerance = (pVisData)?(pVisData->m_fDistToAreaPortalTolerance):(0.1f);
|
|
if ( flDist > fDistTolerance )
|
|
{
|
|
portalVis = GetPortalScreenExtents( pAreaPortal, &clipTmp, portalRect, pReflectionWaterHeight, frustumPlanes );
|
|
}
|
|
else
|
|
{
|
|
portalRect.left = -1;
|
|
portalRect.top = 1;
|
|
portalRect.right = 1;
|
|
portalRect.bottom = -1; // note top/bottom reversed!
|
|
//portalVis=true - not needed, default
|
|
}
|
|
if( portalVis )
|
|
{
|
|
CPortalRect intersection;
|
|
if( GetRectIntersection( &portalRect, pClipRect, &intersection ) )
|
|
{
|
|
#ifdef USE_CONVARS
|
|
if( r_DrawPortals.GetInt() )
|
|
{
|
|
g_PortalRects.AddToTail( intersection );
|
|
}
|
|
#endif
|
|
|
|
// Ok, we can see into this area.
|
|
R_FlowThroughArea( pAreaPortal->otherarea, vecVisOrigin, &intersection, pVisData, pReflectionWaterHeight );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Mark that we're leaving this area.
|
|
R_ClearBit( g_AreaStack, area );
|
|
#endif
|
|
}
|
|
|
|
|
|
static void IncrementGlobalCounter()
|
|
{
|
|
if( g_GlobalCounter == 0xFFFFFFFF )
|
|
{
|
|
for( int i=0; i < g_AreaFrustum.Count(); i++ )
|
|
g_AreaCounter[i] = 0;
|
|
|
|
g_GlobalCounter = 1;
|
|
}
|
|
else
|
|
{
|
|
g_GlobalCounter++;
|
|
}
|
|
}
|
|
|
|
ConVar r_snapportal( "r_snapportal", "-1" );
|
|
static void R_SetupVisibleAreaFrustums()
|
|
{
|
|
#ifndef DEDICATED
|
|
const CViewSetup &viewSetup = g_EngineRenderer->ViewGetCurrent();
|
|
|
|
CPortalRect viewWindow;
|
|
if( viewSetup.m_bOrtho )
|
|
{
|
|
viewWindow.right = viewSetup.m_OrthoRight;
|
|
viewWindow.left = viewSetup.m_OrthoLeft;
|
|
viewWindow.top = viewSetup.m_OrthoTop;
|
|
viewWindow.bottom = viewSetup.m_OrthoBottom;
|
|
}
|
|
else
|
|
{
|
|
// Assuming a view plane distance of 1, figure out the boundaries of a window
|
|
// the view would project into given the FOV.
|
|
float xFOV = g_EngineRenderer->GetFov() * 0.5f;
|
|
float yFOV = g_EngineRenderer->GetFovY() * 0.5f;
|
|
|
|
viewWindow.right = tan( DEG2RAD( xFOV ) );
|
|
viewWindow.left = -viewWindow.right;
|
|
viewWindow.top = tan( DEG2RAD( yFOV ) );
|
|
viewWindow.bottom = -viewWindow.top;
|
|
}
|
|
|
|
Vector viewOrigin = CurrentViewOrigin();
|
|
Vector forward = CurrentViewForward();
|
|
Vector right = CurrentViewRight();
|
|
Vector up = CurrentViewUp();
|
|
VPlane planes[FRUSTUM_NUMPLANES];
|
|
|
|
// Now scale the portals as specified in the normalized view frustum (-1,-1,1,1)
|
|
// into our view window and generate planes out of that.
|
|
for( int i=0; i < g_nVisibleAreas; i++ )
|
|
{
|
|
CPortalRect *pRect = &g_AreaRect[ g_VisibleAreas[i] ];
|
|
Frustum_t *pFrustum = &g_AreaFrustum[g_VisibleAreas[i]];
|
|
CPortalRect portalWindow;
|
|
portalWindow.left = RemapVal( pRect->left, -1, 1, viewWindow.left, viewWindow.right );
|
|
portalWindow.right = RemapVal( pRect->right, -1, 1, viewWindow.left, viewWindow.right );
|
|
portalWindow.top = RemapVal( pRect->top, -1, 1, viewWindow.bottom, viewWindow.top );
|
|
portalWindow.bottom = RemapVal( pRect->bottom, -1, 1, viewWindow.bottom, viewWindow.top );
|
|
|
|
if( viewSetup.m_bOrtho )
|
|
{
|
|
// Left and right planes...
|
|
float orgOffset = DotProduct(viewOrigin, right);
|
|
planes[FRUSTUM_LEFT].Init( right, portalWindow.left + orgOffset );
|
|
planes[FRUSTUM_RIGHT].Init( -right, -portalWindow.right - orgOffset );
|
|
|
|
// Top and bottom planes...
|
|
orgOffset = DotProduct(viewOrigin, up);
|
|
planes[FRUSTUM_TOP].Init( up, portalWindow.top + orgOffset );
|
|
planes[FRUSTUM_BOTTOM].Init( -up, -portalWindow.bottom - orgOffset );
|
|
planes[FRUSTUM_NEARZ].Init( forward, 0 );
|
|
planes[FRUSTUM_FARZ].Init( -forward, -1e6f );
|
|
pFrustum->SetPlanes(planes);
|
|
}
|
|
else
|
|
{
|
|
Vector normal;
|
|
|
|
// right side
|
|
normal = portalWindow.right * forward - right;
|
|
VectorNormalize(normal);
|
|
planes[FRUSTUM_RIGHT].Init( normal, DotProduct(normal,viewOrigin) );
|
|
// left side
|
|
normal = CurrentViewRight() - portalWindow.left * forward;
|
|
VectorNormalize(normal);
|
|
planes[FRUSTUM_LEFT].Init( normal, DotProduct(normal,viewOrigin) );
|
|
|
|
// top
|
|
normal = portalWindow.top * forward - up;
|
|
VectorNormalize(normal);
|
|
planes[FRUSTUM_TOP].Init( normal, DotProduct(normal,viewOrigin) );
|
|
|
|
// bottom
|
|
normal = up - portalWindow.bottom * forward;
|
|
VectorNormalize(normal);
|
|
planes[FRUSTUM_BOTTOM].Init( normal, DotProduct(normal,viewOrigin) );
|
|
|
|
// nearz
|
|
planes[FRUSTUM_NEARZ].Init( forward, DotProduct(forward, viewOrigin) + viewSetup.zNear );
|
|
|
|
// farz
|
|
planes[FRUSTUM_FARZ].Init( -forward, DotProduct(-forward, viewOrigin) - viewSetup.zFar );
|
|
pFrustum->SetPlanes(planes);
|
|
}
|
|
}
|
|
// DEBUG: Code to visualize the areaportal frustums in 3D
|
|
// Useful for debugging
|
|
if ( r_snapportal.GetInt() >= 0 )
|
|
{
|
|
extern void CSGFrustum( Frustum_t &frustum );
|
|
for ( int i = 0; i < g_nVisibleAreas; i++ )
|
|
{
|
|
if ( g_VisibleAreas[i] == r_snapportal.GetInt() )
|
|
{
|
|
Frustum_t *pFrustum = &g_AreaFrustum[ g_VisibleAreas[i] ];
|
|
|
|
pFrustum->SetPlane( FRUSTUM_NEARZ, forward,
|
|
DotProduct(forward, viewOrigin) );
|
|
pFrustum->SetPlane( FRUSTUM_FARZ, -forward,
|
|
DotProduct(-forward, viewOrigin + forward*500) );
|
|
r_snapportal.SetValue( -1 );
|
|
CSGFrustum( *pFrustum );
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
// culls a node to the frustum or area frustum
|
|
bool R_CullNode( mnode_t *pNode )
|
|
{
|
|
if ( !g_bViewerInSolidSpace && pNode->area > 0 )
|
|
{
|
|
// First make sure its whole area is even visible.
|
|
if( !R_IsAreaVisible( pNode->area ) )
|
|
return true;
|
|
return CullNodeSIMD( g_AreaFrustum[pNode->area], pNode );
|
|
}
|
|
|
|
return CullNodeSIMD( g_Frustum, pNode );
|
|
}
|
|
|
|
static ConVar r_portalscloseall( "r_portalscloseall", "0" );
|
|
static ConVar r_portalsopenall( "r_portalsopenall", "0", FCVAR_CHEAT, "Open all portals" );
|
|
static ConVar r_ShowViewerArea( "r_ShowViewerArea", "0" );
|
|
|
|
void R_SetupAreaBits( int iForceViewLeaf /* = -1 */, const VisOverrideData_t* pVisData /* = NULL */, float *pWaterReflectionHeight /* = NULL */ )
|
|
{
|
|
IncrementGlobalCounter();
|
|
|
|
Vector vVisOrigin = pVisData ? pVisData->m_vecVisOrigin : g_EngineRenderer->ViewOrigin();
|
|
|
|
// Clear the visible area bits.
|
|
memset( g_RenderAreaBits, 0, sizeof( g_RenderAreaBits ) );
|
|
memset( g_AreaStack, 0, sizeof( g_AreaStack ) );
|
|
|
|
// Our initial clip rect is the whole screen.
|
|
CPortalRect rect;
|
|
rect.left = rect.bottom = -1;
|
|
rect.top = rect.right = 1;
|
|
|
|
// Flow through areas starting at the one we're in.
|
|
int leaf = iForceViewLeaf;
|
|
// If view point override wasn't specified, use the current view origin
|
|
if ( iForceViewLeaf == -1 )
|
|
{
|
|
leaf = CM_PointLeafnum( vVisOrigin );
|
|
}
|
|
|
|
g_bViewerInSolidSpace = false;
|
|
if( r_portalscloseall.GetBool() )
|
|
{
|
|
if ( GetBaseLocalClient().m_bAreaBitsValid )
|
|
{
|
|
// Clear the visible area bits.
|
|
memset( g_RenderAreaBits, 0, sizeof( g_RenderAreaBits ) );
|
|
int area = host_state.worldbrush->leafs[leaf].area;
|
|
R_SetBit( g_RenderAreaBits, area );
|
|
|
|
g_VisibleAreas[0] = area;
|
|
g_nVisibleAreas = 1;
|
|
|
|
g_AreaCounter[area] = g_GlobalCounter;
|
|
g_AreaRect[area] = rect;
|
|
}
|
|
else
|
|
{
|
|
g_bViewerInSolidSpace = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int ss_Slot = GET_ACTIVE_SPLITSCREEN_SLOT();
|
|
|
|
if ( host_state.worldbrush->leafs[leaf].contents & CONTENTS_SOLID ||
|
|
GetBaseLocalClient().ishltv ||
|
|
#if defined( REPLAY_ENABLED )
|
|
GetBaseLocalClient().isreplay ||
|
|
#endif
|
|
!GetBaseLocalClient().m_bAreaBitsValid ||
|
|
r_portalsopenall.GetBool() )
|
|
{
|
|
// Draw everything if we're in solid space or if r_portalsopenall is true (used for building cubemaps)
|
|
g_bViewerInSolidSpace = true;
|
|
|
|
if ( r_ShowViewerArea.GetInt() )
|
|
Con_NPrintf( 3 + ss_Slot, "(%d), Viewer area: (solid space)", ss_Slot );
|
|
}
|
|
else
|
|
{
|
|
int area = host_state.worldbrush->leafs[leaf].area;
|
|
|
|
if ( r_ShowViewerArea.GetInt() )
|
|
Con_NPrintf( 3 + ss_Slot, "(%d) Viewer area: %d", ss_Slot, area );
|
|
|
|
g_nVisibleAreas = 0;
|
|
|
|
if ( pVisData && pVisData->m_bTrimFrustumToPortalCorners && r_ClipAreaFrustums.GetBool() )
|
|
{
|
|
const float flDistToPortalTolerance = 16.0f;
|
|
// If the current view origin is within some perpendicular distance of the exit portal AND within the radius of the portal,
|
|
// don't attempt to optimize the area rect/frustum
|
|
Vector vViewOriginToPortalOrigin = CurrentViewOrigin() - pVisData->m_vPortalOrigin;
|
|
if ( fabsf( vViewOriginToPortalOrigin.Dot( pVisData->m_vPortalForward ) ) > flDistToPortalTolerance ||
|
|
vViewOriginToPortalOrigin.Length() > pVisData->m_flPortalRadius )
|
|
{
|
|
// invert the rectangle and then grow it to fit the portal
|
|
rect.left = rect.bottom = 1;
|
|
rect.right = rect.top = -1;
|
|
for ( int i = 0; i < 4; ++ i )
|
|
{
|
|
Vector vScreenPos;
|
|
g_EngineRenderer->ClipTransform( pVisData->m_vPortalCorners[ i ], &vScreenPos );
|
|
|
|
rect.left = fpmin( vScreenPos.x, rect.left );
|
|
rect.bottom = fpmin( vScreenPos.y, rect.bottom );
|
|
rect.right = fpmax( vScreenPos.x, rect.right );
|
|
rect.top = fpmax( vScreenPos.y, rect.top );
|
|
}
|
|
rect.left = fpmax( rect.left, -1.0f );
|
|
rect.bottom = fpmax( rect.bottom, -1.0f );
|
|
rect.right = fpmin( rect.right, 1.0f );
|
|
rect.top = fpmin( rect.top, 1.0f );
|
|
}
|
|
}
|
|
R_FlowThroughArea( area, vVisOrigin, &rect, pVisData, pWaterReflectionHeight );
|
|
}
|
|
}
|
|
if ( !g_bViewerInSolidSpace )
|
|
{
|
|
R_SetupVisibleAreaFrustums();
|
|
}
|
|
}
|
|
|
|
|
|
bool R_ShouldUseAreaFrustum( int area )
|
|
{
|
|
if ( g_AreaCounter[area] == g_GlobalCounter )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
const Frustum_t* GetAreaFrustum( int area )
|
|
{
|
|
if ( g_AreaCounter[area] == g_GlobalCounter )
|
|
return &g_AreaFrustum[area];
|
|
else
|
|
return &g_Frustum;
|
|
}
|
|
|
|
|
|
int GetAllAreaFrustums( Frustum_t **pFrustumList, int listMax )
|
|
{
|
|
int count = g_AreaFrustum.Count();
|
|
count = MIN(listMax,count);
|
|
for ( int i = 0; i < count; i++ )
|
|
{
|
|
if ( g_AreaCounter[i] == g_GlobalCounter )
|
|
{
|
|
pFrustumList[i] = &g_AreaFrustum[i];
|
|
}
|
|
else
|
|
{
|
|
pFrustumList[i] = &g_Frustum;
|
|
}
|
|
}
|
|
return count;
|
|
}
|