Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

1747 lines
71 KiB

//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "cbase.h"
#include "portalrenderable_flatbasic.h"
#include "precache_register.h"
#include "Portal_DynamicMeshRenderingUtils.h"
#include "portal_shareddefs.h"
#include "view.h"
#include "c_pixel_visibility.h"
#include "glow_overlay.h"
#include "portal_render_targets.h"
#include "materialsystem/ITexture.h"
#include "toolframework/itoolframework.h"
#include "toolframework_client.h"
#include "tier1/keyvalues.h"
#include "view_scene.h"
#include "tier0/vprof.h"
#include "materialsystem/imaterialvar.h"
extern ConVar r_portal_fastpath;
#define PORTALRENDERABLE_FLATBASIC_MINPIXELVIS 0.0f
CUtlStack<Vector4D> CPortalRenderable_FlatBasic::ms_clipPlaneStack;
CPortalRenderable_FlatBasic::CPortalRenderable_FlatBasic( void )
: m_pLinkedPortal( NULL ),
m_ptOrigin( 0.0f, 0.0f, 0.0f ),
m_vForward( 1.0f, 0.0f, 0.0f ),
m_vUp( 0.0f, 0.0f, 1.0f ),
m_vRight( 0.0f, 1.0f, 0.0f ),
m_bIsPortal2( false )
{
m_InternallyMaintainedData.m_VisData.m_fDistToAreaPortalTolerance = 64.0f;
m_InternallyMaintainedData.m_VisData.m_vecVisOrigin = Vector(0,0,0);
m_InternallyMaintainedData.m_VisData.m_bTrimFrustumToPortalCorners = false;
m_InternallyMaintainedData.m_VisData.m_vPortalOrigin = m_InternallyMaintainedData.m_VisData.m_vPortalForward = vec3_origin;
m_InternallyMaintainedData.m_VisData.m_flPortalRadius = 0.0f;
m_InternallyMaintainedData.m_iViewLeaf = -1;
for( int i = 0; i != ARRAYSIZE( m_InternallyMaintainedData.m_DepthDoublerTextureView ); ++i )
{
m_InternallyMaintainedData.m_DepthDoublerTextureView[i].Identity();
}
m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration = false;
m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = SKYBOX_NOT_VISIBLE;
m_InternallyMaintainedData.m_ptForwardOrigin.Init( 1.0f, 0.0f, 0.0f );
m_InternallyMaintainedData.m_ptCorners[0] =
m_InternallyMaintainedData.m_ptCorners[1] =
m_InternallyMaintainedData.m_ptCorners[2] =
m_InternallyMaintainedData.m_ptCorners[3] =
Vector( 0.0f, 0.0f, 0.0f );
}
void CPortalRenderable_FlatBasic::PortalMoved( void )
{
m_InternallyMaintainedData.m_ptForwardOrigin = m_ptOrigin + m_vForward;
m_InternallyMaintainedData.m_fPlaneDist = m_vForward.Dot( m_ptOrigin );
// Update the points on the portal which we add to PVS
{
Vector vScaledRight = m_vRight * m_fHalfWidth;
Vector vScaledUp = m_vUp * m_fHalfHeight;
m_InternallyMaintainedData.m_ptCorners[0] = (m_InternallyMaintainedData.m_ptForwardOrigin + vScaledRight) + vScaledUp;
m_InternallyMaintainedData.m_ptCorners[1] = (m_InternallyMaintainedData.m_ptForwardOrigin - vScaledRight) + vScaledUp;
m_InternallyMaintainedData.m_ptCorners[2] = (m_InternallyMaintainedData.m_ptForwardOrigin - vScaledRight) - vScaledUp;
m_InternallyMaintainedData.m_ptCorners[3] = (m_InternallyMaintainedData.m_ptForwardOrigin + vScaledRight) - vScaledUp;
m_InternallyMaintainedData.m_VisData.m_vecVisOrigin = m_InternallyMaintainedData.m_ptForwardOrigin;
m_InternallyMaintainedData.m_VisData.m_fDistToAreaPortalTolerance = 64.0f;
m_InternallyMaintainedData.m_VisData.m_bTrimFrustumToPortalCorners = true;
memcpy( m_InternallyMaintainedData.m_VisData.m_vPortalCorners, m_InternallyMaintainedData.m_ptCorners, sizeof( m_InternallyMaintainedData.m_VisData.m_vPortalCorners ) );
m_InternallyMaintainedData.m_VisData.m_vPortalOrigin = m_ptOrigin;
m_InternallyMaintainedData.m_VisData.m_vPortalForward = m_vForward;
m_InternallyMaintainedData.m_VisData.m_flPortalRadius = sqrtf( m_fHalfWidth * m_fHalfWidth + m_fHalfHeight * m_fHalfHeight );
m_InternallyMaintainedData.m_iViewLeaf = enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptForwardOrigin );
}
m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = engine->IsSkyboxVisibleFromPoint( m_InternallyMaintainedData.m_ptForwardOrigin );
for( int i = 0; i < 4 && ( m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners != SKYBOX_3DSKYBOX_VISIBLE ); ++i )
{
SkyboxVisibility_t nCornerVis = engine->IsSkyboxVisibleFromPoint( m_InternallyMaintainedData.m_ptCorners[i] );
if ( ( m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners == SKYBOX_NOT_VISIBLE ) || ( nCornerVis != SKYBOX_NOT_VISIBLE ) )
{
m_InternallyMaintainedData.m_nSkyboxVisibleFromCorners = nCornerVis;
}
}
//render fix bounding planes
{
for( int i = 0; i != PORTALRENDERFIXMESH_OUTERBOUNDPLANES; ++i )
{
float fCirclePos = ((float)(i)) * ((M_PI * 2.0f) / (float)PORTALRENDERFIXMESH_OUTERBOUNDPLANES);
float fUpBlend = cosf( fCirclePos );
float fRightBlend = sinf( fCirclePos );
Vector vNormal = -fUpBlend * m_vUp - fRightBlend * m_vRight;
Vector ptOnPlane = m_ptOrigin + (m_vUp * (fUpBlend * m_fHalfHeight * 1.1f)) + (m_vRight * (fRightBlend * m_fHalfWidth * 1.1f));
m_InternallyMaintainedData.m_BoundingPlanes[i].Init( vNormal, vNormal.Dot( ptOnPlane ) );
}
m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].Init( -m_vForward, (-m_vForward).Dot( m_ptOrigin ) );
m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 1].Init( m_vForward, m_vForward.Dot( m_ptOrigin - (m_vForward * 5.0f) ) );
}
//update depth doubler usability flag
m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration =
( m_pLinkedPortal && //linked to another portal
( m_vForward.Dot( m_pLinkedPortal->m_ptOrigin - m_ptOrigin ) > 0.0f ) && //this portal looking in the general direction of the other portal
( m_vForward.Dot( m_pLinkedPortal->m_vForward ) < -0.7071f ) ); //within 45 degrees of facing directly at each other
if( m_pLinkedPortal )
m_pLinkedPortal->m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration = m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration;
#if 0
if ( IsToolRecording() && ((m_ptOrigin != m_ptLastRecordedOrigin) || (m_qAbsAngle != m_qLastRecordedAngle)) )
{
static EntityTeleportedRecordingState_t state;
KeyValues *msg = new KeyValues( "entity_teleported" );
msg->SetPtr( "state", &state );
state.m_bTeleported = true;
state.m_bViewOverride = false;
state.m_vecTo = m_ptOrigin;
state.m_qaTo = m_qAbsAngle;
VMatrix mat_OldPosition, mat_InvOldPosition, mat_CurPosition;
AngleMatrix( m_qLastRecordedAngle, m_ptLastRecordedOrigin, mat_OldPosition.As3x4() );
MatrixInverseTR( mat_OldPosition, mat_InvOldPosition );
AngleMatrix( m_qAbsAngle, m_ptOrigin, mat_CurPosition.As3x4() );
ConcatTransforms( mat_InvOldPosition.As3x4(), mat_CurPosition.As3x4(), state.m_teleportMatrix );
//state.m_teleportMatrix.Init( Vector( 1.0f, 0.0f, 0.0f ), Vector( 0.0f, 1.0f, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), vec3_origin );
m_ptLastRecordedOrigin = m_ptOrigin;
m_qLastRecordedAngle = m_qAbsAngle;
// Post a message back to all IToolSystems
Assert( (int)GetToolHandle() != 0 );
ToolFramework_PostToolMessage( GetToolHandle(), msg );
msg->deleteThis();
}
#endif
}
bool CPortalRenderable_FlatBasic::WillUseDepthDoublerThisDraw( void ) const
{
return m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration &&
(g_pPortalRender->GetRemainingPortalViewDepth() == 0) &&
(g_pPortalRender->GetViewRecursionLevel() > 1) &&
(g_pPortalRender->GetCurrentViewEntryPortal() == this);
}
ConVar r_portal_use_complex_frustums( "r_portal_use_complex_frustums", "1", FCVAR_CLIENTDLL, "View optimization, turn this off if you get odd visual bugs." );
bool CPortalRenderable_FlatBasic::CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum )
{
if( r_portal_use_complex_frustums.GetBool() == false )
return false;
if( (g_pPortalRender->GetViewRecursionLevel() == 0) &&
( (ptCurrentViewOrigin - m_ptOrigin).LengthSqr() < (m_fHalfHeight * m_fHalfHeight) ) )//FIXME: Player closeness check might need reimplementation
{
//calculations are most likely going to be completely useless, return nothing
return false;
}
if( m_pLinkedPortal == NULL )
return false;
if( m_vForward.Dot( ptCurrentViewOrigin ) <= m_InternallyMaintainedData.m_fPlaneDist )
return false; //looking at portal backface
return CalcFrustumThroughPolygon( m_InternallyMaintainedData.m_ptCorners, 4, ptCurrentViewOrigin, OutputFrustum );
}
ConVar cl_showcomplexfrustum( "cl_showcomplexfrustum", "0" );
bool CPortalRenderable_FlatBasic::CalcFrustumThroughPolygon( const Vector *pPolyVertices, int iPolyVertCount, const Vector &ptCurrentViewOrigin, Frustum OutputFrustum )
{
int iViewRecursionLevel = g_pPortalRender->GetViewRecursionLevel();
int iNextViewRecursionLevel = iViewRecursionLevel + 1;
Vector vTransformedViewOrigin = m_matrixThisToLinked * ptCurrentViewOrigin;
VPlane *pInputFrustumPlanes = g_pPortalRender->GetRecursiveViewComplexFrustums( iViewRecursionLevel ).Base();
int iInputFrustumPlanes = g_pPortalRender->GetRecursiveViewComplexFrustums( iViewRecursionLevel ).Count();
Assert( iInputFrustumPlanes > 0 );
Vector *pClippedVerts;
int iClippedVertCount;
{
//clip the polygon by the input frustum
int iAllocSize = iPolyVertCount + iInputFrustumPlanes;
Vector *pWorkVerts[2];
pWorkVerts[0] = (Vector *)stackalloc( sizeof( Vector ) * iAllocSize * 2 ); //possible to add 1 point per cut, iPolyVertCount starting points, iInputFrustumPlaneCount cuts
pWorkVerts[1] = pWorkVerts[0] + iAllocSize;
//clip by first plane and put output into pInVerts
iClippedVertCount = ClipPolyToPlane( (Vector *)pPolyVertices, iPolyVertCount, pWorkVerts[0], pInputFrustumPlanes[0].m_Normal, pInputFrustumPlanes[0].m_Dist, 0.01f );
//clip by other planes and flipflop in and out pointers
for( int i = 1; i != iInputFrustumPlanes; ++i )
{
if( iClippedVertCount < 3 )
return false; //nothing left in the frustum
iClippedVertCount = ClipPolyToPlane( pWorkVerts[(i & 1) ^ 1], iClippedVertCount, pWorkVerts[i & 1], pInputFrustumPlanes[i].m_Normal, pInputFrustumPlanes[i].m_Dist, 0.01f );
}
if( iClippedVertCount < 3 )
return false; //nothing left in the frustum
pClippedVerts = pWorkVerts[iInputFrustumPlanes & 1];
}
for( int i = 0; i != iClippedVertCount; ++i )
{
pClippedVerts[i] = m_matrixThisToLinked * pClippedVerts[i];
}
VPlane plane_NearZ, plane_FarZ;
//Near Z
plane_NearZ.Init( m_pLinkedPortal->m_vForward, m_pLinkedPortal->m_InternallyMaintainedData.m_fPlaneDist );
//Far Z
{
Vector vNormal = m_matrixThisToLinked.ApplyRotation( pInputFrustumPlanes[iInputFrustumPlanes - 1].m_Normal );
Vector ptOnPlane = pInputFrustumPlanes[iInputFrustumPlanes - 1].m_Dist * pInputFrustumPlanes[iInputFrustumPlanes - 1].m_Normal;
plane_FarZ.Init( vNormal, vNormal.Dot( m_matrixThisToLinked * ptOnPlane ) );
}
VPlane *pOutputFrustumPlanes = (VPlane *)stackalloc( sizeof( VPlane ) * MAX( iClippedVertCount, 4 ) );
//calculate and store the complex frustum with an unbounded number of planes
{
int iComplexCount = UTIL_CalcFrustumThroughConvexPolygon( pClippedVerts, iClippedVertCount, vTransformedViewOrigin, NULL, 0, pOutputFrustumPlanes, iClippedVertCount, 0 );
if( iComplexCount == 0 )
return false;
g_pPortalRender->GetRecursiveViewComplexFrustums( iNextViewRecursionLevel ).SetCount( iComplexCount + 2 ); //+2 for near and far z planes
VPlane *pWritePlane = g_pPortalRender->GetRecursiveViewComplexFrustums( iNextViewRecursionLevel ).Base();
/*for( int i = 0; i != iComplexCount; ++i )
{
Vector vPoint = m_matrixThisToLinked * (pOutputFrustumPlanes[i].m_Normal * pOutputFrustumPlanes[i].m_Dist);
Vector vNormal = m_matrixThisToLinked.ApplyRotation( pOutputFrustumPlanes[i].m_Normal );
pWritePlane->Init( vNormal, vNormal.Dot( vPoint ) );
++pWritePlane;
}*/
memcpy( pWritePlane, pOutputFrustumPlanes, sizeof( VPlane ) * iComplexCount );
pWritePlane += iComplexCount;
*pWritePlane = plane_NearZ;
++pWritePlane;
*pWritePlane = plane_FarZ;
}
//calculate and store the simple frustum, limited to exactly 6 planes
{
int iSimpleCount = UTIL_CalcFrustumThroughConvexPolygon( pClippedVerts, iClippedVertCount, vTransformedViewOrigin, NULL, 0, pOutputFrustumPlanes, 4, 0 );
VPlane *pWritePlane = &OutputFrustum[0];
/*for( int i = 0; i != iSimpleCount; ++i )
{
Vector vPoint = m_matrixThisToLinked * (pOutputFrustumPlanes[i].m_Normal * pOutputFrustumPlanes[i].m_Dist);
Vector vNormal = m_matrixThisToLinked.ApplyRotation( pOutputFrustumPlanes[i].m_Normal );
pWritePlane->Init( vNormal, vNormal.Dot( vPoint ) );
++pWritePlane;
}*/
memcpy( pWritePlane, pOutputFrustumPlanes, sizeof( VPlane ) * iSimpleCount );
pWritePlane += iSimpleCount;
while( iSimpleCount < 4 )
{
//crap, just copy the near plane into would-be empty slots
*pWritePlane = plane_NearZ;
++pWritePlane;
++iSimpleCount;
}
*pWritePlane = plane_NearZ;
++pWritePlane;
*pWritePlane = plane_FarZ;
}
return true;
}
static void DrawFrustum( Frustum_t &frustum )
{
const int maxPoints = 8;
int i;
for( i = 0; i < FRUSTUM_NUMPLANES; i++ )
{
Vector points[maxPoints];
Vector points2[maxPoints];
Vector normal;
float dist;
frustum.GetPlane( i, &normal, &dist );
int numPoints = PolyFromPlane( points, normal, dist );
Assert( numPoints <= maxPoints );
Vector *in, *out;
in = points;
out = points2;
int j;
for( j = 0; j < FRUSTUM_NUMPLANES; j++ )
{
if( i == j )
{
continue;
}
frustum.GetPlane( j, &normal, &dist );
numPoints = ClipPolyToPlane( in, numPoints, out, normal, dist );
Assert( numPoints <= maxPoints );
V_swap( in, out );
}
int c;
for( c = 0; c < numPoints; c++ )
{
debugoverlay->AddLineOverlay( in[c], in[(c+1)%numPoints], 0, 255, 0, false, -1 );
}
}
}
ConVar r_drawportalfrustum( "r_drawportalfrustum", "0" );
ConVar r_lockportalfrustum( "r_lockportalfrustum", "0" );
void CPortalRenderable_FlatBasic::RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView )
{
VPROF( "CPortalRenderable_FlatBasic::RenderPortalViewToBackBuffer" );
if( m_pLinkedPortal == NULL ) //not linked to any portal, so we'll be all static anyways
return;
VPROF_INCREMENT_COUNTER( "PortalRenders", 1 );
Frustum FrustumBackup;
memcpy( FrustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
Frustum seeThroughFrustum;
bool bUseSeeThroughFrustum;
bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
if ( r_drawportalfrustum.GetBool() )
{
static Frustum_t tmpFrustum;
if ( !r_lockportalfrustum.GetBool() )
{
tmpFrustum.SetPlanes( seeThroughFrustum );
}
if ( bUseSeeThroughFrustum )
{
DrawFrustum( tmpFrustum );
}
}
Vector vCameraForward;
AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
// Setup fog state for the camera.
Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
Vector ptRemotePortalPosition = m_pLinkedPortal->m_ptOrigin;
Vector vRemotePortalForward = m_pLinkedPortal->m_vForward;
CViewSetup portalView = cameraView;
if( portalView.zNear < 1.0f )
portalView.zNear = 1.0f;
QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
portalView.origin = ptPOVOrigin;
portalView.angles = qPOVAngles;
VMatrix matCurrentView;
if( cameraView.m_bCustomViewMatrix )
{
matCurrentView.CopyFrom3x4( cameraView.m_matCustomViewMatrix );
}
else
{
matCurrentView.Identity();
//generate the view matrix for the existing position and angle, then wedge our portal matrix onto it as a world transformation that prepends the view
MatrixRotate( matCurrentView, Vector( 1, 0, 0 ), -cameraView.angles[2] );
MatrixRotate( matCurrentView, Vector( 0, 1, 0 ), -cameraView.angles[0] );
MatrixRotate( matCurrentView, Vector( 0, 0, 1 ), -cameraView.angles[1] );
MatrixTranslate( matCurrentView, -cameraView.origin );
}
VMatrix matTemp = matCurrentView * m_pLinkedPortal->m_matrixThisToLinked;
portalView.m_matCustomViewMatrix = matTemp.As3x4();
portalView.m_bCustomViewMatrix = true;
CopyToCurrentView( pViewRender, portalView );
CMatRenderContextPtr pRenderContext( materials );
//if we look through multiple unique pairs of portals, we have to take care not to clip too much
bool bReplaceOldPortalClipPlane = (g_pPortalRender->GetViewRecursionLevel() != 0) && (dynamic_cast<CPortalRenderable_FlatBasic *>(g_pPortalRender->GetCurrentViewExitPortal()) != NULL);
Vector4D vClipPlaneToRestore( 0.0f, 0.0f, 0.0f, 0.0f );
Assert( ( g_pPortalRender->GetViewRecursionLevel() == 0 ) ? ( ms_clipPlaneStack.Count() == 0 ) : true );
{
Vector4D vCustomClipPlane;
vCustomClipPlane.x = vRemotePortalForward.x;
vCustomClipPlane.y = vRemotePortalForward.y;
vCustomClipPlane.z = vRemotePortalForward.z;
vCustomClipPlane.w = vRemotePortalForward.Dot( ptRemotePortalPosition ) - 2.0f; //moving it back a smidge to eliminate visual artifacts for half-in objects
if ( g_pMaterialSystemHardwareConfig->UseFastClipping() )
{
// We need to make sure the clip plane never goes behind the eye position, as that would result in a degenerate projection matrix.
// Note: This clip plane is for the distant side of the portal, so the camera is supposed to stay behind this plane (flCamDist should be negative).
float flCamDist = DotProduct( cameraView.origin, vRemotePortalForward ) - vCustomClipPlane.w;
static const float CAMERA_DIST_EPSILON = 1.0f;
if ( flCamDist > -CAMERA_DIST_EPSILON )
{
float flClipPlaneCorrectionOffset = flCamDist + CAMERA_DIST_EPSILON;
vCustomClipPlane.w += flClipPlaneCorrectionOffset;
}
}
if( bReplaceOldPortalClipPlane )
{
pRenderContext->PopCustomClipPlane(); //HACKHACK: We really only want to remove the clip plane of the current portal view. This assumes we're the only ones leaving clip planes on the stack
ms_clipPlaneStack.Pop( vClipPlaneToRestore );
}
pRenderContext->PushCustomClipPlane( vCustomClipPlane.Base() );
ms_clipPlaneStack.Push( Vector4D( vCustomClipPlane ) );
}
shadowmgr->PushFlashlightScissorBounds();
{
ViewCustomVisibility_t customVisibility;
m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
CMatRenderContextPtr pRenderContext( materials );
render->Push3DView( pRenderContext, portalView, 0, NULL, pViewRender->GetFrustum() );
{
if( bUseSeeThroughFrustum)
memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
render->OverrideViewFrustum( pViewRender->GetFrustum() );
SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() + 1 );
CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
//DRAW!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
{
PIXEVENT( pRenderContext, "PortalRender" );
ViewDrawScene_PortalStencil( pViewRender, portalView, &customVisibility );
}
SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
if( m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration && (g_pPortalRender->GetRemainingPortalViewDepth() == 1) )
{
//save the view matrix for usage with the depth doubler.
//It's important that we do this AFTER using the depth doubler this frame to compensate for the fact that the front buffer is 1 frame behind the current view matrix
//otherwise we get a lag effect when the player changes their viewing angles
pRenderContext->GetMatrix( MATERIAL_VIEW, &m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
}
SetViewRecursionLevel( g_pPortalRender->GetViewRecursionLevel() - 1 );
}
render->PopView( pRenderContext, pViewRender->GetFrustum() );
//restore old frustum
memcpy( pViewRender->GetFrustum(), FrustumBackup, sizeof( Frustum ) );
render->OverrideViewFrustum( FrustumBackup );
}
shadowmgr->PopFlashlightScissorBounds();
pRenderContext->PopCustomClipPlane();
ms_clipPlaneStack.Pop(); // This pops my own clip plane
if( bReplaceOldPortalClipPlane )
{
pRenderContext->PushCustomClipPlane( vClipPlaneToRestore.Base() );
ms_clipPlaneStack.Push( vClipPlaneToRestore );
}
//restore old vis data
CopyToCurrentView( pViewRender, cameraView );
}
void CPortalRenderable_FlatBasic::RenderPortalViewToTexture( CViewRender *pViewRender, const CViewSetup &cameraView )
{
if( m_pLinkedPortal == NULL ) //not linked to any portal, so we'll be all static anyways
return;
float fPixelVisibilty = g_pPortalRender->GetPixelVisilityForPortalSurface( this );
if( (fPixelVisibilty >= 0.0f) && (fPixelVisibilty <= PORTALRENDERABLE_FLATBASIC_MINPIXELVIS) )
return;
ITexture *pRenderTarget;
if( m_bIsPortal2 )
pRenderTarget = portalrendertargets->GetPortal2Texture();
else
pRenderTarget = portalrendertargets->GetPortal1Texture();
// Require that we have render textures for drawing
AssertMsg( pRenderTarget, "Portal render targets not initialized properly" );
// We're about to dereference this, so just bail if we can't
if ( !pRenderTarget )
return;
CMatRenderContextPtr pRenderContext( materials );
Vector vCameraForward;
AngleVectors( cameraView.angles, &vCameraForward, NULL, NULL );
Frustum seeThroughFrustum;
bool bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
// Setup fog state for the camera.
Vector ptPOVOrigin = m_matrixThisToLinked * cameraView.origin;
Vector vPOVForward = m_matrixThisToLinked.ApplyRotation( vCameraForward );
Vector vCameraToPortal = m_ptOrigin - cameraView.origin;
CViewSetup portalView = cameraView;
Frustum frustumBackup;
memcpy( frustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
QAngle qPOVAngles = TransformAnglesToWorldSpace( cameraView.angles, m_matrixThisToLinked.As3x4() );
portalView.width = pRenderTarget->GetActualWidth();
portalView.height = pRenderTarget->GetActualHeight();
portalView.x = 0;
portalView.y = 0;
portalView.origin = ptPOVOrigin;
portalView.angles = qPOVAngles;
portalView.fov = cameraView.fov;
portalView.m_bOrtho = false;
portalView.m_flAspectRatio = (float)cameraView.width / (float)cameraView.height; //use the screen aspect ratio, 0.0f doesn't work as advertised
//pRenderContext->Flush( false );
float fCustomClipPlane[4];
fCustomClipPlane[0] = m_pLinkedPortal->m_vForward.x;
fCustomClipPlane[1] = m_pLinkedPortal->m_vForward.y;
fCustomClipPlane[2] = m_pLinkedPortal->m_vForward.z;
fCustomClipPlane[3] = m_pLinkedPortal->m_vForward.Dot( m_pLinkedPortal->m_ptOrigin - (m_pLinkedPortal->m_vForward * 2.0f) ); //moving it back a smidge to eliminate visual artifacts for half-in objects
pRenderContext->PushCustomClipPlane( fCustomClipPlane );
{
render->Push3DView( pRenderContext, portalView, VIEW_CLEAR_DEPTH, pRenderTarget, pViewRender->GetFrustum() );
{
ViewCustomVisibility_t customVisibility;
m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
SetRemainingViewDepth( 0 );
SetViewRecursionLevel( 1 );
CPortalRenderable *pRenderingViewForPortalBackup = g_pPortalRender->GetCurrentViewEntryPortal();
CPortalRenderable *pRenderingViewExitPortalBackup = g_pPortalRender->GetCurrentViewExitPortal();
SetViewEntranceAndExitPortals( this, m_pLinkedPortal );
bool bDrew3dSkybox = false;
SkyboxVisibility_t nSkyboxVisible = SKYBOX_NOT_VISIBLE;
// if the 3d skybox world is drawn, then don't draw the normal skybox
int nClearFlags = 0;
Draw3dSkyboxworld_Portal( pViewRender, portalView, nClearFlags, bDrew3dSkybox, nSkyboxVisible, pRenderTarget );
if( bUseSeeThroughFrustum )
{
memcpy( pViewRender->GetFrustum(), seeThroughFrustum, sizeof( Frustum ) );
}
render->OverrideViewFrustum( pViewRender->GetFrustum() );
pRenderContext->EnableUserClipTransformOverride( false );
ViewDrawScene( pViewRender, bDrew3dSkybox, nSkyboxVisible, portalView, nClearFlags, (view_id_t)g_pPortalRender->GetCurrentViewId(), false, 0, &customVisibility );
SetViewEntranceAndExitPortals( pRenderingViewForPortalBackup, pRenderingViewExitPortalBackup );
SetRemainingViewDepth( 1 );
SetViewRecursionLevel( 0 );
memcpy( pViewRender->GetFrustum(), frustumBackup, sizeof( Frustum ) );
render->OverrideViewFrustum( pViewRender->GetFrustum() );
}
render->PopView( pRenderContext, pViewRender->GetFrustum() );
}
pRenderContext->PopCustomClipPlane();
//pRenderContext->Flush( false );
CopyToCurrentView( pViewRender, cameraView );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AddToVisAsExitPortal
// input - pViewRender: pointer to the CViewRender class used to render this scene.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void CPortalRenderable_FlatBasic::AddToVisAsExitPortal( ViewCustomVisibility_t *pCustomVisibility )
{
if ( !pCustomVisibility )
return;
// Add four corners of the portal to the renderer as visibility origins
for ( int i = 0; i < 4; ++i )
{
if( enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptCorners[i] ) != -1 )
pCustomVisibility->AddVisOrigin( m_InternallyMaintainedData.m_ptCorners[i] );
}
// Add a point forward from the center of the portal in case the corners are outside of the world
if( enginetrace->GetLeafContainingPoint( m_InternallyMaintainedData.m_ptForwardOrigin ) != -1 )
pCustomVisibility->AddVisOrigin( m_InternallyMaintainedData.m_ptForwardOrigin );
// Specify which leaf to use for area portal culling
pCustomVisibility->ForceVisOverride( m_InternallyMaintainedData.m_VisData );
pCustomVisibility->ForceViewLeaf( m_InternallyMaintainedData.m_iViewLeaf );
}
void CPortalRenderable_FlatBasic::DrawStencilMask( IMatRenderContext *pRenderContext )
{
DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
void CPortalRenderable_FlatBasic::DrawPostStencilFixes( IMatRenderContext *pRenderContext )
{
//fast clipping may have hosed depth, reset it
if ( !r_portal_fastpath.GetBool() )
{
// TODO: Figure out if I actually need to do this
pRenderContext->ClearBuffersObeyStencil( false, true );
}
//replace the fog we overwrote
//RenderFogQuad(); //technically the fog quad is incorrect for all types of fog. But offers no usefulness when the local fog is <= the remote fog
//replace depth
DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
DrawRenderFixMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
void CPortalRenderable_FlatBasic::DrawPortal( IMatRenderContext *pRenderContext )
{
//stencil-based rendering
if( g_pPortalRender->GetCurrentViewExitPortal() != this )
{
if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
&& (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
&& (g_pPortalRender->GetViewRecursionLevel() > 1) )
{
DrawDepthDoublerMesh( pRenderContext );
}
}
}
int CPortalRenderable_FlatBasic::BindPortalMaterial( IMatRenderContext *pRenderContext, int nPassIndex, bool *pAllowRingMeshOptimizationOut )
{
VPROF_BUDGET( __FUNCTION__, "FlatBasic::BindPortalMaterial" );
*pAllowRingMeshOptimizationOut = true;
//stencil-based rendering
if( g_pPortalRender->GetCurrentViewExitPortal() != this )
{
if( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration)
&& (g_pPortalRender->GetRemainingPortalViewDepth() == 0)
&& (g_pPortalRender->GetViewRecursionLevel() > 1)
&& (g_pPortalRender->GetCurrentViewEntryPortal() == this) )
{
if( CPortalRender::DepthDoublerPIPDisableCheck() )
return 0;
if ( g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler.IsValid() )
{
IMaterialVar *pVar = g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &g_pPortalRender->m_MaterialsAccess.m_nDepthDoubleViewMatrixVarCache );
if ( pVar != NULL )
{
pVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
}
}
pRenderContext->Bind( g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler, GetClientRenderable() );
*pAllowRingMeshOptimizationOut = false;
return 1;
}
}
return 0;
}
void CPortalRenderable_FlatBasic::DrawDepthDoublerMesh( IMatRenderContext *pRenderContext, float fForwardOffsetModifier )
{
if( CPortalRender::DepthDoublerPIPDisableCheck() )
return;
if ( g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler.IsValid() )
{
IMaterialVar *pVar = g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler->FindVarFast( "$alternateviewmatrix", &g_pPortalRender->m_MaterialsAccess.m_nDepthDoubleViewMatrixVarCache );
if ( pVar != NULL )
{
pVar->SetMatrixValue( m_InternallyMaintainedData.m_DepthDoublerTextureView[GET_ACTIVE_SPLITSCREEN_SLOT()] );
}
}
DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_PortalDepthDoubler, fForwardOffsetModifier );
}
bool CPortalRenderable_FlatBasic::ShouldUpdateDepthDoublerTexture( const CViewSetup &viewSetup )
{
return ( (m_InternallyMaintainedData.m_bUsableDepthDoublerConfiguration) && (m_pLinkedPortal != NULL) );
}
void CPortalRenderable_FlatBasic::GetToolRecordingState( KeyValues *msg )
{
if ( !ToolsEnabled() )
return;
VPROF_BUDGET( "CPortalRenderable_FlatBasic::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
BaseClass::GetToolRecordingState( msg );
C_Prop_Portal *pLinkedPortal = static_cast<C_Prop_Portal*>( m_pLinkedPortal );
static PortalRecordingState_t state;
state.m_nPortalId = static_cast<C_Prop_Portal*>( this )->index;
state.m_nLinkedPortalId = pLinkedPortal ? pLinkedPortal->index : -1;
state.m_fHalfWidth = m_fHalfWidth;
state.m_fHalfHeight = m_fHalfHeight;
state.m_bIsPortal2 = m_bIsPortal2;
state.m_portalType = "Flat Basic";
msg->SetPtr( "portal", &state );
RemoveEffects( EF_NOINTERP );
}
void CPortalRenderable_FlatBasic::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
{
int nLinkedPortalId = pKeyValues->GetInt( "linkedPortalId" );
m_pLinkedPortal = nLinkedPortalId >= 0 ? (CPortalRenderable_FlatBasic *)FindRecordedPortal( nLinkedPortalId ) : NULL;
if( m_pLinkedPortal )
{
m_pLinkedPortal->m_pLinkedPortal = this;
}
m_bIsPortal2 = pKeyValues->GetInt( "isPortal2" ) != 0;
matrix3x4_t *pMat = (matrix3x4_t*)pKeyValues->GetPtr( "portalToWorld" );
MatrixGetColumn( *pMat, 3, m_ptOrigin );
MatrixGetColumn( *pMat, 0, m_vForward );
MatrixGetColumn( *pMat, 1, m_vRight );
MatrixGetColumn( *pMat, 2, m_vUp );
m_vRight *= -1.0f;
SetHalfSizes( pKeyValues->GetFloat( "halfWidth", 0.0f ), pKeyValues->GetFloat( "halfHeight", 0.0f ) );
PortalMoved();
UTIL_Portal_ComputeMatrix( this, m_pLinkedPortal );
}
extern ConVar mat_wireframe;
static void DrawComplexPortalMesh_SubQuad( Vector &ptBottomLeft, Vector &vUp, Vector &vRight, float *fSubQuadRect, void *pBindEnt, const IMaterial *pMaterial, const VMatrix *pReplacementViewMatrixForTexCoords = NULL )
{
PortalMeshPoint_t Vertices[4];
Vertices[0].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[2]) + (vUp * fSubQuadRect[3]);
Vertices[0].texCoord.x = fSubQuadRect[2];
Vertices[0].texCoord.y = fSubQuadRect[3];
Vertices[1].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[2]) + (vUp * fSubQuadRect[1]);
Vertices[1].texCoord.x = fSubQuadRect[2];
Vertices[1].texCoord.y = fSubQuadRect[1];
Vertices[2].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[0]) + (vUp * fSubQuadRect[1]);
Vertices[2].texCoord.x = fSubQuadRect[0];
Vertices[2].texCoord.y = fSubQuadRect[1];
Vertices[3].vWorldSpacePosition = ptBottomLeft + (vRight * fSubQuadRect[0]) + (vUp * fSubQuadRect[3]);
Vertices[3].texCoord.x = fSubQuadRect[0];
Vertices[3].texCoord.y = fSubQuadRect[3];
Clip_And_Render_Convex_Polygon( Vertices, 4, pMaterial, pBindEnt );
}
#define PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS 8
#define PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS 6
void CPortalRenderable_FlatBasic::DrawComplexPortalMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial, float fForwardOffsetModifier ) //generates and draws the portal mesh (Needed for compatibility with fixed function rendering)
{
PortalMeshPoint_t BaseVertices[4];
Vector ptBottomLeft = m_ptOrigin + (m_vForward * (fForwardOffsetModifier)) - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
Vector vScaledUp = m_vUp * (2.0f * m_fHalfHeight);
Vector vScaledRight = m_vRight * (2.0f * m_fHalfWidth);
VMatrix matView;
pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
float fSubQuadRect[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
float fHeightBegin = 0.0f;
for( int i = 0; i != PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS; ++i )
{
float fHeightEnd = fHeightBegin + (1.0f / ((float)PORTAL_PROJECTION_MESH_SUBDIVIDE_HEIGHTCHUNKS));
fSubQuadRect[1] = fHeightBegin;
fSubQuadRect[3] = fHeightEnd;
float fWidthBegin = 0.0f;
for( int j = 0; j != PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS; ++j )
{
float fWidthEnd = fWidthBegin + (1.0f / ((float)PORTAL_PROJECTION_MESH_SUBDIVIDE_WIDTHCHUNKS));
fSubQuadRect[0] = fWidthBegin;
fSubQuadRect[2] = fWidthEnd;
DrawComplexPortalMesh_SubQuad( ptBottomLeft, vScaledUp, vScaledRight, fSubQuadRect, GetClientRenderable(), pMaterial );
fWidthBegin = fWidthEnd;
}
fHeightBegin = fHeightEnd;
}
//pRenderContext->Flush( false );
}
static void CreateRingMesh( CMeshBuilder &meshBuilder, int nSegs, float flHalfWidth, float flHalfHeight, float flInnerScale, int *pStartVertex, int *pIndexCountOut )
{
float vTangent[4] = { -1.0f, 0.0f, 0.0f, 1.0f };
for ( int i = 0; i < nSegs; i++ )
{
float flRadians = 2.0f * M_PI * float( i ) / float( nSegs );
Vector2D vUvOuter( sinf( flRadians), cosf( flRadians ) );
Vector vPosOuter( -vUvOuter.x * flHalfWidth, -vUvOuter.y * flHalfHeight, 0.0f );
Vector vPosInner( flInnerScale * vPosOuter );
Vector2D vUvInner( flInnerScale * vUvOuter );
vPosOuter *= 1.05f; // Scale factor that hides the poly edge on the portal ring effect
vUvOuter *= 1.05f;
vUvOuter = 0.5f * vUvOuter;
vUvOuter.x += 0.5f;
vUvOuter.y += 0.5f;
vUvInner = 0.5f * vUvInner;
vUvInner.x += 0.5f;
vUvInner.y += 0.5f;
meshBuilder.Position3fv( &vPosOuter.x );
meshBuilder.TexCoord2f( 0, vUvOuter.x, vUvOuter.y );
meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
meshBuilder.Color4f( 1.0f, 1.0f, 1.0f, 1.0f );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &vPosInner.x );
meshBuilder.TexCoord2f( 0, vUvInner.x, vUvInner.y );
meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
meshBuilder.Color4f( 1.0f, 1.0f, 1.0f, 1.0f );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
int nSegmentIndex = *pStartVertex + 2 * i;
int nNextSegmentIndex = *pStartVertex + 2 * ( ( i + 1 ) % nSegs );
meshBuilder.FastIndex( nSegmentIndex );
meshBuilder.FastIndex( nSegmentIndex + 1 );
meshBuilder.FastIndex( nNextSegmentIndex + 1);
meshBuilder.FastIndex( nSegmentIndex );
meshBuilder.FastIndex( nNextSegmentIndex + 1 );
meshBuilder.FastIndex( nNextSegmentIndex );
}
*pStartVertex += nSegs * 2;
*pIndexCountOut = nSegs * 6;
}
inline float ComputePointToPortalDistance( const Vector &vPt, const CPortalRenderable_FlatBasic *pPortal, const Vector &vAdjustedOrigin )
{
// Transform point into portal space
matrix3x4_t matPortalToWorld( pPortal->m_vRight, pPortal->m_vUp, pPortal->m_vForward, vAdjustedOrigin );
Vector vPtPortalSpace;
VectorITransform( vPt, matPortalToWorld, vPtPortalSpace );
// Compute distance from point to portal bounding rectangle
Vector vClamped;
vClamped.x = clamp( vPtPortalSpace.x, -pPortal->GetHalfWidth(), pPortal->GetHalfWidth() );
vClamped.y = clamp( vPtPortalSpace.y, -pPortal->GetHalfHeight(), pPortal->GetHalfHeight() );
vClamped.z = 0.0f;
return ( vPtPortalSpace - vClamped ).Length();
}
IMesh *CPortalRenderable_FlatBasic::CreateMeshForPortals( IMatRenderContext *pRenderContext, int nPortalCount, CPortalRenderable **ppPortals,
CUtlVector< ClampedPortalMeshRenderInfo_t > &clampedPortalMeshRenderInfos )
{
VPROF_BUDGET( "CreateMeshForPortals", "CreateMeshForPortals" );
VertexFormat_t unionVertexFmt = (VertexFormat_t)VERTEX_POSITION | VERTEX_COLOR | VERTEX_NORMAL | VERTEX_TEXCOORD0_2D | VERTEX_USERDATA_SIZE(4) |
VERTEX_TEXCOORD_SIZE( 0, 2 ) | VERTEX_TEXCOORD_SIZE( 1, 2 );
CMeshBuilder meshBuilder;
IMesh* pMesh = pRenderContext->GetDynamicMeshEx( unionVertexFmt );
const int nRingMeshSegmentCount = 12;
int nMaxTriangles = ( nPortalCount * 2 ) + ( nPortalCount * 10 ) + ( nRingMeshSegmentCount * 2 ); // One quad per portal, plus a near cap poly per portal worst case
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 3 * nMaxTriangles, 3 * nMaxTriangles );
Vector *pTmpVerts = (Vector*)stackalloc( nPortalCount * 4 * sizeof( Vector ) );
Vector4D *pGhostColors = (Vector4D*)stackalloc( nPortalCount * sizeof( Vector4D ) );
float *pMaxDecalOffsets = (float*)stackalloc( nPortalCount * sizeof( float ) );
Vector vecDeltaRight, vecDeltaUp;
float vTangent[4];
const Vector vCameraPos( view->GetViewSetup()->origin );
int nStartVertex = 0;
// Create basic quad meshes
for ( int i = 0; i < nPortalCount; i++ )
{
const CPortalRenderable_FlatBasic *pPortal = assert_cast< const CPortalRenderable_FlatBasic* >( ppPortals[i] );
Vector ptCenter = pPortal->m_ptOrigin;
// Math below implements this
// verts[0] = ptCenter + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
// verts[1] = ptCenter + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
// verts[2] = ptCenter - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
// verts[3] = ptCenter - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
Vector *pVerts = pTmpVerts + ( 4 * i );
VectorMultiply( pPortal->m_vRight, pPortal->m_fHalfWidth, vecDeltaRight );
VectorMultiply( pPortal->m_vUp, pPortal->m_fHalfHeight, vecDeltaUp );
VectorSubtract( ptCenter, vecDeltaUp, pVerts[0] );
vecDeltaUp *= 2.0f;
pVerts[0] += vecDeltaRight;
vecDeltaRight *= 2.0f;
VectorAdd( pVerts[0], vecDeltaUp, pVerts[1] );
VectorSubtract( pVerts[0], vecDeltaRight, pVerts[2] );
VectorSubtract( pVerts[1], vecDeltaRight, pVerts[3] );
// Compute max decal offset. We don't want the decal to be offset past the camera position
Vector vDir( vCameraPos - pPortal->m_ptOrigin );
float flCameraDistToPortalPlane = DotProduct( vDir, pPortal->m_vForward );
pMaxDecalOffsets[i] = clamp( flCameraDistToPortalPlane, 0.02f, 0.251f ) - 0.01f;
Color clrPortal = UTIL_Portal_Color( pPortal->m_bIsPortal2 ? 2 : 1, pPortal->GetTeamNumber() );
pGhostColors[i].x = float( clrPortal.r() ) / 255.0f;
pGhostColors[i].y = float( clrPortal.g() ) / 255.0f;
pGhostColors[i].z = float( clrPortal.b() ) / 255.0f;
pGhostColors[i].w = pPortal->GetPortalGhostAlpha();
vTangent[0] = -pPortal->m_vRight.x;
vTangent[1] = -pPortal->m_vRight.y;
vTangent[2] = -pPortal->m_vRight.z;
vTangent[3] = 1.0f;
meshBuilder.Position3fv( &pVerts[0].x );
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
meshBuilder.Color4fv( &pGhostColors[i].x );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &pVerts[1].x );
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
meshBuilder.Color4fv( &pGhostColors[i].x );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &pVerts[3].x );
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
meshBuilder.Color4fv( &pGhostColors[i].x );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &pVerts[2].x );
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
meshBuilder.TexCoord2f( 1, pMaxDecalOffsets[i], 0.0f );
meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
meshBuilder.Color4fv( &pGhostColors[i].x );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.FastQuad( nStartVertex );
nStartVertex += 4;
}
PortalMeshPoint_t portalQuad[16];
int nInsideVertCount = 0;
PortalMeshPoint_t insidePoly[16];
// Make a frustum enclosing the pyramid between the camera position and the near plane.
VPlane nearPlaneFrustum[5];
V_memcpy( nearPlaneFrustum, view->GetFrustum(), 5 * sizeof( VPlane ) );
// flip near plane
nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal;
nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist;
clampedPortalMeshRenderInfos.SetCount( nPortalCount );
int nStartIndex = 6 * nPortalCount;
// Compute inverse view-projectio matrix
VMatrix matView;
VMatrix matProj;
pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
VMatrix matViewProj;
MatrixMultiply( matProj, matView, matViewProj );
VMatrix matInvViewProj;
MatrixInverseGeneral( matViewProj, matInvViewProj );
// Create meshes with a z-near-cap for portals that intersect the near plane
for ( int i = 0; i < nPortalCount; i++ )
{
const CPortalRenderable_FlatBasic *pPortal = assert_cast< const CPortalRenderable_FlatBasic* >( ppPortals[i] );
clampedPortalMeshRenderInfos[i].nStartIndex = -1;
clampedPortalMeshRenderInfos[i].nIndexCount = -1;
if ( R_CullSphere( nearPlaneFrustum, 5, &pPortal->m_ptOrigin, MAX( pPortal->m_fHalfWidth, pPortal->m_fHalfHeight ) ) )
{
// Portal definitely doesn't need a near plane cap
continue;
}
static const float CLIP_NEARPLANE_OFFSET = 0.3f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
static const float PROJECT_NEARPLANE_OFFSET = 0.01f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
static const float PORTAL_DISTANCE_EPSILON = 0.4f;
float flTweakedDecalOffset = pMaxDecalOffsets[i] + 0.05f;
float flDistToPortal = ComputePointToPortalDistance( vCameraPos, pPortal, pPortal->m_ptOrigin + flTweakedDecalOffset * pPortal->m_vForward );
if ( flDistToPortal < PORTAL_DISTANCE_EPSILON )
{
// We're too close to the portal to perform reliable clipping and reprojection to the near plane (the portal plane basically goes through the eye position.
// Instead, we clip the camera near plane by the portal plane and use the result as our near cap poly.
// Init portal quad with near plane. Offset a tiny bit from 1.0 to avoid precision errors that can leave a 1-pixel border around the screen uncovered by the quad.
matInvViewProj.V3Mul( Vector( -1.01f, -1.01f, 0.0f ), portalQuad[0].vWorldSpacePosition );
matInvViewProj.V3Mul( Vector( -1.01f, 1.01f, 0.0f ), portalQuad[1].vWorldSpacePosition );
matInvViewProj.V3Mul( Vector( 1.01f, 1.01f, 0.0f ), portalQuad[2].vWorldSpacePosition );
matInvViewProj.V3Mul( Vector( 1.01f, -1.01f, 0.0f ), portalQuad[3].vWorldSpacePosition );
portalQuad[0].texCoord.Init( 0.5f, 0.5f );
portalQuad[1].texCoord.Init( 0.5f, 0.5f );
portalQuad[2].texCoord.Init( 0.5f, 0.5f );
portalQuad[3].texCoord.Init( 0.5f, 0.5f );
// Clip against portal plane
float flPortalPlaneDist = DotProduct( pPortal->m_vForward, pPortal->m_ptOrigin + flTweakedDecalOffset * pPortal->m_vForward );
ClipPortalPolyToPlane( portalQuad, 4, insidePoly, &nInsideVertCount, -pPortal->m_vForward, -flPortalPlaneDist );
}
else
{
// Clip portal quad against view frustum.
portalQuad[0].vWorldSpacePosition = pTmpVerts[ 4 * i ] + flTweakedDecalOffset * pPortal->m_vForward;
portalQuad[1].vWorldSpacePosition = pTmpVerts[ 4 * i + 1 ] + flTweakedDecalOffset * pPortal->m_vForward;
portalQuad[2].vWorldSpacePosition = pTmpVerts[ 4 * i + 3 ] + flTweakedDecalOffset * pPortal->m_vForward;
portalQuad[3].vWorldSpacePosition = pTmpVerts[ 4 * i + 2 ] + flTweakedDecalOffset * pPortal->m_vForward;
portalQuad[0].texCoord.Init( 0.5f, 0.5f );
portalQuad[1].texCoord.Init( 0.5f, 0.5f );
portalQuad[2].texCoord.Init( 0.5f, 0.5f );
portalQuad[3].texCoord.Init( 0.5f, 0.5f );
/*
portalQuad[0].texCoord.Init( 0.0f, 1.0f );
portalQuad[1].texCoord.Init( 0.0f, 0.0f );
portalQuad[2].texCoord.Init( 1.0f, 0.0f );
portalQuad[3].texCoord.Init( 1.0f, 1.0f );
*/
// Clip against flipped near plane so that we get the part of the portal quad that would be clipped by the near plane. Offset the near plane so that our cap poly will cover up
// the clip intersection line.
bool bClippedSomething = ClipPortalPolyToPlane( portalQuad, 4, insidePoly, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal,
nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - CLIP_NEARPLANE_OFFSET );
if ( !bClippedSomething )
{
continue;
}
// Clip against the four remaining frustum planes. Offset the planes a tiny bit so that we have some slack when reprojecting the clipped verts back onto the near plane.
// This hack is only acceptable because we're not passing the proper texture coordinates for the quad anyways.
ClipPortalPolyToPlane( insidePoly, nInsideVertCount, portalQuad, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Normal, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Dist - 0.01f );
ClipPortalPolyToPlane( portalQuad, nInsideVertCount, insidePoly, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Normal, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Dist - 0.01f );
ClipPortalPolyToPlane( insidePoly, nInsideVertCount, portalQuad, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_TOP ].m_Normal, nearPlaneFrustum[ FRUSTUM_TOP ].m_Dist - 0.01f );
ClipPortalPolyToPlane( portalQuad, nInsideVertCount, insidePoly, &nInsideVertCount, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Normal, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Dist - 0.01f );
}
if ( nInsideVertCount < 3 )
{
continue;
}
ProjectPortalPolyToPlane( insidePoly, nInsideVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - PROJECT_NEARPLANE_OFFSET, vCameraPos );
vTangent[0] = -pPortal->m_vRight.x;
vTangent[1] = -pPortal->m_vRight.y;
vTangent[2] = -pPortal->m_vRight.z;
vTangent[3] = 1.0f;
nInsideVertCount = ( nInsideVertCount < 3 ) ? 0 : nInsideVertCount;
int nInsideTriCount = MAX( nInsideVertCount - 2, 0 );
clampedPortalMeshRenderInfos[i].nStartIndex = nStartIndex;
clampedPortalMeshRenderInfos[i].nIndexCount = nInsideTriCount * 3;
for ( int j = 0; j < nInsideVertCount; j++ )
{
meshBuilder.Position3fv( &( insidePoly[j].vWorldSpacePosition.x ) );
meshBuilder.TexCoord2fv( 0, &( insidePoly[j].texCoord.x ) );
meshBuilder.TexCoord2f( 1, 0.0f, 0.0f ); // No decal offset for near plane cap poly
meshBuilder.Normal3fv( &( pPortal->m_vForward.x ) );
meshBuilder.Color4fv( &pGhostColors[i].x );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
}
for ( int j = 0; j < nInsideTriCount; j++ )
{
meshBuilder.FastIndex( nStartVertex );
meshBuilder.FastIndex( nStartVertex + j + 1 );
meshBuilder.FastIndex( nStartVertex + j + 2 );
}
nStartVertex += nInsideVertCount;
nStartIndex += clampedPortalMeshRenderInfos[i].nIndexCount;
}
int nRingMeshIndexCount = 0;
CreateRingMesh( meshBuilder, nRingMeshSegmentCount, 32.0f, 56.0f, 0.65f, &nStartVertex, &nRingMeshIndexCount );
clampedPortalMeshRenderInfos.AddToTail();
clampedPortalMeshRenderInfos.Tail().nStartIndex = nStartIndex;
clampedPortalMeshRenderInfos.Tail().nIndexCount = nRingMeshIndexCount;
meshBuilder.End();
return pMesh;
}
bool CPortalRenderable_FlatBasic::ComputeClipSpacePortalCorners( Vector4D *pClipSpacePortalCornersOut, const VMatrix &matViewProj ) const
{
// Math below implements this
// verts[0] = ptCenter + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
// verts[1] = ptCenter + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
// verts[2] = ptCenter - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
// verts[3] = ptCenter - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
Vector4D verts[4];
Vector vecDeltaRight, vecDeltaUp;
VectorMultiply( m_vRight, m_fHalfWidth, vecDeltaRight );
VectorMultiply( m_vUp, m_fHalfHeight, vecDeltaUp );
VectorSubtract( m_ptOrigin.Base(), vecDeltaUp.Base(), verts[0].Base() );
vecDeltaUp *= 2.0f;
VectorAdd( verts[0].Base(), vecDeltaRight.Base(), verts[0].Base() );
vecDeltaRight *= 2.0f;
VectorAdd( verts[0].Base(), vecDeltaUp.Base(), verts[1].Base() );
VectorSubtract( verts[0].Base(), vecDeltaRight.Base(), verts[2].Base() );
VectorSubtract( verts[1].Base(), vecDeltaRight.Base(), verts[3].Base() );
bool bClipsNearPlane = false;
for ( int i = 0; i < 4; i++ )
{
verts[i].w = 1.0f;
matViewProj.V4Mul( verts[i], pClipSpacePortalCornersOut[i] );
bClipsNearPlane |= ( pClipSpacePortalCornersOut[i].z <= 0.0f );
}
return !bClipsNearPlane;
}
void CPortalRenderable_FlatBasic::DrawSimplePortalMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial, float fForwardOffsetModifier, float flAlpha, const Vector *pVertexColor )
{
VPROF_BUDGET( "DrawSimplePortalMesh", "DrawSimplePortalMesh" );
pRenderContext->Bind( (IMaterial *)pMaterial, GetClientRenderable() );
// This can depend on the Bind command above, so keep this after!
UpdateFrontBufferTexturesForMaterial( (IMaterial *)pMaterial );
pRenderContext->MatrixMode( MATERIAL_MODEL ); //just in case
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
// Disable offset modifier, put it in shader code in portal and portal_refract shaders instead
// FIXME: Remove this from the code
fForwardOffsetModifier = 0;
Vector ptCenter = m_ptOrigin + (m_vForward * fForwardOffsetModifier);
// Math below implements this
// verts[0] = ptCenter + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
// verts[1] = ptCenter + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
// verts[2] = ptCenter - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
// verts[3] = ptCenter - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
Vector verts[4];
Vector vecDeltaRight, vecDeltaUp;
VectorMultiply( m_vRight, m_fHalfWidth, vecDeltaRight );
VectorMultiply( m_vUp, m_fHalfHeight, vecDeltaUp );
VectorSubtract( ptCenter, vecDeltaUp, verts[0] );
vecDeltaUp *= 2.0f;
verts[0] += vecDeltaRight;
vecDeltaRight *= 2.0f;
VectorAdd( verts[0], vecDeltaUp, verts[1] );
VectorSubtract( verts[0], vecDeltaRight, verts[2] );
VectorSubtract( verts[1], vecDeltaRight, verts[3] );
float pColor[4] = { 1.0f, 1.0f, 1.0f, flAlpha };
if( pVertexColor )
{
memcpy( pColor, pVertexColor, sizeof( float ) * 3 );
}
float vTangent[4] = { -m_vRight.x, -m_vRight.y, -m_vRight.z, 1.0f };
CMeshBuilder meshBuilder;
IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
meshBuilder.Position3fv( &verts[0].x );
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
meshBuilder.Color4fv( pColor );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &verts[1].x );
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
meshBuilder.Color4fv( pColor );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &verts[2].x );
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
meshBuilder.Color4fv( pColor );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &verts[3].x );
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.TexCoord2f( 1, 0.25f, 0.0f );
meshBuilder.Normal3f( m_vForward.x, m_vForward.y, m_vForward.z );
meshBuilder.Color4fv( pColor );
meshBuilder.UserData( vTangent );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
if( mat_wireframe.GetBool() )
{
pRenderContext->Bind( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_Wireframe, (CPortalRenderable*)this );
IMesh* pMesh = pRenderContext->GetDynamicMesh( false );
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
meshBuilder.Position3fv( &verts[0].x );
meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
meshBuilder.TexCoord2f( 1, 0.0f, 1.0f );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &verts[1].x );
meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &verts[2].x );
meshBuilder.TexCoord2f( 0, 1.0f, 1.0f );
meshBuilder.TexCoord2f( 1, 1.0f, 1.0f );
meshBuilder.AdvanceVertex();
meshBuilder.Position3fv( &verts[3].x );
meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
meshBuilder.TexCoord2f( 1, 1.0f, 0.0f );
meshBuilder.AdvanceVertex();
meshBuilder.End();
pMesh->Draw();
}
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
}
void CPortalRenderable_FlatBasic::DrawRenderFixMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial, float fFrontClipDistance )
{
if( g_pPortalRender->GetViewRecursionLevel() != 0 )
return; //a render fix should only ever be necessary in the primary view
Vector ptCameraOrigin = CurrentViewOrigin();
Vector vPortalCenterToCamera = ptCameraOrigin - m_ptOrigin;
if( (vPortalCenterToCamera.Dot( m_vForward ) < -1.0f) ) //camera coplanar (to 1.0 units) or in front of portal plane
return;
if( vPortalCenterToCamera.LengthSqr() < (m_fHalfHeight * m_fHalfHeight) ) //FIXME: Player closeness check might need reimplementation
{
float fOldDist = m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist;
float fCameraDist = m_vForward.Dot(ptCameraOrigin - m_ptOrigin);
if( fFrontClipDistance > fCameraDist ) //never clip further out than the camera, we can see into the garbage space of the portal view's texture
fFrontClipDistance = fCameraDist;
m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist -= fFrontClipDistance;
Internal_DrawRenderFixMesh( pRenderContext, pMaterial );
m_InternallyMaintainedData.m_BoundingPlanes[PORTALRENDERFIXMESH_OUTERBOUNDPLANES].m_Dist = fOldDist;
}
}
void CPortalRenderable_FlatBasic::Internal_DrawRenderFixMesh( IMatRenderContext *pRenderContext, const IMaterial *pMaterial )
{
PortalMeshPoint_t WorkVertices[12];
PortalMeshPoint_t clippedVerts[12];
int nClippedVertCount = 0;
pRenderContext->MatrixMode( MATERIAL_MODEL ); //just in case
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
// Compute inverse view-projection matrix
VMatrix matView;
VMatrix matProj;
pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
VMatrix matViewProj;
MatrixMultiply( matProj, matView, matViewProj );
VMatrix matInvViewProj;
MatrixInverseGeneral( matViewProj, matInvViewProj );
VPlane nearPlaneFrustum[5];
V_memcpy( nearPlaneFrustum, view->GetFrustum(), 5 * sizeof( VPlane ) );
// flip near plane
nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal;
nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist = -nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist;
static const float PORTAL_OFFSET = 0.275f;
static const float CLIP_NEARPLANE_OFFSET = 0.3f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
static const float PROJECT_NEARPLANE_OFFSET = 0.01f; // push near plane back a bit so that the near plane cap overlaps the clip seam of the portal quad on the actual near plane
static const float PORTAL_DISTANCE_EPSILON = 0.4f;
const Vector vCameraPos( view->GetViewSetup()->origin );
Vector vOffsetOrigin( m_ptOrigin + PORTAL_OFFSET * m_vForward );
float flDistToPortal = ComputePointToPortalDistance( vCameraPos, this, vOffsetOrigin );
if ( flDistToPortal < PORTAL_DISTANCE_EPSILON )
{
// We're too close to the portal to perform reliable clipping and reprojection to the near plane (the portal plane basically goes through the eye position.
// Instead, we clip the camera near plane by the portal plane and use the result as our near cap poly.
// Init portal quad with near plane. Offset a tiny bit from 1.0 to avoid precision errors that can leave a 1-pixel border around the screen uncovered by the quad.
matInvViewProj.V3Mul( Vector( -1.01f, -1.01f, 0.0f ), WorkVertices[0].vWorldSpacePosition );
matInvViewProj.V3Mul( Vector( 1.01f, -1.01f, 0.0f ), WorkVertices[1].vWorldSpacePosition );
matInvViewProj.V3Mul( Vector( 1.01f, 1.01f, 0.0f ), WorkVertices[2].vWorldSpacePosition );
matInvViewProj.V3Mul( Vector( -1.01f, 1.01f, 0.0f ), WorkVertices[3].vWorldSpacePosition );
WorkVertices[0].texCoord.Init( 0.5f, 0.5f );
WorkVertices[1].texCoord.Init( 0.5f, 0.5f );
WorkVertices[2].texCoord.Init( 0.5f, 0.5f );
WorkVertices[3].texCoord.Init( 0.5f, 0.5f );
// Clip against portal plane
float flPortalPlaneDist = DotProduct( m_vForward, vOffsetOrigin );
ClipPortalPolyToPlane( WorkVertices, 4, clippedVerts, &nClippedVertCount, -m_vForward, -flPortalPlaneDist );
}
else
{
// Clip portal quad against view frustum.
WorkVertices[0].vWorldSpacePosition = vOffsetOrigin - (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
WorkVertices[1].vWorldSpacePosition = vOffsetOrigin + (m_vRight * m_fHalfWidth) + (m_vUp * m_fHalfHeight);
WorkVertices[2].vWorldSpacePosition = vOffsetOrigin + (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
WorkVertices[3].vWorldSpacePosition = vOffsetOrigin - (m_vRight * m_fHalfWidth) - (m_vUp * m_fHalfHeight);
WorkVertices[0].texCoord.Init( 0.5f, 0.5f );
WorkVertices[1].texCoord.Init( 0.5f, 0.5f );
WorkVertices[2].texCoord.Init( 0.5f, 0.5f );
WorkVertices[3].texCoord.Init( 0.5f, 0.5f );
// Clip against flipped near plane so that we get the part of the portal quad that would be clipped by the near plane. Offset the near plane so that our cap poly will cover up
// the clip intersection line.
bool bClippedSomething = ClipPortalPolyToPlane( WorkVertices, 4, clippedVerts, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal,
nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - CLIP_NEARPLANE_OFFSET );
if ( !bClippedSomething )
{
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
return;
}
// Clip against the four remaining frustum planes. Offset the planes a tiny bit so that we have some slack when reprojecting the clipped verts back onto the near plane.
// This hack is only acceptable because we're not passing the proper texture coordinates for the quad anyways.
ClipPortalPolyToPlane( clippedVerts, nClippedVertCount, WorkVertices, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Normal, nearPlaneFrustum[ FRUSTUM_RIGHT ].m_Dist - 0.01f );
ClipPortalPolyToPlane( WorkVertices, nClippedVertCount, clippedVerts, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Normal, nearPlaneFrustum[ FRUSTUM_LEFT ].m_Dist - 0.01f );
ClipPortalPolyToPlane( clippedVerts, nClippedVertCount, WorkVertices, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_TOP ].m_Normal, nearPlaneFrustum[ FRUSTUM_TOP ].m_Dist - 0.01f );
ClipPortalPolyToPlane( WorkVertices, nClippedVertCount, clippedVerts, &nClippedVertCount, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Normal, nearPlaneFrustum[ FRUSTUM_BOTTOM ].m_Dist - 0.01f );
}
if ( nClippedVertCount < 3 )
{
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
return;
}
ProjectPortalPolyToPlane( clippedVerts, nClippedVertCount, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Normal, nearPlaneFrustum[ FRUSTUM_NEARZ ].m_Dist - PROJECT_NEARPLANE_OFFSET, vCameraPos );
pRenderContext->OverrideDepthEnable( true, true, false );
RenderPortalMeshConvexPolygon( clippedVerts, nClippedVertCount, pMaterial, this );
pRenderContext->OverrideDepthEnable( false, true, true );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
}
void CPortalRenderable_FlatBasic::ClipFixToBoundingAreaAndDraw( PortalMeshPoint_t *pVerts, const IMaterial *pMaterial )
{
PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( 4 * (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2) * 16 * sizeof( PortalMeshPoint_t ) ); //really only should need 4x points, but I'm paranoid
PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( 4 * (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 2) * 16 * sizeof( PortalMeshPoint_t ) );
PortalMeshPoint_t *pTempVerts;
memcpy( pInVerts, pVerts, sizeof( PortalMeshPoint_t ) * 4 );
int iVertCount = 4;
//clip by bounding area planes
{
for( int i = 0; i != (PORTALRENDERFIXMESH_OUTERBOUNDPLANES + 1); ++i )
{
if( iVertCount < 3 )
return; //nothing to draw
iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, m_InternallyMaintainedData.m_BoundingPlanes[i].m_Normal, m_InternallyMaintainedData.m_BoundingPlanes[i].m_Dist, 0.01f );
pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
}
if( iVertCount < 3 )
return; //nothing to draw
}
//clip by the viewing frustum
{
VPlane *pFrustum = view->GetFrustum();
for( int i = 0; i != FRUSTUM_NUMPLANES; ++i )
{
if( iVertCount < 3 )
return; //nothing to draw
iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, pFrustum[i].m_Normal, pFrustum[i].m_Dist, 0.01f );
pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
}
if( iVertCount < 3 )
return; //nothing to draw
}
CMatRenderContextPtr pRenderContext( materials );
//project the points so we can fudge the numbers a bit and move them to exactly 0.0f depth
{
VMatrix matProj, matView, matViewProj;
pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
MatrixMultiply( matProj, matView, matViewProj );
for( int i = 0; i != iVertCount; ++i )
{
float W, inverseW;
W = matViewProj.m[3][0] * pInVerts[i].vWorldSpacePosition.x;
W += matViewProj.m[3][1] * pInVerts[i].vWorldSpacePosition.y;
W += matViewProj.m[3][2] * pInVerts[i].vWorldSpacePosition.z;
W += matViewProj.m[3][3];
inverseW = 1.0f / W;
pInVerts[i].vWorldSpacePosition = matViewProj * pInVerts[i].vWorldSpacePosition;
pInVerts[i].vWorldSpacePosition *= inverseW;
pInVerts[i].vWorldSpacePosition.z = 0.00001f; //the primary reason we're projecting on the CPU to begin with
}
}
//render with identity transforms and clipping disabled
{
bool bClippingEnabled = pRenderContext->EnableClipping( false );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PushMatrix();
pRenderContext->LoadIdentity();
RenderPortalMeshConvexPolygon( pInVerts, iVertCount, pMaterial, this );
if( mat_wireframe.GetBool() )
RenderPortalMeshConvexPolygon( pInVerts, iVertCount, materials->FindMaterial( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS, false ), this );
pRenderContext->MatrixMode( MATERIAL_MODEL );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_VIEW );
pRenderContext->PopMatrix();
pRenderContext->MatrixMode( MATERIAL_PROJECTION );
pRenderContext->PopMatrix();
pRenderContext->EnableClipping( bClippingEnabled );
}
}
//-----------------------------------------------------------------------------
// renders a quad that simulates fog as an overlay for something else (most notably the hole we create for stencil mode portals)
//-----------------------------------------------------------------------------
void CPortalRenderable_FlatBasic::RenderFogQuad( void )
{
CMatRenderContextPtr pRenderContext( materials );
if( pRenderContext->GetFogMode() == MATERIAL_FOG_NONE )
return;
float fFogStart, fFogEnd;
pRenderContext->GetFogDistances( &fFogStart, &fFogEnd, NULL );
float vertexColor[4];
unsigned char fogColor[3];
pRenderContext->GetFogColor( fogColor );
float fFogMaxDensity = 1.0f; //pRenderContext->GetFogMaxDensity(); //function only exists in a pickled changelist for now
/*float fColorScale = LinearToGammaFullRange( pRenderContext->GetToneMappingScaleLinear().x );
fogColor[0] *= fColorScale;
fogColor[1] *= fColorScale;
fogColor[2] *= fColorScale;*/
vertexColor[0] = fogColor[0] * (1.0f / 255.0f);
vertexColor[1] = fogColor[1] * (1.0f / 255.0f);
vertexColor[2] = fogColor[2] * (1.0f / 255.0f);
float ooFogRange = 1.0f;
if ( fFogEnd != fFogStart )
{
ooFogRange = 1.0f / (fFogEnd - fFogStart);
}
float FogEndOverFogRange = ooFogRange * fFogEnd;
VMatrix matView, matViewProj, matProj;
pRenderContext->GetMatrix( MATERIAL_VIEW, &matView );
pRenderContext->GetMatrix( MATERIAL_PROJECTION, &matProj );
MatrixMultiply( matProj, matView, matViewProj );
Vector vUp = m_vUp * (m_fHalfHeight * 2.0f);
Vector vRight = m_vRight * (m_fHalfWidth * 2.0f);
Vector ptCorners[4];
ptCorners[0] = (m_ptOrigin + vUp) + vRight;
ptCorners[1] = (m_ptOrigin + vUp) - vRight;
ptCorners[2] = (m_ptOrigin - vUp) + vRight;
ptCorners[3] = (m_ptOrigin - vUp) - vRight;
pRenderContext->Bind( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_TranslucentVertexColor );
IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
CMeshBuilder meshBuilder;
meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
for( int i = 0; i != 4; ++i )
{
float projZ;
projZ = matViewProj.m[2][0] * ptCorners[i].x;
projZ += matViewProj.m[2][1] * ptCorners[i].y;
projZ += matViewProj.m[2][2] * ptCorners[i].z;
projZ += matViewProj.m[2][3];
float fFogAmount = ((-projZ * ooFogRange) + FogEndOverFogRange); //projZ should be negative
//range fix
if( fFogAmount >= fFogMaxDensity )
fFogAmount = fFogMaxDensity;
if( fFogAmount <= 0.0f )
fFogAmount = 0.0f;
vertexColor[3] = fFogMaxDensity - fFogAmount; //alpha is inverse fog
meshBuilder.Position3fv( &ptCorners[i].x );
meshBuilder.Color4fv( vertexColor );
meshBuilder.AdvanceVertex();
}
meshBuilder.End();
pMesh->Draw();
}
bool CPortalRenderable_FlatBasic::DoesExitViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
{
bool bAboveWater = false, bBelowWater = false;
for( int i = 0; i != 4; ++i )
{
bAboveWater |= (m_InternallyMaintainedData.m_ptCorners[i].z >= waterZ);
bBelowWater |= (m_InternallyMaintainedData.m_ptCorners[i].z <= waterZ);
}
if( !(bAboveWater && bBelowWater) )
{
RANDOM_CEG_TEST_SECRET();
return false;
}
Vector vMins, vMaxs;
vMins = vMaxs = m_InternallyMaintainedData.m_ptCorners[0];
for( int i = 1; i != 4; ++i )
{
vMins = VectorMin( vMins, m_InternallyMaintainedData.m_ptCorners[i] );
vMaxs = VectorMax( vMaxs, m_InternallyMaintainedData.m_ptCorners[i] );
}
return render->DoesBoxIntersectWaterVolume( vMins, vMaxs, leafWaterDataID );
}
float CPortalRenderable_FlatBasic::GetPortalDistanceBias() const
{
Vector vCameraToExitPortal = m_ptOrigin - CurrentViewOrigin();
float flDistModifier = vCameraToExitPortal.Length();
flDistModifier = MAX( flDistModifier, 0.01f );
// Increase the distance factor based on angle (so we drop detail more aggressively when viewed at an angle)
// Vector vNormalizedCameraToExitPortal = vCameraToExitPortal * ( 1.0f / flDistModifier );
// float flAngleModifier = vNormalizedCameraToExitPortal.Dot( m_vForward );
// flAngleModifier = clamp( flAngleModifier, 0.01f, 1.0f );
// flAngleModifier = sqrtf( flAngleModifier ); // Use a square root to reduce the multiplier effect
// flDistModifier /= flAngleModifier;
return flDistModifier;
}
bool CPortalRenderable_FlatBasic::ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, const CUtlVector<VPlane> &currentComplexFrustum )
{
if( m_pLinkedPortal == NULL )
return false;
Vector vCameraPos = currentView.origin;
if( (g_pPortalRender->GetViewRecursionLevel() == 0) &&
((m_ptOrigin - vCameraPos).LengthSqr() < (m_fHalfHeight * m_fHalfHeight)) ) //FIXME: Player closeness check might need reimplementation
{
return true; //fudgery time. The player might not be able to see the surface, but they can probably see the render fix
}
if( m_vForward.Dot( vCameraPos ) <= m_InternallyMaintainedData.m_fPlaneDist )
return false; //looking at portal backface
int iCurrentFrustmPlanes = currentComplexFrustum.Count();
//now slice up the portal quad and see if any is visible within the frustum
int allocSize = (6 + currentComplexFrustum.Count()); //possible to add 1 point per cut, 4 starting points, N plane cuts, 2 extra because I'm paranoid
Vector *pInVerts = (Vector *)stackalloc( sizeof( Vector ) * allocSize * 2 );
Vector *pOutVerts = pInVerts + allocSize;
Vector *pTempVerts;
//clip by first plane and put output into pInVerts
int iVertCount = ClipPolyToPlane( m_InternallyMaintainedData.m_ptCorners, 4, pInVerts, currentComplexFrustum[0].m_Normal, currentComplexFrustum[0].m_Dist, 0.01f );
//clip by other planes and flipflop in and out pointers
for( int i = 1; i != iCurrentFrustmPlanes; ++i )
{
if( iVertCount < 3 )
return false; //nothing left in the frustum
iVertCount = ClipPolyToPlane( pInVerts, iVertCount, pOutVerts, currentComplexFrustum[i].m_Normal, currentComplexFrustum[i].m_Dist, 0.01f );
pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
}
if( iVertCount < 3 )
return false; //nothing left in the frustum
return true;
}
bool CPortalRenderable_FlatBasic::ShouldUpdatePortalView_BasedOnPixelVisibility( float fScreenFilledByStencilMaskLastFrame_Normalized )
{
return (fScreenFilledByStencilMaskLastFrame_Normalized < 0.0f) || // < 0 is an error value
(fScreenFilledByStencilMaskLastFrame_Normalized > PORTALRENDERABLE_FLATBASIC_MINPIXELVIS );
}
CPortalRenderable *CreatePortal_FlatBasic_Fn( void )
{
return new CPortalRenderable_FlatBasic;
}
static CPortalRenderableCreator_AutoRegister CreatePortal_FlatBasic( "Flat Basic", CreatePortal_FlatBasic_Fn );