diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/portal/PortalRender.cpp | |
| download | archived-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.cpp | 1178 |
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; +} + + + + + + |