cstrike15_src/game/client/portal/c_prop_mirror.cpp
2025-06-04 03:22:50 +02:00

673 lines
25 KiB
C++

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
//NOTE: Mirrors with models require an attachment named "MirrorSurface_Attach" with x facing out of the mirror plane.
//They also require that the mirror surface be in a bodygroup by itself named "MirrorSurface" with the first index being the mirror, second being empty.
//Lastly, they require that all non-mirror geometry be in bodygroups that have the second entry as empty.
//It's a good idea to put a cubemap on the mirror surface material because they're not infinitely recursive
#include "cbase.h"
#include "portalrenderable_flatbasic.h"
#include "tier0/vprof.h"
#include "model_types.h"
#include "debugoverlay_shared.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
class C_Prop_Mirror : public CPortalRenderable_FlatBasic
{
public:
DECLARE_CLASS( C_Prop_Mirror, CPortalRenderable_FlatBasic );
DECLARE_CLIENTCLASS();
C_Prop_Mirror( void );
virtual void Spawn( void );
virtual void UpdateOnRemove( void );
virtual void OnDataChanged( DataUpdateType_t type );
virtual bool ShouldDraw( void ) { return true; }
virtual int DrawModel( int flags, const RenderableInstance_t &instance );
virtual CStudioHdr *OnNewModel( void );
virtual void ClientThink( void );
virtual bool ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, const CUtlVector<VPlane> &currentComplexFrustum );
virtual bool ShouldUpdatePortalView_BasedOnPixelVisibility( float fScreenFilledByStencilMaskLastFrame_Normalized ) { return true; }
void UpdateReflectionPlane( void );
void UpdateReflectionPolygon( void ); //call this before accessing m_LocalSpaceReflectionPolygonVerts, it'll detect if an update is necessary and do it
virtual void DrawStencilMask( IMatRenderContext *pRenderContext );
virtual void DrawPostStencilFixes( IMatRenderContext *pRenderContext );
virtual void RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView );
bool CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum );
static CMaterialReference sm_Mirror_Stencil;
float m_fWidth;
float m_fHeight;
int m_iMirrorSurfaceBodyGroup;
int m_iMirrorFaceAttachment;
bool m_bModel;
Vector m_LocalSpaceReflectionPolygonVerts[10]; //best guess at the reflection polygon by intersecting the reflection plane with the local space rendering OBB
int m_LocalSpaceReflectionPolygonVertCount;
struct ReflectPlaneCachedData_t
{
//Vector vAttachmentOrigin; //handled by m_ptOrigin
QAngle qAttachmentAngle;
bool bModel;
Vector vLocalSpaceAttachmentOrigin;
QAngle qLocalSpaceAttachmentAngles;
Vector vRenderOBB_Mins;
Vector vRenderOBB_Maxs;
};
ReflectPlaneCachedData_t m_CachedReflectedData;
};
CMaterialReference C_Prop_Mirror::sm_Mirror_Stencil;
IMPLEMENT_CLIENTCLASS_DT( C_Prop_Mirror, DT_Prop_Mirror, CProp_Mirror )
RecvPropFloat( RECVINFO(m_fWidth) ),
RecvPropFloat( RECVINFO(m_fHeight) ),
END_RECV_TABLE()
C_Prop_Mirror::C_Prop_Mirror( void )
{
m_ptOrigin.Invalidate();
m_CachedReflectedData.qAttachmentAngle.Invalidate();
m_CachedReflectedData.vLocalSpaceAttachmentOrigin.Invalidate();
m_CachedReflectedData.qLocalSpaceAttachmentAngles.Invalidate();
m_CachedReflectedData.vRenderOBB_Maxs.Invalidate();
m_matrixThisToLinked.m[3][0] = 0.0f;
m_matrixThisToLinked.m[3][1] = 0.0f;
m_matrixThisToLinked.m[3][2] = 0.0f;
m_matrixThisToLinked.m[3][3] = 1.0f;
}
void C_Prop_Mirror::Spawn( void )
{
BaseClass::Spawn();
if( !sm_Mirror_Stencil.IsValid() )
{
sm_Mirror_Stencil.Init( "decals/portalstencildecal", TEXTURE_GROUP_CLIENT_EFFECTS );
}
m_pLinkedPortal = this;
g_pPortalRender->AddPortal( this ); //will know if we're already added and avoid adding twice
}
void C_Prop_Mirror::UpdateOnRemove( void )
{
BaseClass::UpdateOnRemove();
g_pPortalRender->RemovePortal( this );
}
CStudioHdr *C_Prop_Mirror::OnNewModel( void )
{
CStudioHdr *pRetVal = BaseClass::OnNewModel();
if( (GetModel() != NULL) )
{
m_iMirrorSurfaceBodyGroup = FindBodygroupByName( "MirrorSurface" );
m_iMirrorFaceAttachment = LookupAttachment( "MirrorSurface_Attach" );
if( (m_iMirrorSurfaceBodyGroup >= 0) && (m_iMirrorFaceAttachment >= 0) )
{
m_bModel = true;
SetBodygroup( m_iMirrorSurfaceBodyGroup, 1 ); //by default the mirror surface is hidden
}
else
{
Warning( "Prop_Mirror model missing vital data %s\n", (m_iMirrorFaceAttachment < 0) ?
((m_iMirrorSurfaceBodyGroup < 0) ? "MirrorSurface bodygroup and MirrorSurface_Attach attachment point" : "MirrorSurface_Attach attachment point") :
"MirrorSurface bodygroup" );
m_bModel = false;
}
}
if( m_bModel )
{
SetNextClientThink( CLIENT_THINK_ALWAYS );
}
else
{
SetNextClientThink( CLIENT_THINK_NEVER );
}
return pRetVal;
}
void C_Prop_Mirror::UpdateReflectionPlane( void )
{
PortalMoved();
Vector vToOrigin( m_ptOrigin.Dot( m_vForward ), m_ptOrigin.Dot( m_vRight ), -m_ptOrigin.Dot( m_vUp ) );
//generate mirroring matrix. Move mirror to origin using base vectors, flip on forward axis, move back to position and orientation
{
m_matrixThisToLinked.m[0][0] = (-m_vForward.x * m_vForward.x) + (m_vRight.x * m_vRight.x) + (m_vUp.x * m_vUp.x);
m_matrixThisToLinked.m[0][1] = (-m_vForward.x * m_vForward.y) + (m_vRight.x * m_vRight.y) + (m_vUp.x * m_vUp.y);
m_matrixThisToLinked.m[0][2] = (-m_vForward.x * m_vForward.z) + (m_vRight.x * m_vRight.z) + (m_vUp.x * m_vUp.z);
m_matrixThisToLinked.m[0][3] = (vToOrigin.x * m_vForward.x) - (vToOrigin.y * m_vRight.x) + (vToOrigin.z * m_vUp.x) + m_ptOrigin.x;
m_matrixThisToLinked.m[1][0] = m_matrixThisToLinked.m[0][1]; //rotation portion of the matrix is equal to it's own transpose
m_matrixThisToLinked.m[1][1] = (-m_vForward.y * m_vForward.y) + (m_vRight.y * m_vRight.y) + (m_vUp.y * m_vUp.y);
m_matrixThisToLinked.m[1][2] = (-m_vForward.y * m_vForward.z) + (m_vRight.y * m_vRight.z) + (m_vUp.y * m_vUp.z);
m_matrixThisToLinked.m[1][3] = (vToOrigin.x * m_vForward.y) - (vToOrigin.y * m_vRight.y) + (vToOrigin.z * m_vUp.y) + m_ptOrigin.y;
m_matrixThisToLinked.m[2][0] = m_matrixThisToLinked.m[0][2]; //rotation portion of the matrix is equal to it's own transpose
m_matrixThisToLinked.m[2][1] = m_matrixThisToLinked.m[1][2]; //rotation portion of the matrix is equal to it's own transpose
m_matrixThisToLinked.m[2][2] = (-m_vForward.z * m_vForward.z) + (m_vRight.z * m_vRight.z) + (m_vUp.z * m_vUp.z);
m_matrixThisToLinked.m[2][3] = (vToOrigin.x * m_vForward.z) - (vToOrigin.y * m_vRight.z) + (vToOrigin.z * m_vUp.z) + m_ptOrigin.z;
}
UpdateReflectionPolygon();
}
void C_Prop_Mirror::UpdateReflectionPolygon( void )
{
if( m_bModel != m_CachedReflectedData.bModel )
{
m_CachedReflectedData.qAttachmentAngle.Invalidate();
m_CachedReflectedData.vLocalSpaceAttachmentOrigin.Invalidate();
m_CachedReflectedData.qLocalSpaceAttachmentAngles.Invalidate();
m_CachedReflectedData.vRenderOBB_Maxs.Invalidate();
m_CachedReflectedData.bModel = m_bModel;
}
if( m_bModel )
{
Vector vMins, vMaxs;
GetRenderBounds( vMins, vMaxs );
Vector vLocalAttachmentOrigin;
QAngle qLocalAttachmentAngles;
GetAttachmentLocal( m_iMirrorFaceAttachment, vLocalAttachmentOrigin, qLocalAttachmentAngles );
if( (vMins == m_CachedReflectedData.vRenderOBB_Mins) && (vMaxs == m_CachedReflectedData.vRenderOBB_Maxs) &&
(vLocalAttachmentOrigin == m_CachedReflectedData.vLocalSpaceAttachmentOrigin) && (qLocalAttachmentAngles == m_CachedReflectedData.qLocalSpaceAttachmentAngles) )
{
return; //nothing to update
}
m_CachedReflectedData.vRenderOBB_Mins = vMins;
m_CachedReflectedData.vRenderOBB_Maxs = vMaxs;
m_CachedReflectedData.vLocalSpaceAttachmentOrigin = vLocalAttachmentOrigin;
m_CachedReflectedData.qLocalSpaceAttachmentAngles = qLocalAttachmentAngles;
Vector vAttachmentVectors[3];
AngleVectors( qLocalAttachmentAngles, &vAttachmentVectors[0], &vAttachmentVectors[1], &vAttachmentVectors[2] );
float fLargestOBBDiff = vMaxs.x - vMins.x;
for( int i = 1; i != 3; ++i )
{
float fDiff = vMaxs[i] - vMins[i];
if( fDiff > fLargestOBBDiff )
{
fLargestOBBDiff = fDiff;
}
}
fLargestOBBDiff *= 4.0f; //to easily cover diagonal intersection and then some
Vector vClipBuffers[2][10]; //4 starting points, possible to create 1 extra point per cut, 6 cuts
vClipBuffers[0][0] = vLocalAttachmentOrigin + (vAttachmentVectors[1] * fLargestOBBDiff) + (vAttachmentVectors[2] * fLargestOBBDiff);
vClipBuffers[0][1] = vLocalAttachmentOrigin - (vAttachmentVectors[1] * fLargestOBBDiff) + (vAttachmentVectors[2] * fLargestOBBDiff);
vClipBuffers[0][2] = vLocalAttachmentOrigin - (vAttachmentVectors[1] * fLargestOBBDiff) - (vAttachmentVectors[2] * fLargestOBBDiff);
vClipBuffers[0][3] = vLocalAttachmentOrigin + (vAttachmentVectors[1] * fLargestOBBDiff) - (vAttachmentVectors[2] * fLargestOBBDiff);
int iVertCount = 4;
VPlane vClipPlanes[6];
vClipPlanes[0].Init( Vector( 1.0f, 0.0f, 0.0f ), vMins.x );
vClipPlanes[1].Init( Vector( -1.0f, 0.0f, 0.0f ), -vMaxs.x );
vClipPlanes[2].Init( Vector( 0.0f, 1.0f, 0.0f ), vMins.y );
vClipPlanes[3].Init( Vector( 0.0f, -1.0f, 0.0f ), -vMaxs.y );
vClipPlanes[4].Init( Vector( 0.0f, 0.0f, 1.0f ), vMins.z );
vClipPlanes[5].Init( Vector( 0.0f, 0.0f, -1.0f ), -vMaxs.z );
for( int i = 0; i != 6; ++i )
{
iVertCount = ClipPolyToPlane( vClipBuffers[i & 1], iVertCount, vClipBuffers[(i & 1) ^ 1], vClipPlanes[i].m_Normal, vClipPlanes[i].m_Dist, 0.01f );
}
Assert( iVertCount >= 3 );
m_LocalSpaceReflectionPolygonVertCount = iVertCount;
memcpy( m_LocalSpaceReflectionPolygonVerts, vClipBuffers[0], sizeof( Vector ) * iVertCount );
}
else
{
if( (m_CachedReflectedData.vRenderOBB_Maxs.x == m_fWidth) && (m_CachedReflectedData.vRenderOBB_Maxs.y == m_fHeight) )
return;
m_LocalSpaceReflectionPolygonVertCount = 4;
float fHalfWidth = GetHalfWidth();
float fHalfHeight = GetHalfHeight();
m_LocalSpaceReflectionPolygonVerts[0].Init( 0.0f, fHalfWidth, fHalfHeight );
m_LocalSpaceReflectionPolygonVerts[1].Init( 0.0f, -fHalfWidth, fHalfHeight );
m_LocalSpaceReflectionPolygonVerts[2].Init( 0.0f, -fHalfWidth, -fHalfHeight );
m_LocalSpaceReflectionPolygonVerts[3].Init( 0.0f, fHalfWidth, -fHalfHeight );
}
}
void C_Prop_Mirror::OnDataChanged( DataUpdateType_t type )
{
BaseClass::OnDataChanged( type );
SetHalfSizes( m_fWidth / 2.0f, m_fHeight / 2.0f );
if( m_bModel )
{
Vector vMirrorAttachmentOrigin;
QAngle qMirrorAttachmentAngles;
GetAttachment( m_iMirrorFaceAttachment, vMirrorAttachmentOrigin, qMirrorAttachmentAngles );
if( (m_ptOrigin != vMirrorAttachmentOrigin) || (m_CachedReflectedData.qAttachmentAngle != qMirrorAttachmentAngles) )
{
m_ptOrigin = vMirrorAttachmentOrigin;
m_CachedReflectedData.qAttachmentAngle = qMirrorAttachmentAngles;
AngleVectors( qMirrorAttachmentAngles, &m_vForward, &m_vRight, &m_vUp );
UpdateReflectionPlane();
}
}
else
{
if( (m_ptOrigin != GetRenderOrigin()) || (m_CachedReflectedData.qAttachmentAngle != GetRenderAngles()) )
{
m_ptOrigin = GetRenderOrigin();
m_CachedReflectedData.qAttachmentAngle = GetRenderAngles();
AngleVectors( GetRenderAngles(), &m_vForward, &m_vRight, &m_vUp );
UpdateReflectionPlane();
}
}
}
void C_Prop_Mirror::ClientThink( void )
{
BaseClass::ClientThink();
if( m_bModel )
{
Vector vMirrorAttachmentOrigin;
QAngle qMirrorAttachmentAngles;
GetAttachment( m_iMirrorFaceAttachment, vMirrorAttachmentOrigin, qMirrorAttachmentAngles );
if( (m_ptOrigin != vMirrorAttachmentOrigin) || (m_CachedReflectedData.qAttachmentAngle != qMirrorAttachmentAngles) )
{
m_ptOrigin = vMirrorAttachmentOrigin;
AngleVectors( qMirrorAttachmentAngles, &m_vForward, &m_vRight, &m_vUp );
UpdateReflectionPlane();
}
}
}
void C_Prop_Mirror::DrawStencilMask( IMatRenderContext *pRenderContext )
{
if( m_bModel )
{
int iNumBodyGroups = GetNumBodyGroups();
int *pOldBodyGroups = (int *)stackalloc( sizeof( int ) * iNumBodyGroups );
for( int i = 0; i != iNumBodyGroups; ++i )
{
pOldBodyGroups[i] = GetBodygroup( i );
SetBodygroup( i, 1 );
}
pOldBodyGroups[m_iMirrorSurfaceBodyGroup] = 1;
SetBodygroup( m_iMirrorSurfaceBodyGroup, 0 );
RenderableInstance_t tempInstance;
tempInstance.m_nAlpha = 255;
modelrender->ForcedMaterialOverride( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
DrawModel( STUDIO_RENDER, tempInstance );
modelrender->ForcedMaterialOverride( NULL );
for( int i = 0; i != iNumBodyGroups; ++i )
{
SetBodygroup( i, pOldBodyGroups[i] );
}
}
else
{
DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
}
void C_Prop_Mirror::DrawPostStencilFixes( IMatRenderContext *pRenderContext )
{
if( m_bModel )
{
int iNumBodyGroups = GetNumBodyGroups();
int *pOldBodyGroups = (int *)stackalloc( sizeof( int ) * iNumBodyGroups );
for( int i = 0; i != iNumBodyGroups; ++i )
{
pOldBodyGroups[i] = GetBodygroup( i );
SetBodygroup( i, 1 );
}
pOldBodyGroups[m_iMirrorSurfaceBodyGroup] = 1;
SetBodygroup( m_iMirrorSurfaceBodyGroup, 0 );
RenderableInstance_t tempInstance;
tempInstance.m_nAlpha = 255;
modelrender->ForcedMaterialOverride( (IMaterial *)(const IMaterial *)g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
DrawModel( STUDIO_RENDER, tempInstance );
modelrender->ForcedMaterialOverride( NULL );
for( int i = 0; i != iNumBodyGroups; ++i )
{
SetBodygroup( i, pOldBodyGroups[i] );
}
}
else
{
DrawSimplePortalMesh( pRenderContext, g_pPortalRender->m_MaterialsAccess.m_WriteZ_Model );
}
}
bool C_Prop_Mirror::ShouldUpdatePortalView_BasedOnView( const CViewSetup &currentView, const CUtlVector<VPlane> &currentComplexFrustum )
{
if( m_vForward.Dot( currentView.origin ) <= m_vForward.Dot( m_ptOrigin ) )
return false; //camera is behind the mirror plane
UpdateReflectionPolygon();
matrix3x4_t matRenderTransform;
AngleMatrix( GetRenderAngles(), GetRenderOrigin(), matRenderTransform );
Vector *pClipBuffers[2];
int clipAllocSize = (m_LocalSpaceReflectionPolygonVertCount + currentComplexFrustum.Count() + 2); //possible to add 1 point per cut, m_LocalSpaceReflectionPolygonVertCount starting points, currentComplexFrustum.Count() plane cuts, 2 extra because I'm paranoid
pClipBuffers[0] = (Vector *)stackalloc( sizeof( Vector ) * clipAllocSize * 2 );
pClipBuffers[1] = pClipBuffers[0] + clipAllocSize;
for( int i = 0; i != m_LocalSpaceReflectionPolygonVertCount; ++i )
{
VectorTransform( m_LocalSpaceReflectionPolygonVerts[i], matRenderTransform, pClipBuffers[0][i] );
}
const VPlane *currentFrustum = currentComplexFrustum.Base();
int iCurrentFrustumPlanes = currentComplexFrustum.Count();
//clip by first plane and put output into pInVerts
int iVertCount = ClipPolyToPlane( pClipBuffers[0], m_LocalSpaceReflectionPolygonVertCount, pClipBuffers[1], currentFrustum[0].m_Normal, currentFrustum[0].m_Dist, 0.01f );
for( int i = 1; i != iCurrentFrustumPlanes; ++i )
{
if( iVertCount < 3 )
return false; //nothing left in the frustum
iVertCount = ClipPolyToPlane( pClipBuffers[i&1], iVertCount, pClipBuffers[(i&1)^1], currentFrustum[i].m_Normal, currentFrustum[i].m_Dist, 0.01f );
}
#if 0 //for visibility culling debugging
int iFinalBuffer = (iCurrentFrustumPlanes & 1) ^ 1;
if( g_pPortalRender->GetViewRecursionLevel() == 0 )
{
NDebugOverlay::Line( pClipBuffers[iFinalBuffer][iVertCount - 1], pClipBuffers[iFinalBuffer][0], 255, 0, 0, false, 0.0f );
for( int j = 0; j != iVertCount - 1; ++j )
{
NDebugOverlay::Line( pClipBuffers[iFinalBuffer][j], pClipBuffers[iFinalBuffer][j+1], 255, 0, 0, false, 0.0f );
}
}
#endif
return (iVertCount >= 3);
#if 0
if( m_bModel )
{
//it would be complicated to get the exact shape of the mirror surface and cut it up, so we'll just use the model's oriented bounding box.
//which can provide up to 3 visible quads, if any 1 of them passes the test then the entire thing is visible.
if( m_vForward.Dot( currentView.origin ) <= m_vForward.Dot( m_ptOrigin ) )
return false; //camera is behind the mirror plane
Vector vMins, vMaxs;
GetRenderBounds( vMins, vMaxs );
Vector vRenderOrigin = GetRenderOrigin();
Vector vOBBVectors[3];
#if 1
AngleVectors( GetRenderAngles(), &vOBBVectors[0], &vOBBVectors[1], &vOBBVectors[2] );
vOBBVectors[1] = -vOBBVectors[1];
#else
{
matrix3x4_t fRotateMatrix;
AngleMatrix( GetRenderAngles(), fRotateMatrix );
VectorRotate( Vector( 1.0f, 0.0f, 0.0f ), fRotateMatrix, vOBBVectors[0] );
VectorRotate( Vector( 0.0f, 1.0f, 0.0f ), fRotateMatrix, vOBBVectors[1] );
VectorRotate( Vector( 0.0f, 0.0f, 1.0f ), fRotateMatrix, vOBBVectors[2] );
}
#endif
Vector vRenderOriginToCamera = currentView.origin - vRenderOrigin;
Vector *pClipBuffers[2];
int clipAllocSize = (6 + currentComplexFrustum.Count()); //possible to add 1 point per cut, 4 starting points, N plane cuts, 2 extra because I'm paranoid
pClipBuffers[0] = (Vector *)stackalloc( sizeof( Vector ) * clipAllocSize * 2 );
pClipBuffers[1] = pClipBuffers[0] + clipAllocSize;
VPlane *currentFrustum = currentComplexFrustum.Base();
int iCurrentFrustumPlanes = currentComplexFrustum.Count();
for( int i = 0; i != 3; ++i )
{
Vector vPolyStartPoint;
float fDot = vRenderOriginToCamera.Dot( vOBBVectors[i] );
if( fDot > vMaxs[i] )
{
//on the positive side of this axis
vPolyStartPoint = vRenderOrigin + (vOBBVectors[i] * vMaxs[i]);
}
else if( fDot < vMins[i] )
{
//on the negative side of this axis
vPolyStartPoint = vRenderOrigin + (vOBBVectors[i] * vMins[i]);
}
else
{
//can't see the outside of the face
continue;
}
int iBasis[2];
iBasis[0] = (i&1) ^ 1;
iBasis[1] = (i&2) ^ 2;
pClipBuffers[0][0] = vPolyStartPoint + (vOBBVectors[iBasis[0]] * vMins[iBasis[0]]) + (vOBBVectors[iBasis[1]] * vMaxs[iBasis[1]]);
pClipBuffers[0][1] = vPolyStartPoint + (vOBBVectors[iBasis[0]] * vMaxs[iBasis[0]]) + (vOBBVectors[iBasis[1]] * vMaxs[iBasis[1]]);
pClipBuffers[0][2] = vPolyStartPoint + (vOBBVectors[iBasis[0]] * vMaxs[iBasis[0]]) + (vOBBVectors[iBasis[1]] * vMins[iBasis[1]]);
pClipBuffers[0][3] = vPolyStartPoint + (vOBBVectors[iBasis[0]] * vMins[iBasis[0]]) + (vOBBVectors[iBasis[1]] * vMins[iBasis[1]]);
//clip by first plane and put output into pInVerts
int iVertCount = ClipPolyToPlane( pClipBuffers[0], 4, pClipBuffers[1], currentFrustum[0].m_Normal, currentFrustum[0].m_Dist, 0.01f );
for( int j = 1; j != iCurrentFrustumPlanes; ++j )
{
if( iVertCount < 3 )
break; //nothing left in the frustum
iVertCount = ClipPolyToPlane( pClipBuffers[j&1], iVertCount, pClipBuffers[(j&1)^1], currentFrustum[j].m_Normal, currentFrustum[j].m_Dist, 0.01f );
}
if( iVertCount >= 3 )
{
return true; //this polygon is visible, we're done
}
}
return false; //none of the polygons are visible
}
else
{
//rely on flatbasic's technique since we're just using a quad
return BaseClass::ShouldUpdatePortalView_BasedOnView( currentView, currentComplexFrustum );
}
#endif
}
extern ConVar r_portal_use_complex_frustums;
bool C_Prop_Mirror::CalcFrustumThroughPortal( const Vector &ptCurrentViewOrigin, Frustum OutputFrustum )
{
if( r_portal_use_complex_frustums.GetBool() == false )
return false;
if( m_vForward.Dot( ptCurrentViewOrigin ) <= m_vForward.Dot( m_ptOrigin ) )
return false; //looking at portal backface
Vector pTransformedVerts[10];
const matrix3x4_t &matLocalToWorld = EntityToWorldTransform();
for( int i = 0; i != m_LocalSpaceReflectionPolygonVertCount; ++i )
{
VectorTransform( &m_LocalSpaceReflectionPolygonVerts[i].x, matLocalToWorld, &pTransformedVerts[i].x );
}
return CalcFrustumThroughPolygon( pTransformedVerts, m_LocalSpaceReflectionPolygonVertCount, ptCurrentViewOrigin, OutputFrustum );
}
void C_Prop_Mirror::RenderPortalViewToBackBuffer( CViewRender *pViewRender, const CViewSetup &cameraView )
{
VPROF( "C_Prop_Mirror::RenderPortalViewToBackBuffer" );
Frustum FrustumBackup;
memcpy( FrustumBackup, pViewRender->GetFrustum(), sizeof( Frustum ) );
Frustum seeThroughFrustum;
bool bUseSeeThroughFrustum;
bUseSeeThroughFrustum = CalcFrustumThroughPortal( cameraView.origin, seeThroughFrustum );
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 );
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 mirror 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_matrixThisToLinked; //technically we should be using the inverse of m_matrixThisToLinked, but it's the same matrix!
portalView.m_matCustomViewMatrix = matTemp.As3x4();
portalView.m_bCustomViewMatrix = true;
CopyToCurrentView( pViewRender, portalView );
CMatRenderContextPtr pRenderContext( materials );
pRenderContext->FlipCullMode(); //mirroring the world requires a flip in cull mode or we'll draw everything bass ackwards
//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);
{
float fCustomClipPlane[4];
fCustomClipPlane[0] = m_vForward.x;
fCustomClipPlane[1] = m_vForward.y;
fCustomClipPlane[2] = m_vForward.z;
fCustomClipPlane[3] = m_vForward.Dot( m_ptOrigin );
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
pRenderContext->PushCustomClipPlane( fCustomClipPlane );
}
{
ViewCustomVisibility_t customVisibility;
m_pLinkedPortal->AddToVisAsExitPortal( &customVisibility );
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!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
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 );
}
pRenderContext->FlipCullMode(); //flip the cull mode again to restore it to it's original setting
pRenderContext->PopCustomClipPlane();
if( bReplaceOldPortalClipPlane )
{
CPortalRenderable_FlatBasic *pCurrentExitPortal = (CPortalRenderable_FlatBasic *)g_pPortalRender->GetCurrentViewExitPortal();
float fCustomClipPlane[4];
fCustomClipPlane[0] = pCurrentExitPortal->m_vForward.x;
fCustomClipPlane[1] = pCurrentExitPortal->m_vForward.y;
fCustomClipPlane[2] = pCurrentExitPortal->m_vForward.z;
fCustomClipPlane[3] = pCurrentExitPortal->m_vForward.Dot( pCurrentExitPortal->m_ptOrigin - (pCurrentExitPortal->m_vForward * 0.5f) ); //moving it back a smidge to eliminate visual artifacts for half-in objects
pRenderContext->PushCustomClipPlane( fCustomClipPlane );
}
//restore old vis data
CopyToCurrentView( pViewRender, cameraView );
}
int C_Prop_Mirror::DrawModel( int flags, const RenderableInstance_t &instance )
{
if( g_pPortalRender->GetRemainingPortalViewDepth() == 0 )
{
SetBodygroup( m_iMirrorSurfaceBodyGroup, 0 );
}
int iRetVal = BaseClass::DrawModel( flags, instance );
if( g_pPortalRender->GetRemainingPortalViewDepth() == 0 )
{
SetBodygroup( m_iMirrorSurfaceBodyGroup, 1 );
}
return iRetVal;
}