summaryrefslogtreecommitdiff
path: root/game/client/portal/PortalRender.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/portal/PortalRender.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/portal/PortalRender.cpp')
-rw-r--r--game/client/portal/PortalRender.cpp1178
1 files changed, 1178 insertions, 0 deletions
diff --git a/game/client/portal/PortalRender.cpp b/game/client/portal/PortalRender.cpp
new file mode 100644
index 0000000..ce4ca46
--- /dev/null
+++ b/game/client/portal/PortalRender.cpp
@@ -0,0 +1,1178 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+
+#include "cbase.h"
+#include "PortalRender.h"
+#include "clienteffectprecachesystem.h"
+#include "view.h"
+#include "c_pixel_visibility.h"
+#include "glow_overlay.h"
+#include "portal_render_targets.h" //depth doubler
+#include "materialsystem/itexture.h"
+#include "toolframework/itoolframework.h"
+#include "tier1/KeyValues.h"
+#include "view_scene.h"
+#include "viewrender.h"
+#include "vprof.h"
+
+CLIENTEFFECT_REGISTER_BEGIN( PrecachePortalDrawingMaterials )
+CLIENTEFFECT_MATERIAL( "shadertest/wireframe" )
+CLIENTEFFECT_MATERIAL( "engine/writez_model" )
+CLIENTEFFECT_MATERIAL( "engine/TranslucentVertexColor" )
+CLIENTEFFECT_REGISTER_END()
+
+#define TEMP_DISABLE_PORTAL_VIS_QUERY
+
+static ConVar r_forcecheapwater( "r_forcecheapwater", "0", FCVAR_CLIENTDLL | FCVAR_CHEAT, "Force all water to be cheap water, will show old renders if enabled after water has been seen" );
+
+
+ConVar r_portal_use_stencils( "r_portal_use_stencils", "1", FCVAR_CLIENTDLL, "Render portal views using stencils (if available)" ); //draw portal views using stencil rendering
+ConVar r_portal_stencil_depth( "r_portal_stencil_depth", "2", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "When using stencil views, this changes how many views within views we see" );
+
+//-----------------------------------------------------------------------------
+//
+// Portal rendering management class
+//
+//-----------------------------------------------------------------------------
+static CPortalRender s_PortalRender;
+CPortalRender* g_pPortalRender = &s_PortalRender;
+
+
+//-------------------------------------------
+//Portal View ID Node helpers
+//-------------------------------------------
+CUtlVector<int> s_iFreedViewIDs; //when a view id node gets freed, it's primary view id gets added here
+
+PortalViewIDNode_t *AllocPortalViewIDNode( int iChildLinkCount )
+{
+ PortalViewIDNode_t *pNode = new PortalViewIDNode_t; //for now we just new/delete
+
+ int iFreedIDsCount = s_iFreedViewIDs.Count();
+ if( iFreedIDsCount != 0 )
+ {
+ pNode->iPrimaryViewID = s_iFreedViewIDs.Tail();
+ s_iFreedViewIDs.FastRemove( iFreedIDsCount - 1 );
+ }
+ else
+ {
+ static int iNewAllocationCounter = VIEW_ID_COUNT;
+ pNode->iPrimaryViewID = iNewAllocationCounter;
+ iNewAllocationCounter += 2; //2 to make room for skybox view ids
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pNode->occlusionQueryHandle = pRenderContext->CreateOcclusionQueryObject();
+#endif
+ pNode->iOcclusionQueryPixelsRendered = -5;
+ pNode->iWindowPixelsAtQueryTime = 0;
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+
+ if( iChildLinkCount != 0 )
+ {
+ pNode->ChildNodes.SetCount( iChildLinkCount );
+ memset( pNode->ChildNodes.Base(), NULL, sizeof( PortalViewIDNode_t * ) * iChildLinkCount );
+ }
+
+ return pNode;
+}
+
+void FreePortalViewIDNode( PortalViewIDNode_t *pNode )
+{
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ if( pNode->ChildNodes[i] != NULL )
+ FreePortalViewIDNode( pNode->ChildNodes[i] );
+ }
+
+ s_iFreedViewIDs.AddToTail( pNode->iPrimaryViewID );
+
+ CMatRenderContextPtr pRenderContext( materials );
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pRenderContext->DestroyOcclusionQueryObject( pNode->occlusionQueryHandle );
+#endif
+
+ delete pNode; //for now we just new/delete
+}
+
+void IncreasePortalViewIDChildLinkCount( PortalViewIDNode_t *pNode )
+{
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ if( pNode->ChildNodes[i] != NULL )
+ IncreasePortalViewIDChildLinkCount( pNode->ChildNodes[i] );
+ }
+ pNode->ChildNodes.AddToTail( NULL );
+}
+
+void RemovePortalViewIDChildLinkIndex( PortalViewIDNode_t *pNode, int iRemoveIndex )
+{
+ Assert( pNode->ChildNodes.Count() > iRemoveIndex );
+
+ if( pNode->ChildNodes[iRemoveIndex] != NULL )
+ {
+ FreePortalViewIDNode( pNode->ChildNodes[iRemoveIndex] );
+ pNode->ChildNodes[iRemoveIndex] = NULL;
+ }
+
+ //I know the current behavior for CUtlVector::FastRemove() is to move the tail into the removed index. But I need that behavior to be true in the future as well so I'm doing it explicitly
+ pNode->ChildNodes[iRemoveIndex] = pNode->ChildNodes.Tail();
+ pNode->ChildNodes.Remove( pNode->ChildNodes.Count() - 1 );
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ if( pNode->ChildNodes[i] )
+ RemovePortalViewIDChildLinkIndex( pNode->ChildNodes[i], iRemoveIndex );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Active Portal class
+//
+//-----------------------------------------------------------------------------
+CPortalRenderable::CPortalRenderable( void ) :
+ m_bIsPlaybackPortal( false )
+{
+ m_matrixThisToLinked.Identity();
+
+ //Portal view ID indexing setup
+ IncreasePortalViewIDChildLinkCount( &s_PortalRender.m_HeadPortalViewIDNode );
+ m_iPortalViewIDNodeIndex = s_PortalRender.m_AllPortals.AddToTail( this );
+}
+
+CPortalRenderable::~CPortalRenderable( void )
+{
+ int iLast = s_PortalRender.m_AllPortals.Count() - 1;
+
+ //update the soon-to-be-transplanted portal's index
+ s_PortalRender.m_AllPortals[iLast]->m_iPortalViewIDNodeIndex = m_iPortalViewIDNodeIndex;
+
+ //I know the current behavior for CUtlVector::FastRemove() is to move the tail into the removed index. But I need that behavior to be true in the future as well so I'm doing it explicitly
+ s_PortalRender.m_AllPortals[m_iPortalViewIDNodeIndex] = s_PortalRender.m_AllPortals.Tail();
+ s_PortalRender.m_AllPortals.Remove( iLast );
+
+ RemovePortalViewIDChildLinkIndex( &s_PortalRender.m_HeadPortalViewIDNode, m_iPortalViewIDNodeIndex ); //does the same transplant operation as above to all portal view id nodes
+}
+
+
+void CPortalRenderable::BeginPortalPixelVisibilityQuery( void )
+{
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ return;
+#endif
+
+ if( g_pPortalRender->ShouldUseStencilsToRenderPortals() ) //this function exists because we require help in texture mode, we need no assistance in stencil mode. Moreover, doing the query twice will probably fubar the results
+ return;
+
+ PortalViewIDNode_t *pCurrentPortalViewNode = g_pPortalRender->m_PortalViewIDNodeChain[g_pPortalRender->m_iViewRecursionLevel]->ChildNodes[m_iPortalViewIDNodeIndex];
+
+ if( pCurrentPortalViewNode )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->BeginOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+
+ int iX, iY, iWidth, iHeight;
+ pRenderContext->GetViewport( iX, iY, iWidth, iHeight );
+
+ pCurrentPortalViewNode->iWindowPixelsAtQueryTime = iWidth * iHeight;
+ }
+}
+
+void CPortalRenderable::EndPortalPixelVisibilityQuery( void )
+{
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ return;
+#endif
+
+ if( g_pPortalRender->ShouldUseStencilsToRenderPortals() ) //this function exists because we require help in texture mode, we need no assistance in stencil mode. Moreover, doing the query twice will probably fubar the results
+ return;
+
+ PortalViewIDNode_t *pCurrentPortalViewNode = g_pPortalRender->m_PortalViewIDNodeChain[g_pPortalRender->m_iViewRecursionLevel]->ChildNodes[m_iPortalViewIDNodeIndex];
+
+ if( pCurrentPortalViewNode )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->EndOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CPortalRender::CPortalRender()
+: m_MaterialsAccess( m_Materials )
+{
+ m_iRemainingPortalViewDepth = 1; //let's portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
+ m_iViewRecursionLevel = 0;
+ m_pRenderingViewForPortal = NULL;
+ m_pRenderingViewExitPortal = NULL;
+
+ m_PortalViewIDNodeChain[0] = &m_HeadPortalViewIDNode;
+}
+
+
+void CPortalRender::LevelInitPreEntity()
+{
+ // refresh materials - not sure if this needs to be done every level
+ m_Materials.m_Wireframe.Init( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_WriteZ_Model.Init( "engine/writez_model", TEXTURE_GROUP_CLIENT_EFFECTS );
+ m_Materials.m_TranslucentVertexColor.Init( "engine/TranslucentVertexColor", TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+void CPortalRender::LevelShutdownPreEntity()
+{
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ delete m_RecordedPortals[i].m_pActivePortal;
+ }
+ m_RecordedPortals.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// only use stencils when it's requested, and expensive water won't cause it to freak out
+//-----------------------------------------------------------------------------
+bool CPortalRender::ShouldUseStencilsToRenderPortals( ) const
+{
+ // only use stencils when it's requested, and available
+ return r_portal_use_stencils.GetBool() && ( materials->StencilBufferBits() != 0 );
+}
+
+
+int CPortalRender::ShouldForceCheaperWaterLevel() const
+{
+ if( r_forcecheapwater.GetBool() )
+ return 0;
+
+ if( m_iViewRecursionLevel > 0 )
+ {
+ if( portalrendertargets->GetWaterReflectionTextureForStencilDepth( m_iViewRecursionLevel ) == NULL )
+ return 0;
+
+ PortalViewIDNode_t *pPixelVisNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel - 1]->ChildNodes[m_pRenderingViewForPortal->m_iPortalViewIDNodeIndex];
+
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized >= 0.0f )
+ {
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.005f )
+ return 0;
+
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.02f )
+ return 1;
+
+ if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.05f )
+ return 2;
+ }
+ }
+
+ return 3;
+}
+
+bool CPortalRender::ShouldObeyStencilForClears() const
+{
+ return (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals();
+}
+
+void CPortalRender::WaterRenderingHandler_PreReflection() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( false );
+ }
+}
+
+void CPortalRender::WaterRenderingHandler_PostReflection() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( true );
+ }
+}
+
+void CPortalRender::WaterRenderingHandler_PreRefraction() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( false );
+ }
+}
+
+void CPortalRender::WaterRenderingHandler_PostRefraction() const
+{
+ if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->SetStencilEnable( true );
+ }
+}
+
+
+void Recursive_UpdatePortalPixelVisibility( PortalViewIDNode_t *pNode, IMatRenderContext *pRenderContext )
+{
+ if( pNode->iWindowPixelsAtQueryTime > 0 )
+ {
+ if( pNode->iOcclusionQueryPixelsRendered < -1 )
+ {
+ //First couple queries. We seem to be getting bogus 0's on the first queries sometimes. ignore the results.
+ ++pNode->iOcclusionQueryPixelsRendered;
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+ }
+ else
+ {
+ pNode->iOcclusionQueryPixelsRendered = pRenderContext->OcclusionQuery_GetNumPixelsRendered( pNode->occlusionQueryHandle );
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = ((float)pNode->iOcclusionQueryPixelsRendered) / ((float)pNode->iWindowPixelsAtQueryTime);
+ }
+ }
+ else
+ {
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+ }
+
+ pNode->iWindowPixelsAtQueryTime = 0;
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
+ if( pChildNode )
+ Recursive_UpdatePortalPixelVisibility( pChildNode, pRenderContext );
+ }
+}
+
+void CPortalRender::UpdatePortalPixelVisibility( void )
+{
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ return;
+#endif
+
+ if( m_iViewRecursionLevel != 0 )
+ return;
+
+ IMatRenderContext *pRenderContext = materials->GetRenderContext();
+ //CMatRenderContextPtr pRenderContext( materials );
+
+ for( int i = m_HeadPortalViewIDNode.ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = m_HeadPortalViewIDNode.ChildNodes[i];
+ if( pChildNode )
+ Recursive_UpdatePortalPixelVisibility( pChildNode, pRenderContext );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Invalidates pixel visibility data for all portals for this next frame.
+//-----------------------------------------------------------------------------
+void Recursive_InvalidatePortalPixelVis( PortalViewIDNode_t *pNode )
+{
+ pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
+ pNode->iOcclusionQueryPixelsRendered = -5;
+ pNode->iWindowPixelsAtQueryTime = 0;
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
+ if( pChildNode )
+ Recursive_InvalidatePortalPixelVis( pChildNode );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Preserves pixel visibility data when view id's are getting swapped around
+//-----------------------------------------------------------------------------
+void CPortalRender::EnteredPortal( CPortalRenderable *pEnteredPortal )
+{
+ CPortalRenderable *pExitPortal = pEnteredPortal->GetLinkedPortal();
+ Assert( pExitPortal != NULL );
+
+ if ( pExitPortal == NULL )
+ return;
+
+ int iNodeLinkCount = m_HeadPortalViewIDNode.ChildNodes.Count();
+
+ PortalViewIDNode_t *pNewHead = m_HeadPortalViewIDNode.ChildNodes[pEnteredPortal->m_iPortalViewIDNodeIndex];
+ m_HeadPortalViewIDNode.ChildNodes[pEnteredPortal->m_iPortalViewIDNodeIndex] = NULL;
+
+ //Create a new node that will preserve main's visibility. This new node will be linked to the new head node at the exit portal's index (imagine entering a portal walking backwards)
+ PortalViewIDNode_t *pExitPortalsNewNode = AllocPortalViewIDNode( iNodeLinkCount );
+ {
+ for( int i = 0; i != iNodeLinkCount; ++i )
+ {
+ pExitPortalsNewNode->ChildNodes[i] = m_HeadPortalViewIDNode.ChildNodes[i];
+ m_HeadPortalViewIDNode.ChildNodes[i] = NULL;
+ }
+
+ PixelVisibility_ShiftVisibilityViews( VIEW_MAIN, pExitPortalsNewNode->iPrimaryViewID );
+ PixelVisibility_ShiftVisibilityViews( VIEW_3DSKY, pExitPortalsNewNode->iPrimaryViewID + 1 );
+ }
+
+
+
+ if( pNewHead ) //it's possible we entered a portal we couldn't see through
+ {
+ Assert( pNewHead->ChildNodes.Count() == m_HeadPortalViewIDNode.ChildNodes.Count() );
+ Assert( pNewHead->ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] == NULL ); //seeing out of an exit portal back into itself should be impossible
+
+ for( int i = 0; i != iNodeLinkCount; ++i )
+ {
+ m_HeadPortalViewIDNode.ChildNodes[i] = pNewHead->ChildNodes[i];
+ pNewHead->ChildNodes[i] = NULL; //going to be freeing the node in a minute, don't want to kill transplanted children
+ }
+
+ //Since the primary views will always be 0 and 1, we have to shift results instead of replacing the id's
+ PixelVisibility_ShiftVisibilityViews( pNewHead->iPrimaryViewID, VIEW_MAIN );
+ PixelVisibility_ShiftVisibilityViews( pNewHead->iPrimaryViewID + 1, VIEW_3DSKY );
+
+ FreePortalViewIDNode( pNewHead );
+ }
+
+ Assert( m_HeadPortalViewIDNode.ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] == NULL ); //asserted above in pNewHead code, but call me paranoid
+ m_HeadPortalViewIDNode.ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] = pExitPortalsNewNode;
+
+ //Because pixel visibility is based off of *last* frame's visibility. We can get cases where a certain portal
+ //wasn't visible last frame, but is takes up most of the screen this frame.
+ //Set all portal pixel visibility to unknown visibility.
+ for( int i = m_HeadPortalViewIDNode.ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = m_HeadPortalViewIDNode.ChildNodes[i];
+ if( pChildNode )
+ Recursive_InvalidatePortalPixelVis( pChildNode );
+ }
+}
+
+
+
+
+
+bool CPortalRender::DrawPortalsUsingStencils( CViewRender *pViewRender )
+{
+ VPROF( "CPortalRender::DrawPortalsUsingStencils" );
+
+ if( !ShouldUseStencilsToRenderPortals() )
+ return false;
+
+ int iDrawFlags = pViewRender->GetDrawFlags();
+
+ if ( (iDrawFlags & DF_RENDER_REFLECTION) != 0 )
+ return false;
+
+ if ( ((iDrawFlags & DF_CLIP_Z) != 0) && ((iDrawFlags & DF_CLIP_BELOW) == 0) ) //clipping above the water height
+ return false;
+
+ int iNumRenderablePortals = m_ActivePortals.Count();
+
+ // This loop is necessary because tools can suppress rendering without telling the portal system
+ CUtlVector< CPortalRenderable* > actualActivePortals( 0, iNumRenderablePortals );
+ for ( int i = 0; i < iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pPortalRenderable = m_ActivePortals[i];
+ C_BaseEntity *pPairedEntity = pPortalRenderable->PortalRenderable_GetPairedEntity();
+ bool bIsVisible = (pPairedEntity == NULL) || (pPairedEntity->IsVisible() && pPairedEntity->ShouldDraw()); //either unknown visibility or definitely visible.
+
+ if ( !pPortalRenderable->m_bIsPlaybackPortal )
+ {
+ if ( !bIsVisible )
+ {
+ //can't see through the portal, free up it's view id node for use elsewhere
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] != NULL )
+ {
+ FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] );
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] = NULL;
+ }
+
+ continue;
+ }
+ }
+
+ actualActivePortals.AddToTail( m_ActivePortals[i] );
+ }
+ iNumRenderablePortals = actualActivePortals.Count();
+ if( iNumRenderablePortals == 0 )
+ return false;
+
+ const int iMaxDepth = MIN( r_portal_stencil_depth.GetInt(), MIN( MAX_PORTAL_RECURSIVE_VIEWS, (1 << materials->StencilBufferBits()) ) - 1 );
+
+ if( m_iViewRecursionLevel >= iMaxDepth ) //can't support any more views
+ {
+ m_iRemainingPortalViewDepth = 0; //special case handler for max depth 0 cases
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = actualActivePortals[i];
+ pCurrentPortal->DrawPortal();
+ }
+ return false;
+ }
+
+ m_iRemainingPortalViewDepth = (iMaxDepth - m_iViewRecursionLevel) - 1;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Flush( true ); //to prevent screwing up the last opaque object
+
+ //queued mode makes us pass the barrier of just noticeable difference when using a previous frame's occlusion as a draw skip check
+ bool bIsQueuedMode = (materials->GetThreadMode() == MATERIAL_QUEUED_THREADED);
+
+ const CViewSetup *pViewSetup = pViewRender->GetViewSetup();
+ m_RecursiveViewSetups[m_iViewRecursionLevel] = *pViewSetup;
+
+ CViewSetup ViewBackup;// = *pViewSetup; //backup the view, we'll need to restore it
+ memcpy( &ViewBackup, pViewSetup, sizeof( CViewSetup ) );
+
+ Vector ptCameraOrigin = pViewSetup->origin;
+ Vector vCameraForward;
+ AngleVectors( pViewSetup->angles, &vCameraForward, NULL, NULL );
+
+ int iX, iY, iWidth, iHeight;
+ pRenderContext->GetViewport( iX, iY, iWidth, iHeight );
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ int iScreenPixelCount = iWidth * iHeight;
+#endif
+
+ bool bRebuildDrawListsWhenDone = false;
+
+
+ int iParentLevelStencilReferenceValue = m_iViewRecursionLevel;
+ int iStencilReferenceValue = iParentLevelStencilReferenceValue + 1;
+
+ if( m_iViewRecursionLevel == 0 ) //first entry into the stencil drawing
+ {
+ pRenderContext->SetStencilEnable( true );
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_REPLACE );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilTestMask( 0xFF );
+ pRenderContext->SetStencilWriteMask( 0xFF );
+ pRenderContext->SetStencilReferenceValue( 0 );
+
+ m_RecursiveViewComplexFrustums[0].RemoveAll(); //clear any garbage leftover in the complex frustums from last frame
+ }
+
+ if( m_RecursiveViewComplexFrustums[m_iViewRecursionLevel].Count() == 0 )
+ {
+ //nothing in the complex frustum from the current view, copy the standard frustum in
+ m_RecursiveViewComplexFrustums[m_iViewRecursionLevel].AddMultipleToTail( FRUSTUM_NUMPLANES, pViewRender->GetFrustum() );
+ }
+
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = actualActivePortals[i];
+
+ m_RecursiveViewComplexFrustums[m_iViewRecursionLevel + 1].RemoveAll(); //clear any previously stored complex frustum
+
+ if( (pCurrentPortal->GetLinkedPortal() == NULL) ||
+ (pCurrentPortal == m_pRenderingViewExitPortal) ||
+ (pCurrentPortal->ShouldUpdatePortalView_BasedOnView( *pViewSetup, m_RecursiveViewComplexFrustums[m_iViewRecursionLevel] ) == false) )
+ {
+ //can't see through the portal, free up it's view id node for use elsewhere
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] != NULL )
+ {
+ FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] );
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = NULL;
+ }
+ continue;
+ }
+
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
+
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] == NULL )
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = AllocPortalViewIDNode( m_HeadPortalViewIDNode.ChildNodes.Count() );
+
+ PortalViewIDNode_t *pCurrentPortalViewNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
+
+ // Step 0, Allow for special effects to happen before cutting a hole
+ {
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
+
+ pCurrentPortal->DrawPreStencilMask();
+ }
+
+ //step 1, write out the stencil values (and colors if you want, but really not necessary)
+ {
+ //pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_INCR );
+ //pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ //pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ //pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
+
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pRenderContext->BeginOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+ pCurrentPortalViewNode->iWindowPixelsAtQueryTime = iScreenPixelCount;
+#endif
+
+ pCurrentPortal->DrawStencilMask();
+
+#ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
+ pRenderContext->EndOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
+#endif
+ }
+
+ //see if we can skip the heavy lifting due to low visibility
+ if ( bIsQueuedMode || //don't use pixel visibly as a skip check in queued mode, the data is simply too old.
+ pCurrentPortal->ShouldUpdatePortalView_BasedOnPixelVisibility( pCurrentPortalViewNode->fScreenFilledByPortalSurfaceLastFrame_Normalized ) )
+ {
+ //step 2, clear the depth buffer in stencil areas so we can render a new scene to them
+ {
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
+ pRenderContext->ClearBuffersObeyStencil( false, true );
+ }
+
+
+ //step 3, fill in stencil views (remember that in multiple depth situations that any subportals will run through this function again before this section completes, thereby screwing with stencil settings)
+ {
+ bRebuildDrawListsWhenDone = true;
+
+ MaterialFogMode_t fogModeBackup = pRenderContext->GetFogMode();
+ unsigned char fogColorBackup[4];
+ pRenderContext->GetFogColor( fogColorBackup );
+ float fFogStartBackup, fFogEndBackup, fFogZBackup;
+ pRenderContext->GetFogDistances( &fFogStartBackup, &fFogEndBackup, &fFogZBackup );
+ CGlowOverlay::BackupSkyOverlayData( m_iViewRecursionLevel );
+
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
+
+ pCurrentPortal->RenderPortalViewToBackBuffer( pViewRender, *pViewSetup );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = NULL;
+
+ CGlowOverlay::RestoreSkyOverlayData( m_iViewRecursionLevel );
+ memcpy( (void *)pViewSetup, &ViewBackup, sizeof( CViewSetup ) );
+ pViewRender->m_pActiveRenderer->EnableWorldFog();
+
+ pRenderContext->FogMode( fogModeBackup );
+ pRenderContext->FogColor3ubv( fogColorBackup );
+ pRenderContext->FogStart( fFogStartBackup );
+ pRenderContext->FogEnd( fFogEndBackup );
+ pRenderContext->SetFogZ( fFogZBackup );
+
+
+ //do a full reset of what we think the stencil operations are in case the recursive calls got weird
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilTestMask( 0xFF );
+ pRenderContext->SetStencilWriteMask( 0xFF );
+ pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
+ }
+
+ //step 4, patch up the fact that we just made a hole in the wall because it's not *really* a hole at all
+ {
+ pCurrentPortal->DrawPostStencilFixes();
+ }
+ }
+
+ //step 5, restore the stencil mask to the parent level
+ {
+ pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_DECR );
+ pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
+ pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
+
+ pRenderContext->PerformFullScreenStencilOperation();
+ }
+ }
+
+ //step 6, go back to non-stencil rendering mode in preparation to resume normal scene rendering
+ if( m_iViewRecursionLevel == 0 )
+ {
+ Assert( m_pRenderingViewForPortal == NULL );
+ Assert( m_pRenderingViewExitPortal == NULL );
+ m_pRenderingViewExitPortal = NULL;
+ m_pRenderingViewForPortal = NULL;
+
+ pRenderContext->SetStencilEnable( false );
+ pRenderContext->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NEVER);
+ pRenderContext->SetStencilPassOperation(STENCILOPERATION_KEEP);
+ pRenderContext->SetStencilFailOperation(STENCILOPERATION_KEEP);
+ pRenderContext->SetStencilZFailOperation(STENCILOPERATION_KEEP);
+ pRenderContext->SetStencilTestMask( 0xFF );
+ pRenderContext->SetStencilWriteMask( 0xFF );
+ pRenderContext->SetStencilReferenceValue( 0 );
+
+ m_RecursiveViewComplexFrustums[0].RemoveAll();
+ }
+ else
+ {
+ pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
+ pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
+ pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
+ }
+
+ if( bRebuildDrawListsWhenDone )
+ {
+ memcpy( (void *)pViewSetup, &ViewBackup, sizeof( CViewSetup ) ); //if we don't restore this, the view is permanently altered (in mid render of an existing scene)
+ }
+
+ pRenderContext->Flush( true ); //just in case
+
+ ++m_iRemainingPortalViewDepth;
+
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = actualActivePortals[i];
+ pCurrentPortal->DrawPortal();
+ }
+
+ return bRebuildDrawListsWhenDone;
+}
+
+
+
+
+
+
+#ifdef _DEBUG
+extern bool g_bRenderingCameraView;
+#endif
+
+void CPortalRender::DrawPortalsToTextures( CViewRender *pViewRender, const CViewSetup &cameraView )
+{
+ if( ShouldUseStencilsToRenderPortals() )
+ return;
+
+ /*if ( (pViewRender->GetDrawFlags() & DF_RENDER_REFLECTION) != 0 )
+ return;*/
+
+ m_iRemainingPortalViewDepth = 1;
+ m_iViewRecursionLevel = 0;
+ m_pRenderingViewForPortal = NULL;
+ m_pRenderingViewExitPortal = NULL;
+
+ m_RecursiveViewSetups[m_iViewRecursionLevel] = cameraView;
+
+ m_RecursiveViewComplexFrustums[0].RemoveAll(); //clear any garbage leftover in the complex frustums from last frame
+ m_RecursiveViewComplexFrustums[0].AddMultipleToTail( FRUSTUM_NUMPLANES, pViewRender->GetFrustum() );
+
+
+#ifdef _DEBUG
+ g_bRenderingCameraView = true;
+#endif
+
+ int iNumRenderablePortals = g_pPortalRender->m_ActivePortals.Count();
+
+ Vector ptCameraOrigin = cameraView.origin;
+
+ //an extraneous push to update the frustum
+ render->Push3DView( cameraView, 0, NULL, pViewRender->GetFrustum() );
+
+ for( int i = 0; i != iNumRenderablePortals; ++i )
+ {
+ CPortalRenderable *pCurrentPortal = g_pPortalRender->m_ActivePortals[i];
+
+ if( (pCurrentPortal->ShouldUpdatePortalView_BasedOnView( cameraView, m_RecursiveViewComplexFrustums[m_iViewRecursionLevel] ) == false) ||
+ (pCurrentPortal->GetLinkedPortal() == NULL) )
+ {
+ //can't see through the portal, free up it's view id node for use elsewhere
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] != NULL )
+ {
+ FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] );
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = NULL;
+ }
+ continue;
+ }
+
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
+
+ if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] == NULL )
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = AllocPortalViewIDNode( m_HeadPortalViewIDNode.ChildNodes.Count() );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
+
+ pCurrentPortal->RenderPortalViewToTexture( pViewRender, cameraView );
+
+ m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = NULL;
+ }
+
+ render->PopView( pViewRender->GetFrustum() );
+
+ m_iRemainingPortalViewDepth = 1;
+ m_iViewRecursionLevel = 0;
+
+ Assert( m_pRenderingViewForPortal == NULL );
+ Assert( m_pRenderingViewExitPortal == NULL );
+ m_pRenderingViewForPortal = NULL;
+ m_pRenderingViewExitPortal = NULL;
+
+#ifdef _DEBUG
+ g_bRenderingCameraView = false;
+#endif
+}
+
+void CPortalRender::AddPortal( CPortalRenderable *pPortal )
+{
+ for( int i = m_ActivePortals.Count(); --i >= 0; )
+ {
+ if( m_ActivePortals[i] == pPortal )
+ return;
+ }
+
+ m_ActivePortals.AddToTail( pPortal );
+}
+
+void CPortalRender::RemovePortal( CPortalRenderable *pPortal )
+{
+ for( int i = m_ActivePortals.Count(); --i >= 0; )
+ {
+ if( m_ActivePortals[i] == pPortal )
+ {
+ m_ActivePortals.FastRemove( i );
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we currently rendering a portal?
+//-----------------------------------------------------------------------------
+bool CPortalRender::IsRenderingPortal() const
+{
+ return m_pRenderingViewForPortal != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns view recursion level
+//-----------------------------------------------------------------------------
+int CPortalRender::GetViewRecursionLevel() const
+{
+ return m_iViewRecursionLevel;
+}
+
+//-----------------------------------------------------------------------------
+//normalized for how many of the screen's possible pixels it takes up, less than zero indicates a lack of data from last frame
+//-----------------------------------------------------------------------------
+float CPortalRender::GetPixelVisilityForPortalSurface( const CPortalRenderable *pPortal ) const
+{
+ PortalViewIDNode_t *pNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortal->m_iPortalViewIDNodeIndex];
+ if( pNode )
+ return pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized;
+
+ return -1.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to query about the exit portal associated with the currently rendering portal
+//-----------------------------------------------------------------------------
+const Vector &CPortalRender::GetExitPortalFogOrigin() const
+{
+ return m_pRenderingViewExitPortal->GetFogOrigin();
+}
+
+void CPortalRender::ShiftFogForExitPortalView() const
+{
+ if ( m_pRenderingViewExitPortal )
+ {
+ m_pRenderingViewExitPortal->ShiftFogForExitPortalView();
+ }
+}
+
+void CPortalRenderable::ShiftFogForExitPortalView() const
+{
+ CMatRenderContextPtr pRenderContext( materials );
+ float fFogStart, fFogEnd, fFogZ;
+ pRenderContext->GetFogDistances( &fFogStart, &fFogEnd, &fFogZ );
+
+ Vector vFogOrigin = GetFogOrigin();
+ Vector vCameraToExitPortal = vFogOrigin - CurrentViewOrigin();
+ float fDistModifier = vCameraToExitPortal.Dot( CurrentViewForward() );
+
+ fFogStart += fDistModifier;
+ fFogEnd += fDistModifier;
+ //fFogZ += something; //FIXME: find out what the hell to do with this
+
+ pRenderContext->FogStart( fFogStart );
+ pRenderContext->FogEnd( fFogEnd );
+ pRenderContext->SetFogZ( fFogZ );
+}
+
+SkyboxVisibility_t CPortalRender::IsSkyboxVisibleFromExitPortal() const
+{
+ return m_pRenderingViewExitPortal->SkyBoxVisibleFromPortal();
+}
+
+bool CPortalRender::DoesExitPortalViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
+{
+ return m_pRenderingViewExitPortal->DoesExitViewIntersectWaterPlane( waterZ, leafWaterDataID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the remaining number of portals to render within other portals
+// lets portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
+//-----------------------------------------------------------------------------
+int CPortalRender::GetRemainingPortalViewDepth() const
+{
+ return m_iRemainingPortalViewDepth;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the current View IDs
+//-----------------------------------------------------------------------------
+int CPortalRender::GetCurrentViewId() const
+{
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel] != NULL );
+#ifdef _DEBUG
+ for( int i = 0; i != m_iViewRecursionLevel; ++i )
+ {
+ Assert( m_PortalViewIDNodeChain[i]->iPrimaryViewID != m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID );
+ }
+#endif
+
+ return m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID;
+}
+
+int CPortalRender::GetCurrentSkyboxViewId() const
+{
+ Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel] != NULL );
+ return m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID + 1;
+}
+
+
+void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float flY, float w, float h ); //implemented in view_scene.cpp
+
+void CPortalRender::OverlayPortalRenderTargets( float w, float h )
+{
+ OverlayCameraRenderTarget( "engine/debug_portal_1", 0,0, w,h );
+ OverlayCameraRenderTarget( "engine/debug_portal_2", w+10,0, w,h );
+
+ OverlayCameraRenderTarget( "engine/debug_water_reflect_0", 0, h+10, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_reflect_1", w+10, h+10, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_reflect_2", (w+10) * 2, h+10, w,h );
+
+ OverlayCameraRenderTarget( "engine/debug_water_refract_0", 0, (h+10) * 2, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_refract_1", w+10, (h+10) * 2, w,h );
+ OverlayCameraRenderTarget( "engine/debug_water_refract_2", (w+10) * 2, (h+10) * 2, w,h );
+}
+
+void CPortalRender::UpdateDepthDoublerTexture( const CViewSetup &viewSetup )
+{
+ bool bShouldUpdate = false;
+
+ for( int i = m_ActivePortals.Count(); --i >= 0; )
+ {
+ CPortalRenderable *pPortal = m_ActivePortals[i];
+
+ if( pPortal->ShouldUpdateDepthDoublerTexture( viewSetup ) )
+ {
+ bShouldUpdate = true;
+ break;
+ }
+ }
+
+ if( bShouldUpdate )
+ {
+ Rect_t srcRect;
+ srcRect.x = viewSetup.x;
+ srcRect.y = viewSetup.y;
+ srcRect.width = viewSetup.width;
+ srcRect.height = viewSetup.height;
+
+ ITexture *pTexture = portalrendertargets->GetDepthDoublerTexture();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->CopyRenderTargetToTextureEx( pTexture, 0, &srcRect, NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a recorded portal
+//-----------------------------------------------------------------------------
+int CPortalRender::FindRecordedPortalIndex( int nPortalId )
+{
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_RecordedPortals[i].m_nPortalId == nPortalId )
+ return i;
+ }
+ return -1;
+}
+
+CPortalRenderable* CPortalRender::FindRecordedPortal( int nPortalId )
+{
+ int nIndex = FindRecordedPortalIndex( nPortalId );
+ return ( nIndex >= 0 ) ? m_RecordedPortals[nIndex].m_pActivePortal : NULL;
+}
+
+CPortalRenderable* CPortalRender::FindRecordedPortal( IClientRenderable *pRenderable )
+{
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_RecordedPortals[i].m_pPlaybackRenderable == pRenderable )
+ return m_RecordedPortals[i].m_pActivePortal;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles a portal update message
+//-----------------------------------------------------------------------------
+void CPortalRender::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
+{
+ // Iterate through all the portal ids of all the portals in the keyvalues message
+ CUtlVector<int> foundIds;
+ for ( KeyValues *pCurr = pKeyValues->GetFirstTrueSubKey(); pCurr; pCurr = pCurr->GetNextTrueSubKey() )
+ {
+ // Create new area portals for those ids that don't exist
+ int nPortalId = pCurr->GetInt( "portalId" );
+ IClientRenderable *pRenderable = (IClientRenderable*)pCurr->GetPtr( "clientRenderable" );
+ int nIndex = FindRecordedPortalIndex( nPortalId );
+ if ( nIndex < 0 )
+ {
+ CPortalRenderable *pPortal = NULL;
+ const char *szType = pCurr->GetString( "portalType", "flatBasic" ); //"flatBasic" being the type commonly found in "Portal" mod
+ //search through registered creation functions for one that makes this type of portal
+ for( int i = m_PortalRenderableCreators.Count(); --i >= 0; )
+ {
+ if( FStrEq( szType, m_PortalRenderableCreators[i].portalType.String() ) )
+ {
+ pPortal = m_PortalRenderableCreators[i].creationFunc();
+ break;
+ }
+ }
+
+ if( pPortal == NULL )
+ {
+ AssertMsg( false, "Unable to find creation function for portal type." );
+ Warning( "CPortalRender::HandlePortalPlaybackMessage() unable to find creation function for portal type: %s\n", szType );
+ }
+ else
+ {
+ pPortal->m_bIsPlaybackPortal = true;
+ int k = m_RecordedPortals.AddToTail( );
+ m_RecordedPortals[k].m_pActivePortal = pPortal;
+ m_RecordedPortals[k].m_nPortalId = nPortalId;
+ m_RecordedPortals[k].m_pPlaybackRenderable = pRenderable;
+ AddPortal( pPortal );
+ }
+ }
+ else
+ {
+ m_RecordedPortals[nIndex].m_pPlaybackRenderable = pRenderable;
+ }
+ foundIds.AddToTail( nPortalId );
+ }
+
+ // Delete portals that didn't appear in the list
+ int nFoundCount = foundIds.Count();
+ int nCount = m_RecordedPortals.Count();
+ for ( int i = nCount; --i >= 0; )
+ {
+ int j;
+ for ( j = 0; j < nFoundCount; ++j )
+ {
+ if ( foundIds[j] == m_RecordedPortals[i].m_nPortalId )
+ break;
+ }
+
+ if ( j == nFoundCount )
+ {
+ RemovePortal( m_RecordedPortals[i].m_pActivePortal );
+ delete m_RecordedPortals[i].m_pActivePortal;
+ m_RecordedPortals.FastRemove(i);
+ }
+ }
+
+ // Iterate through all the portal ids of all the portals in the keyvalues message
+ for ( KeyValues *pCurr = pKeyValues->GetFirstTrueSubKey(); pCurr; pCurr = pCurr->GetNextTrueSubKey() )
+ {
+ // Update the state of the portals based on the recorded info
+ int nPortalId = pCurr->GetInt( "portalId" );
+ CPortalRenderable *pPortal = FindRecordedPortal( nPortalId );
+ Assert( pPortal );
+
+ pPortal->HandlePortalPlaybackMessage( pCurr );
+ }
+
+ // Make the portals update their internal state
+ /*nCount = m_RecordedPortals.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_RecordedPortals[i].m_pActivePortal->PortalMoved();
+ m_RecordedPortals[i].m_pActivePortal->ComputeLinkMatrix();
+ }*/
+}
+
+
+void CPortalRender::AddPortalCreationFunc( const char *szPortalType, PortalRenderableCreationFunc creationFunc )
+{
+#ifdef _DEBUG
+ for( int i = m_PortalRenderableCreators.Count(); --i >= 0; )
+ {
+ AssertMsg( FStrEq( m_PortalRenderableCreators[i].portalType.String(), szPortalType ) == false, "Multiple portal renderable creation functions for same type of portal renderable." );
+ }
+#endif
+
+ PortalRenderableCreationFunction_t temp;
+ temp.creationFunc = creationFunc;
+ temp.portalType.Set( szPortalType );
+
+ m_PortalRenderableCreators.AddToTail( temp );
+}
+
+bool Recursive_IsPortalViewID( PortalViewIDNode_t *pNode, view_id_t id )
+{
+ if ( pNode->iPrimaryViewID == id )
+ return true;
+
+ for( int i = pNode->ChildNodes.Count(); --i >= 0; )
+ {
+ PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
+ if( pChildNode )
+ {
+ return Recursive_IsPortalViewID( pChildNode, id );
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tests the parameter view ID against ID's used by portal pixel vis queries
+// Input : id - id tested against used portal view ids
+// Output : Returns true if id matches an ID used by a portal, or it's recursive sub portals
+//-----------------------------------------------------------------------------
+bool CPortalRender::IsPortalViewID( view_id_t id )
+{
+ if ( id == m_HeadPortalViewIDNode.iPrimaryViewID )
+ return true;
+
+ for ( int i = 0; i < MAX_PORTAL_RECURSIVE_VIEWS; ++i )
+ {
+ PortalViewIDNode_t* pNode = m_PortalViewIDNodeChain[i];
+ if ( pNode )
+ {
+ // recursively search child nodes, they get their own ids.
+ if ( Recursive_IsPortalViewID( pNode, id ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+
+
+
+