Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1178 lines
42 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "PortalRender.h"
  9. #include "clienteffectprecachesystem.h"
  10. #include "view.h"
  11. #include "c_pixel_visibility.h"
  12. #include "glow_overlay.h"
  13. #include "portal_render_targets.h" //depth doubler
  14. #include "materialsystem/itexture.h"
  15. #include "toolframework/itoolframework.h"
  16. #include "tier1/KeyValues.h"
  17. #include "view_scene.h"
  18. #include "viewrender.h"
  19. #include "vprof.h"
  20. CLIENTEFFECT_REGISTER_BEGIN( PrecachePortalDrawingMaterials )
  21. CLIENTEFFECT_MATERIAL( "shadertest/wireframe" )
  22. CLIENTEFFECT_MATERIAL( "engine/writez_model" )
  23. CLIENTEFFECT_MATERIAL( "engine/TranslucentVertexColor" )
  24. CLIENTEFFECT_REGISTER_END()
  25. #define TEMP_DISABLE_PORTAL_VIS_QUERY
  26. 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" );
  27. 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
  28. 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" );
  29. //-----------------------------------------------------------------------------
  30. //
  31. // Portal rendering management class
  32. //
  33. //-----------------------------------------------------------------------------
  34. static CPortalRender s_PortalRender;
  35. CPortalRender* g_pPortalRender = &s_PortalRender;
  36. //-------------------------------------------
  37. //Portal View ID Node helpers
  38. //-------------------------------------------
  39. CUtlVector<int> s_iFreedViewIDs; //when a view id node gets freed, it's primary view id gets added here
  40. PortalViewIDNode_t *AllocPortalViewIDNode( int iChildLinkCount )
  41. {
  42. PortalViewIDNode_t *pNode = new PortalViewIDNode_t; //for now we just new/delete
  43. int iFreedIDsCount = s_iFreedViewIDs.Count();
  44. if( iFreedIDsCount != 0 )
  45. {
  46. pNode->iPrimaryViewID = s_iFreedViewIDs.Tail();
  47. s_iFreedViewIDs.FastRemove( iFreedIDsCount - 1 );
  48. }
  49. else
  50. {
  51. static int iNewAllocationCounter = VIEW_ID_COUNT;
  52. pNode->iPrimaryViewID = iNewAllocationCounter;
  53. iNewAllocationCounter += 2; //2 to make room for skybox view ids
  54. }
  55. CMatRenderContextPtr pRenderContext( materials );
  56. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  57. pNode->occlusionQueryHandle = pRenderContext->CreateOcclusionQueryObject();
  58. #endif
  59. pNode->iOcclusionQueryPixelsRendered = -5;
  60. pNode->iWindowPixelsAtQueryTime = 0;
  61. pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
  62. if( iChildLinkCount != 0 )
  63. {
  64. pNode->ChildNodes.SetCount( iChildLinkCount );
  65. memset( pNode->ChildNodes.Base(), NULL, sizeof( PortalViewIDNode_t * ) * iChildLinkCount );
  66. }
  67. return pNode;
  68. }
  69. void FreePortalViewIDNode( PortalViewIDNode_t *pNode )
  70. {
  71. for( int i = pNode->ChildNodes.Count(); --i >= 0; )
  72. {
  73. if( pNode->ChildNodes[i] != NULL )
  74. FreePortalViewIDNode( pNode->ChildNodes[i] );
  75. }
  76. s_iFreedViewIDs.AddToTail( pNode->iPrimaryViewID );
  77. CMatRenderContextPtr pRenderContext( materials );
  78. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  79. pRenderContext->DestroyOcclusionQueryObject( pNode->occlusionQueryHandle );
  80. #endif
  81. delete pNode; //for now we just new/delete
  82. }
  83. void IncreasePortalViewIDChildLinkCount( PortalViewIDNode_t *pNode )
  84. {
  85. for( int i = pNode->ChildNodes.Count(); --i >= 0; )
  86. {
  87. if( pNode->ChildNodes[i] != NULL )
  88. IncreasePortalViewIDChildLinkCount( pNode->ChildNodes[i] );
  89. }
  90. pNode->ChildNodes.AddToTail( NULL );
  91. }
  92. void RemovePortalViewIDChildLinkIndex( PortalViewIDNode_t *pNode, int iRemoveIndex )
  93. {
  94. Assert( pNode->ChildNodes.Count() > iRemoveIndex );
  95. if( pNode->ChildNodes[iRemoveIndex] != NULL )
  96. {
  97. FreePortalViewIDNode( pNode->ChildNodes[iRemoveIndex] );
  98. pNode->ChildNodes[iRemoveIndex] = NULL;
  99. }
  100. //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
  101. pNode->ChildNodes[iRemoveIndex] = pNode->ChildNodes.Tail();
  102. pNode->ChildNodes.Remove( pNode->ChildNodes.Count() - 1 );
  103. for( int i = pNode->ChildNodes.Count(); --i >= 0; )
  104. {
  105. if( pNode->ChildNodes[i] )
  106. RemovePortalViewIDChildLinkIndex( pNode->ChildNodes[i], iRemoveIndex );
  107. }
  108. }
  109. //-----------------------------------------------------------------------------
  110. //
  111. // Active Portal class
  112. //
  113. //-----------------------------------------------------------------------------
  114. CPortalRenderable::CPortalRenderable( void ) :
  115. m_bIsPlaybackPortal( false )
  116. {
  117. m_matrixThisToLinked.Identity();
  118. //Portal view ID indexing setup
  119. IncreasePortalViewIDChildLinkCount( &s_PortalRender.m_HeadPortalViewIDNode );
  120. m_iPortalViewIDNodeIndex = s_PortalRender.m_AllPortals.AddToTail( this );
  121. }
  122. CPortalRenderable::~CPortalRenderable( void )
  123. {
  124. int iLast = s_PortalRender.m_AllPortals.Count() - 1;
  125. //update the soon-to-be-transplanted portal's index
  126. s_PortalRender.m_AllPortals[iLast]->m_iPortalViewIDNodeIndex = m_iPortalViewIDNodeIndex;
  127. //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
  128. s_PortalRender.m_AllPortals[m_iPortalViewIDNodeIndex] = s_PortalRender.m_AllPortals.Tail();
  129. s_PortalRender.m_AllPortals.Remove( iLast );
  130. RemovePortalViewIDChildLinkIndex( &s_PortalRender.m_HeadPortalViewIDNode, m_iPortalViewIDNodeIndex ); //does the same transplant operation as above to all portal view id nodes
  131. }
  132. void CPortalRenderable::BeginPortalPixelVisibilityQuery( void )
  133. {
  134. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  135. return;
  136. #endif
  137. 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
  138. return;
  139. PortalViewIDNode_t *pCurrentPortalViewNode = g_pPortalRender->m_PortalViewIDNodeChain[g_pPortalRender->m_iViewRecursionLevel]->ChildNodes[m_iPortalViewIDNodeIndex];
  140. if( pCurrentPortalViewNode )
  141. {
  142. CMatRenderContextPtr pRenderContext( materials );
  143. pRenderContext->BeginOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
  144. int iX, iY, iWidth, iHeight;
  145. pRenderContext->GetViewport( iX, iY, iWidth, iHeight );
  146. pCurrentPortalViewNode->iWindowPixelsAtQueryTime = iWidth * iHeight;
  147. }
  148. }
  149. void CPortalRenderable::EndPortalPixelVisibilityQuery( void )
  150. {
  151. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  152. return;
  153. #endif
  154. 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
  155. return;
  156. PortalViewIDNode_t *pCurrentPortalViewNode = g_pPortalRender->m_PortalViewIDNodeChain[g_pPortalRender->m_iViewRecursionLevel]->ChildNodes[m_iPortalViewIDNodeIndex];
  157. if( pCurrentPortalViewNode )
  158. {
  159. CMatRenderContextPtr pRenderContext( materials );
  160. pRenderContext->EndOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
  161. }
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Constructor
  165. //-----------------------------------------------------------------------------
  166. CPortalRender::CPortalRender()
  167. : m_MaterialsAccess( m_Materials )
  168. {
  169. 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
  170. m_iViewRecursionLevel = 0;
  171. m_pRenderingViewForPortal = NULL;
  172. m_pRenderingViewExitPortal = NULL;
  173. m_PortalViewIDNodeChain[0] = &m_HeadPortalViewIDNode;
  174. }
  175. void CPortalRender::LevelInitPreEntity()
  176. {
  177. // refresh materials - not sure if this needs to be done every level
  178. m_Materials.m_Wireframe.Init( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS );
  179. m_Materials.m_WriteZ_Model.Init( "engine/writez_model", TEXTURE_GROUP_CLIENT_EFFECTS );
  180. m_Materials.m_TranslucentVertexColor.Init( "engine/TranslucentVertexColor", TEXTURE_GROUP_CLIENT_EFFECTS );
  181. }
  182. void CPortalRender::LevelShutdownPreEntity()
  183. {
  184. int nCount = m_RecordedPortals.Count();
  185. for ( int i = 0; i < nCount; ++i )
  186. {
  187. delete m_RecordedPortals[i].m_pActivePortal;
  188. }
  189. m_RecordedPortals.RemoveAll();
  190. }
  191. //-----------------------------------------------------------------------------
  192. // only use stencils when it's requested, and expensive water won't cause it to freak out
  193. //-----------------------------------------------------------------------------
  194. bool CPortalRender::ShouldUseStencilsToRenderPortals( ) const
  195. {
  196. // only use stencils when it's requested, and available
  197. return r_portal_use_stencils.GetBool() && ( materials->StencilBufferBits() != 0 );
  198. }
  199. int CPortalRender::ShouldForceCheaperWaterLevel() const
  200. {
  201. if( r_forcecheapwater.GetBool() )
  202. return 0;
  203. if( m_iViewRecursionLevel > 0 )
  204. {
  205. if( portalrendertargets->GetWaterReflectionTextureForStencilDepth( m_iViewRecursionLevel ) == NULL )
  206. return 0;
  207. PortalViewIDNode_t *pPixelVisNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel - 1]->ChildNodes[m_pRenderingViewForPortal->m_iPortalViewIDNodeIndex];
  208. if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized >= 0.0f )
  209. {
  210. if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.005f )
  211. return 0;
  212. if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.02f )
  213. return 1;
  214. if( pPixelVisNode->fScreenFilledByPortalSurfaceLastFrame_Normalized < 0.05f )
  215. return 2;
  216. }
  217. }
  218. return 3;
  219. }
  220. bool CPortalRender::ShouldObeyStencilForClears() const
  221. {
  222. return (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals();
  223. }
  224. void CPortalRender::WaterRenderingHandler_PreReflection() const
  225. {
  226. if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
  227. {
  228. CMatRenderContextPtr pRenderContext( materials );
  229. pRenderContext->SetStencilEnable( false );
  230. }
  231. }
  232. void CPortalRender::WaterRenderingHandler_PostReflection() const
  233. {
  234. if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
  235. {
  236. CMatRenderContextPtr pRenderContext( materials );
  237. pRenderContext->SetStencilEnable( true );
  238. }
  239. }
  240. void CPortalRender::WaterRenderingHandler_PreRefraction() const
  241. {
  242. if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
  243. {
  244. CMatRenderContextPtr pRenderContext( materials );
  245. pRenderContext->SetStencilEnable( false );
  246. }
  247. }
  248. void CPortalRender::WaterRenderingHandler_PostRefraction() const
  249. {
  250. if( (m_iViewRecursionLevel > 0) && ShouldUseStencilsToRenderPortals() )
  251. {
  252. CMatRenderContextPtr pRenderContext( materials );
  253. pRenderContext->SetStencilEnable( true );
  254. }
  255. }
  256. void Recursive_UpdatePortalPixelVisibility( PortalViewIDNode_t *pNode, IMatRenderContext *pRenderContext )
  257. {
  258. if( pNode->iWindowPixelsAtQueryTime > 0 )
  259. {
  260. if( pNode->iOcclusionQueryPixelsRendered < -1 )
  261. {
  262. //First couple queries. We seem to be getting bogus 0's on the first queries sometimes. ignore the results.
  263. ++pNode->iOcclusionQueryPixelsRendered;
  264. pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
  265. }
  266. else
  267. {
  268. pNode->iOcclusionQueryPixelsRendered = pRenderContext->OcclusionQuery_GetNumPixelsRendered( pNode->occlusionQueryHandle );
  269. pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = ((float)pNode->iOcclusionQueryPixelsRendered) / ((float)pNode->iWindowPixelsAtQueryTime);
  270. }
  271. }
  272. else
  273. {
  274. pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
  275. }
  276. pNode->iWindowPixelsAtQueryTime = 0;
  277. for( int i = pNode->ChildNodes.Count(); --i >= 0; )
  278. {
  279. PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
  280. if( pChildNode )
  281. Recursive_UpdatePortalPixelVisibility( pChildNode, pRenderContext );
  282. }
  283. }
  284. void CPortalRender::UpdatePortalPixelVisibility( void )
  285. {
  286. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  287. return;
  288. #endif
  289. if( m_iViewRecursionLevel != 0 )
  290. return;
  291. IMatRenderContext *pRenderContext = materials->GetRenderContext();
  292. //CMatRenderContextPtr pRenderContext( materials );
  293. for( int i = m_HeadPortalViewIDNode.ChildNodes.Count(); --i >= 0; )
  294. {
  295. PortalViewIDNode_t *pChildNode = m_HeadPortalViewIDNode.ChildNodes[i];
  296. if( pChildNode )
  297. Recursive_UpdatePortalPixelVisibility( pChildNode, pRenderContext );
  298. }
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Invalidates pixel visibility data for all portals for this next frame.
  302. //-----------------------------------------------------------------------------
  303. void Recursive_InvalidatePortalPixelVis( PortalViewIDNode_t *pNode )
  304. {
  305. pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized = -1.0f;
  306. pNode->iOcclusionQueryPixelsRendered = -5;
  307. pNode->iWindowPixelsAtQueryTime = 0;
  308. for( int i = pNode->ChildNodes.Count(); --i >= 0; )
  309. {
  310. PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
  311. if( pChildNode )
  312. Recursive_InvalidatePortalPixelVis( pChildNode );
  313. }
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Preserves pixel visibility data when view id's are getting swapped around
  317. //-----------------------------------------------------------------------------
  318. void CPortalRender::EnteredPortal( CPortalRenderable *pEnteredPortal )
  319. {
  320. CPortalRenderable *pExitPortal = pEnteredPortal->GetLinkedPortal();
  321. Assert( pExitPortal != NULL );
  322. if ( pExitPortal == NULL )
  323. return;
  324. int iNodeLinkCount = m_HeadPortalViewIDNode.ChildNodes.Count();
  325. PortalViewIDNode_t *pNewHead = m_HeadPortalViewIDNode.ChildNodes[pEnteredPortal->m_iPortalViewIDNodeIndex];
  326. m_HeadPortalViewIDNode.ChildNodes[pEnteredPortal->m_iPortalViewIDNodeIndex] = NULL;
  327. //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)
  328. PortalViewIDNode_t *pExitPortalsNewNode = AllocPortalViewIDNode( iNodeLinkCount );
  329. {
  330. for( int i = 0; i != iNodeLinkCount; ++i )
  331. {
  332. pExitPortalsNewNode->ChildNodes[i] = m_HeadPortalViewIDNode.ChildNodes[i];
  333. m_HeadPortalViewIDNode.ChildNodes[i] = NULL;
  334. }
  335. PixelVisibility_ShiftVisibilityViews( VIEW_MAIN, pExitPortalsNewNode->iPrimaryViewID );
  336. PixelVisibility_ShiftVisibilityViews( VIEW_3DSKY, pExitPortalsNewNode->iPrimaryViewID + 1 );
  337. }
  338. if( pNewHead ) //it's possible we entered a portal we couldn't see through
  339. {
  340. Assert( pNewHead->ChildNodes.Count() == m_HeadPortalViewIDNode.ChildNodes.Count() );
  341. Assert( pNewHead->ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] == NULL ); //seeing out of an exit portal back into itself should be impossible
  342. for( int i = 0; i != iNodeLinkCount; ++i )
  343. {
  344. m_HeadPortalViewIDNode.ChildNodes[i] = pNewHead->ChildNodes[i];
  345. pNewHead->ChildNodes[i] = NULL; //going to be freeing the node in a minute, don't want to kill transplanted children
  346. }
  347. //Since the primary views will always be 0 and 1, we have to shift results instead of replacing the id's
  348. PixelVisibility_ShiftVisibilityViews( pNewHead->iPrimaryViewID, VIEW_MAIN );
  349. PixelVisibility_ShiftVisibilityViews( pNewHead->iPrimaryViewID + 1, VIEW_3DSKY );
  350. FreePortalViewIDNode( pNewHead );
  351. }
  352. Assert( m_HeadPortalViewIDNode.ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] == NULL ); //asserted above in pNewHead code, but call me paranoid
  353. m_HeadPortalViewIDNode.ChildNodes[pExitPortal->m_iPortalViewIDNodeIndex] = pExitPortalsNewNode;
  354. //Because pixel visibility is based off of *last* frame's visibility. We can get cases where a certain portal
  355. //wasn't visible last frame, but is takes up most of the screen this frame.
  356. //Set all portal pixel visibility to unknown visibility.
  357. for( int i = m_HeadPortalViewIDNode.ChildNodes.Count(); --i >= 0; )
  358. {
  359. PortalViewIDNode_t *pChildNode = m_HeadPortalViewIDNode.ChildNodes[i];
  360. if( pChildNode )
  361. Recursive_InvalidatePortalPixelVis( pChildNode );
  362. }
  363. }
  364. bool CPortalRender::DrawPortalsUsingStencils( CViewRender *pViewRender )
  365. {
  366. VPROF( "CPortalRender::DrawPortalsUsingStencils" );
  367. if( !ShouldUseStencilsToRenderPortals() )
  368. return false;
  369. int iDrawFlags = pViewRender->GetDrawFlags();
  370. if ( (iDrawFlags & DF_RENDER_REFLECTION) != 0 )
  371. return false;
  372. if ( ((iDrawFlags & DF_CLIP_Z) != 0) && ((iDrawFlags & DF_CLIP_BELOW) == 0) ) //clipping above the water height
  373. return false;
  374. int iNumRenderablePortals = m_ActivePortals.Count();
  375. // This loop is necessary because tools can suppress rendering without telling the portal system
  376. CUtlVector< CPortalRenderable* > actualActivePortals( 0, iNumRenderablePortals );
  377. for ( int i = 0; i < iNumRenderablePortals; ++i )
  378. {
  379. CPortalRenderable *pPortalRenderable = m_ActivePortals[i];
  380. C_BaseEntity *pPairedEntity = pPortalRenderable->PortalRenderable_GetPairedEntity();
  381. bool bIsVisible = (pPairedEntity == NULL) || (pPairedEntity->IsVisible() && pPairedEntity->ShouldDraw()); //either unknown visibility or definitely visible.
  382. if ( !pPortalRenderable->m_bIsPlaybackPortal )
  383. {
  384. if ( !bIsVisible )
  385. {
  386. //can't see through the portal, free up it's view id node for use elsewhere
  387. if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] != NULL )
  388. {
  389. FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] );
  390. m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortalRenderable->m_iPortalViewIDNodeIndex] = NULL;
  391. }
  392. continue;
  393. }
  394. }
  395. actualActivePortals.AddToTail( m_ActivePortals[i] );
  396. }
  397. iNumRenderablePortals = actualActivePortals.Count();
  398. if( iNumRenderablePortals == 0 )
  399. return false;
  400. const int iMaxDepth = MIN( r_portal_stencil_depth.GetInt(), MIN( MAX_PORTAL_RECURSIVE_VIEWS, (1 << materials->StencilBufferBits()) ) - 1 );
  401. if( m_iViewRecursionLevel >= iMaxDepth ) //can't support any more views
  402. {
  403. m_iRemainingPortalViewDepth = 0; //special case handler for max depth 0 cases
  404. for( int i = 0; i != iNumRenderablePortals; ++i )
  405. {
  406. CPortalRenderable *pCurrentPortal = actualActivePortals[i];
  407. pCurrentPortal->DrawPortal();
  408. }
  409. return false;
  410. }
  411. m_iRemainingPortalViewDepth = (iMaxDepth - m_iViewRecursionLevel) - 1;
  412. CMatRenderContextPtr pRenderContext( materials );
  413. pRenderContext->Flush( true ); //to prevent screwing up the last opaque object
  414. //queued mode makes us pass the barrier of just noticeable difference when using a previous frame's occlusion as a draw skip check
  415. bool bIsQueuedMode = (materials->GetThreadMode() == MATERIAL_QUEUED_THREADED);
  416. const CViewSetup *pViewSetup = pViewRender->GetViewSetup();
  417. m_RecursiveViewSetups[m_iViewRecursionLevel] = *pViewSetup;
  418. CViewSetup ViewBackup;// = *pViewSetup; //backup the view, we'll need to restore it
  419. memcpy( &ViewBackup, pViewSetup, sizeof( CViewSetup ) );
  420. Vector ptCameraOrigin = pViewSetup->origin;
  421. Vector vCameraForward;
  422. AngleVectors( pViewSetup->angles, &vCameraForward, NULL, NULL );
  423. int iX, iY, iWidth, iHeight;
  424. pRenderContext->GetViewport( iX, iY, iWidth, iHeight );
  425. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  426. int iScreenPixelCount = iWidth * iHeight;
  427. #endif
  428. bool bRebuildDrawListsWhenDone = false;
  429. int iParentLevelStencilReferenceValue = m_iViewRecursionLevel;
  430. int iStencilReferenceValue = iParentLevelStencilReferenceValue + 1;
  431. if( m_iViewRecursionLevel == 0 ) //first entry into the stencil drawing
  432. {
  433. pRenderContext->SetStencilEnable( true );
  434. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_ALWAYS );
  435. pRenderContext->SetStencilPassOperation( STENCILOPERATION_REPLACE );
  436. pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  437. pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  438. pRenderContext->SetStencilTestMask( 0xFF );
  439. pRenderContext->SetStencilWriteMask( 0xFF );
  440. pRenderContext->SetStencilReferenceValue( 0 );
  441. m_RecursiveViewComplexFrustums[0].RemoveAll(); //clear any garbage leftover in the complex frustums from last frame
  442. }
  443. if( m_RecursiveViewComplexFrustums[m_iViewRecursionLevel].Count() == 0 )
  444. {
  445. //nothing in the complex frustum from the current view, copy the standard frustum in
  446. m_RecursiveViewComplexFrustums[m_iViewRecursionLevel].AddMultipleToTail( FRUSTUM_NUMPLANES, pViewRender->GetFrustum() );
  447. }
  448. for( int i = 0; i != iNumRenderablePortals; ++i )
  449. {
  450. CPortalRenderable *pCurrentPortal = actualActivePortals[i];
  451. m_RecursiveViewComplexFrustums[m_iViewRecursionLevel + 1].RemoveAll(); //clear any previously stored complex frustum
  452. if( (pCurrentPortal->GetLinkedPortal() == NULL) ||
  453. (pCurrentPortal == m_pRenderingViewExitPortal) ||
  454. (pCurrentPortal->ShouldUpdatePortalView_BasedOnView( *pViewSetup, m_RecursiveViewComplexFrustums[m_iViewRecursionLevel] ) == false) )
  455. {
  456. //can't see through the portal, free up it's view id node for use elsewhere
  457. if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] != NULL )
  458. {
  459. FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] );
  460. m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = NULL;
  461. }
  462. continue;
  463. }
  464. Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
  465. if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] == NULL )
  466. m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = AllocPortalViewIDNode( m_HeadPortalViewIDNode.ChildNodes.Count() );
  467. PortalViewIDNode_t *pCurrentPortalViewNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
  468. // Step 0, Allow for special effects to happen before cutting a hole
  469. {
  470. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
  471. pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
  472. pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  473. pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  474. pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
  475. pCurrentPortal->DrawPreStencilMask();
  476. }
  477. //step 1, write out the stencil values (and colors if you want, but really not necessary)
  478. {
  479. //pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
  480. pRenderContext->SetStencilPassOperation( STENCILOPERATION_INCR );
  481. //pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  482. //pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  483. //pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
  484. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  485. pRenderContext->BeginOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
  486. pCurrentPortalViewNode->iWindowPixelsAtQueryTime = iScreenPixelCount;
  487. #endif
  488. pCurrentPortal->DrawStencilMask();
  489. #ifndef TEMP_DISABLE_PORTAL_VIS_QUERY
  490. pRenderContext->EndOcclusionQueryDrawing( pCurrentPortalViewNode->occlusionQueryHandle );
  491. #endif
  492. }
  493. //see if we can skip the heavy lifting due to low visibility
  494. if ( bIsQueuedMode || //don't use pixel visibly as a skip check in queued mode, the data is simply too old.
  495. pCurrentPortal->ShouldUpdatePortalView_BasedOnPixelVisibility( pCurrentPortalViewNode->fScreenFilledByPortalSurfaceLastFrame_Normalized ) )
  496. {
  497. //step 2, clear the depth buffer in stencil areas so we can render a new scene to them
  498. {
  499. pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
  500. pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
  501. pRenderContext->ClearBuffersObeyStencil( false, true );
  502. }
  503. //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)
  504. {
  505. bRebuildDrawListsWhenDone = true;
  506. MaterialFogMode_t fogModeBackup = pRenderContext->GetFogMode();
  507. unsigned char fogColorBackup[4];
  508. pRenderContext->GetFogColor( fogColorBackup );
  509. float fFogStartBackup, fFogEndBackup, fFogZBackup;
  510. pRenderContext->GetFogDistances( &fFogStartBackup, &fFogEndBackup, &fFogZBackup );
  511. CGlowOverlay::BackupSkyOverlayData( m_iViewRecursionLevel );
  512. Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
  513. m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
  514. pCurrentPortal->RenderPortalViewToBackBuffer( pViewRender, *pViewSetup );
  515. m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = NULL;
  516. CGlowOverlay::RestoreSkyOverlayData( m_iViewRecursionLevel );
  517. memcpy( (void *)pViewSetup, &ViewBackup, sizeof( CViewSetup ) );
  518. pViewRender->m_pActiveRenderer->EnableWorldFog();
  519. pRenderContext->FogMode( fogModeBackup );
  520. pRenderContext->FogColor3ubv( fogColorBackup );
  521. pRenderContext->FogStart( fFogStartBackup );
  522. pRenderContext->FogEnd( fFogEndBackup );
  523. pRenderContext->SetFogZ( fFogZBackup );
  524. //do a full reset of what we think the stencil operations are in case the recursive calls got weird
  525. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
  526. pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
  527. pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  528. pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  529. pRenderContext->SetStencilTestMask( 0xFF );
  530. pRenderContext->SetStencilWriteMask( 0xFF );
  531. pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
  532. }
  533. //step 4, patch up the fact that we just made a hole in the wall because it's not *really* a hole at all
  534. {
  535. pCurrentPortal->DrawPostStencilFixes();
  536. }
  537. }
  538. //step 5, restore the stencil mask to the parent level
  539. {
  540. pRenderContext->SetStencilReferenceValue( iStencilReferenceValue );
  541. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
  542. pRenderContext->SetStencilPassOperation( STENCILOPERATION_DECR );
  543. pRenderContext->SetStencilFailOperation( STENCILOPERATION_KEEP );
  544. pRenderContext->SetStencilZFailOperation( STENCILOPERATION_KEEP );
  545. pRenderContext->PerformFullScreenStencilOperation();
  546. }
  547. }
  548. //step 6, go back to non-stencil rendering mode in preparation to resume normal scene rendering
  549. if( m_iViewRecursionLevel == 0 )
  550. {
  551. Assert( m_pRenderingViewForPortal == NULL );
  552. Assert( m_pRenderingViewExitPortal == NULL );
  553. m_pRenderingViewExitPortal = NULL;
  554. m_pRenderingViewForPortal = NULL;
  555. pRenderContext->SetStencilEnable( false );
  556. pRenderContext->SetStencilCompareFunction(STENCILCOMPARISONFUNCTION_NEVER);
  557. pRenderContext->SetStencilPassOperation(STENCILOPERATION_KEEP);
  558. pRenderContext->SetStencilFailOperation(STENCILOPERATION_KEEP);
  559. pRenderContext->SetStencilZFailOperation(STENCILOPERATION_KEEP);
  560. pRenderContext->SetStencilTestMask( 0xFF );
  561. pRenderContext->SetStencilWriteMask( 0xFF );
  562. pRenderContext->SetStencilReferenceValue( 0 );
  563. m_RecursiveViewComplexFrustums[0].RemoveAll();
  564. }
  565. else
  566. {
  567. pRenderContext->SetStencilReferenceValue( iParentLevelStencilReferenceValue );
  568. pRenderContext->SetStencilCompareFunction( STENCILCOMPARISONFUNCTION_EQUAL );
  569. pRenderContext->SetStencilPassOperation( STENCILOPERATION_KEEP );
  570. }
  571. if( bRebuildDrawListsWhenDone )
  572. {
  573. memcpy( (void *)pViewSetup, &ViewBackup, sizeof( CViewSetup ) ); //if we don't restore this, the view is permanently altered (in mid render of an existing scene)
  574. }
  575. pRenderContext->Flush( true ); //just in case
  576. ++m_iRemainingPortalViewDepth;
  577. for( int i = 0; i != iNumRenderablePortals; ++i )
  578. {
  579. CPortalRenderable *pCurrentPortal = actualActivePortals[i];
  580. pCurrentPortal->DrawPortal();
  581. }
  582. return bRebuildDrawListsWhenDone;
  583. }
  584. #ifdef _DEBUG
  585. extern bool g_bRenderingCameraView;
  586. #endif
  587. void CPortalRender::DrawPortalsToTextures( CViewRender *pViewRender, const CViewSetup &cameraView )
  588. {
  589. if( ShouldUseStencilsToRenderPortals() )
  590. return;
  591. /*if ( (pViewRender->GetDrawFlags() & DF_RENDER_REFLECTION) != 0 )
  592. return;*/
  593. m_iRemainingPortalViewDepth = 1;
  594. m_iViewRecursionLevel = 0;
  595. m_pRenderingViewForPortal = NULL;
  596. m_pRenderingViewExitPortal = NULL;
  597. m_RecursiveViewSetups[m_iViewRecursionLevel] = cameraView;
  598. m_RecursiveViewComplexFrustums[0].RemoveAll(); //clear any garbage leftover in the complex frustums from last frame
  599. m_RecursiveViewComplexFrustums[0].AddMultipleToTail( FRUSTUM_NUMPLANES, pViewRender->GetFrustum() );
  600. #ifdef _DEBUG
  601. g_bRenderingCameraView = true;
  602. #endif
  603. int iNumRenderablePortals = g_pPortalRender->m_ActivePortals.Count();
  604. Vector ptCameraOrigin = cameraView.origin;
  605. //an extraneous push to update the frustum
  606. render->Push3DView( cameraView, 0, NULL, pViewRender->GetFrustum() );
  607. for( int i = 0; i != iNumRenderablePortals; ++i )
  608. {
  609. CPortalRenderable *pCurrentPortal = g_pPortalRender->m_ActivePortals[i];
  610. if( (pCurrentPortal->ShouldUpdatePortalView_BasedOnView( cameraView, m_RecursiveViewComplexFrustums[m_iViewRecursionLevel] ) == false) ||
  611. (pCurrentPortal->GetLinkedPortal() == NULL) )
  612. {
  613. //can't see through the portal, free up it's view id node for use elsewhere
  614. if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] != NULL )
  615. {
  616. FreePortalViewIDNode( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] );
  617. m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = NULL;
  618. }
  619. continue;
  620. }
  621. Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes.Count() > pCurrentPortal->m_iPortalViewIDNodeIndex );
  622. if( m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] == NULL )
  623. m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex] = AllocPortalViewIDNode( m_HeadPortalViewIDNode.ChildNodes.Count() );
  624. m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pCurrentPortal->m_iPortalViewIDNodeIndex];
  625. pCurrentPortal->RenderPortalViewToTexture( pViewRender, cameraView );
  626. m_PortalViewIDNodeChain[m_iViewRecursionLevel + 1] = NULL;
  627. }
  628. render->PopView( pViewRender->GetFrustum() );
  629. m_iRemainingPortalViewDepth = 1;
  630. m_iViewRecursionLevel = 0;
  631. Assert( m_pRenderingViewForPortal == NULL );
  632. Assert( m_pRenderingViewExitPortal == NULL );
  633. m_pRenderingViewForPortal = NULL;
  634. m_pRenderingViewExitPortal = NULL;
  635. #ifdef _DEBUG
  636. g_bRenderingCameraView = false;
  637. #endif
  638. }
  639. void CPortalRender::AddPortal( CPortalRenderable *pPortal )
  640. {
  641. for( int i = m_ActivePortals.Count(); --i >= 0; )
  642. {
  643. if( m_ActivePortals[i] == pPortal )
  644. return;
  645. }
  646. m_ActivePortals.AddToTail( pPortal );
  647. }
  648. void CPortalRender::RemovePortal( CPortalRenderable *pPortal )
  649. {
  650. for( int i = m_ActivePortals.Count(); --i >= 0; )
  651. {
  652. if( m_ActivePortals[i] == pPortal )
  653. {
  654. m_ActivePortals.FastRemove( i );
  655. return;
  656. }
  657. }
  658. }
  659. //-----------------------------------------------------------------------------
  660. // Are we currently rendering a portal?
  661. //-----------------------------------------------------------------------------
  662. bool CPortalRender::IsRenderingPortal() const
  663. {
  664. return m_pRenderingViewForPortal != NULL;
  665. }
  666. //-----------------------------------------------------------------------------
  667. // Returns view recursion level
  668. //-----------------------------------------------------------------------------
  669. int CPortalRender::GetViewRecursionLevel() const
  670. {
  671. return m_iViewRecursionLevel;
  672. }
  673. //-----------------------------------------------------------------------------
  674. //normalized for how many of the screen's possible pixels it takes up, less than zero indicates a lack of data from last frame
  675. //-----------------------------------------------------------------------------
  676. float CPortalRender::GetPixelVisilityForPortalSurface( const CPortalRenderable *pPortal ) const
  677. {
  678. PortalViewIDNode_t *pNode = m_PortalViewIDNodeChain[m_iViewRecursionLevel]->ChildNodes[pPortal->m_iPortalViewIDNodeIndex];
  679. if( pNode )
  680. return pNode->fScreenFilledByPortalSurfaceLastFrame_Normalized;
  681. return -1.0f;
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Methods to query about the exit portal associated with the currently rendering portal
  685. //-----------------------------------------------------------------------------
  686. const Vector &CPortalRender::GetExitPortalFogOrigin() const
  687. {
  688. return m_pRenderingViewExitPortal->GetFogOrigin();
  689. }
  690. void CPortalRender::ShiftFogForExitPortalView() const
  691. {
  692. if ( m_pRenderingViewExitPortal )
  693. {
  694. m_pRenderingViewExitPortal->ShiftFogForExitPortalView();
  695. }
  696. }
  697. void CPortalRenderable::ShiftFogForExitPortalView() const
  698. {
  699. CMatRenderContextPtr pRenderContext( materials );
  700. float fFogStart, fFogEnd, fFogZ;
  701. pRenderContext->GetFogDistances( &fFogStart, &fFogEnd, &fFogZ );
  702. Vector vFogOrigin = GetFogOrigin();
  703. Vector vCameraToExitPortal = vFogOrigin - CurrentViewOrigin();
  704. float fDistModifier = vCameraToExitPortal.Dot( CurrentViewForward() );
  705. fFogStart += fDistModifier;
  706. fFogEnd += fDistModifier;
  707. //fFogZ += something; //FIXME: find out what the hell to do with this
  708. pRenderContext->FogStart( fFogStart );
  709. pRenderContext->FogEnd( fFogEnd );
  710. pRenderContext->SetFogZ( fFogZ );
  711. }
  712. SkyboxVisibility_t CPortalRender::IsSkyboxVisibleFromExitPortal() const
  713. {
  714. return m_pRenderingViewExitPortal->SkyBoxVisibleFromPortal();
  715. }
  716. bool CPortalRender::DoesExitPortalViewIntersectWaterPlane( float waterZ, int leafWaterDataID ) const
  717. {
  718. return m_pRenderingViewExitPortal->DoesExitViewIntersectWaterPlane( waterZ, leafWaterDataID );
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Returns the remaining number of portals to render within other portals
  722. // lets portals know that they should do "end of the line" kludges to cover up that portals don't go infinitely recursive
  723. //-----------------------------------------------------------------------------
  724. int CPortalRender::GetRemainingPortalViewDepth() const
  725. {
  726. return m_iRemainingPortalViewDepth;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Returns the current View IDs
  730. //-----------------------------------------------------------------------------
  731. int CPortalRender::GetCurrentViewId() const
  732. {
  733. Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel] != NULL );
  734. #ifdef _DEBUG
  735. for( int i = 0; i != m_iViewRecursionLevel; ++i )
  736. {
  737. Assert( m_PortalViewIDNodeChain[i]->iPrimaryViewID != m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID );
  738. }
  739. #endif
  740. return m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID;
  741. }
  742. int CPortalRender::GetCurrentSkyboxViewId() const
  743. {
  744. Assert( m_PortalViewIDNodeChain[m_iViewRecursionLevel] != NULL );
  745. return m_PortalViewIDNodeChain[m_iViewRecursionLevel]->iPrimaryViewID + 1;
  746. }
  747. void OverlayCameraRenderTarget( const char *pszMaterialName, float flX, float flY, float w, float h ); //implemented in view_scene.cpp
  748. void CPortalRender::OverlayPortalRenderTargets( float w, float h )
  749. {
  750. OverlayCameraRenderTarget( "engine/debug_portal_1", 0,0, w,h );
  751. OverlayCameraRenderTarget( "engine/debug_portal_2", w+10,0, w,h );
  752. OverlayCameraRenderTarget( "engine/debug_water_reflect_0", 0, h+10, w,h );
  753. OverlayCameraRenderTarget( "engine/debug_water_reflect_1", w+10, h+10, w,h );
  754. OverlayCameraRenderTarget( "engine/debug_water_reflect_2", (w+10) * 2, h+10, w,h );
  755. OverlayCameraRenderTarget( "engine/debug_water_refract_0", 0, (h+10) * 2, w,h );
  756. OverlayCameraRenderTarget( "engine/debug_water_refract_1", w+10, (h+10) * 2, w,h );
  757. OverlayCameraRenderTarget( "engine/debug_water_refract_2", (w+10) * 2, (h+10) * 2, w,h );
  758. }
  759. void CPortalRender::UpdateDepthDoublerTexture( const CViewSetup &viewSetup )
  760. {
  761. bool bShouldUpdate = false;
  762. for( int i = m_ActivePortals.Count(); --i >= 0; )
  763. {
  764. CPortalRenderable *pPortal = m_ActivePortals[i];
  765. if( pPortal->ShouldUpdateDepthDoublerTexture( viewSetup ) )
  766. {
  767. bShouldUpdate = true;
  768. break;
  769. }
  770. }
  771. if( bShouldUpdate )
  772. {
  773. Rect_t srcRect;
  774. srcRect.x = viewSetup.x;
  775. srcRect.y = viewSetup.y;
  776. srcRect.width = viewSetup.width;
  777. srcRect.height = viewSetup.height;
  778. ITexture *pTexture = portalrendertargets->GetDepthDoublerTexture();
  779. CMatRenderContextPtr pRenderContext( materials );
  780. pRenderContext->CopyRenderTargetToTextureEx( pTexture, 0, &srcRect, NULL );
  781. }
  782. }
  783. //-----------------------------------------------------------------------------
  784. // Finds a recorded portal
  785. //-----------------------------------------------------------------------------
  786. int CPortalRender::FindRecordedPortalIndex( int nPortalId )
  787. {
  788. int nCount = m_RecordedPortals.Count();
  789. for ( int i = 0; i < nCount; ++i )
  790. {
  791. if ( m_RecordedPortals[i].m_nPortalId == nPortalId )
  792. return i;
  793. }
  794. return -1;
  795. }
  796. CPortalRenderable* CPortalRender::FindRecordedPortal( int nPortalId )
  797. {
  798. int nIndex = FindRecordedPortalIndex( nPortalId );
  799. return ( nIndex >= 0 ) ? m_RecordedPortals[nIndex].m_pActivePortal : NULL;
  800. }
  801. CPortalRenderable* CPortalRender::FindRecordedPortal( IClientRenderable *pRenderable )
  802. {
  803. int nCount = m_RecordedPortals.Count();
  804. for ( int i = 0; i < nCount; ++i )
  805. {
  806. if ( m_RecordedPortals[i].m_pPlaybackRenderable == pRenderable )
  807. return m_RecordedPortals[i].m_pActivePortal;
  808. }
  809. return NULL;
  810. }
  811. //-----------------------------------------------------------------------------
  812. // Handles a portal update message
  813. //-----------------------------------------------------------------------------
  814. void CPortalRender::HandlePortalPlaybackMessage( KeyValues *pKeyValues )
  815. {
  816. // Iterate through all the portal ids of all the portals in the keyvalues message
  817. CUtlVector<int> foundIds;
  818. for ( KeyValues *pCurr = pKeyValues->GetFirstTrueSubKey(); pCurr; pCurr = pCurr->GetNextTrueSubKey() )
  819. {
  820. // Create new area portals for those ids that don't exist
  821. int nPortalId = pCurr->GetInt( "portalId" );
  822. IClientRenderable *pRenderable = (IClientRenderable*)pCurr->GetPtr( "clientRenderable" );
  823. int nIndex = FindRecordedPortalIndex( nPortalId );
  824. if ( nIndex < 0 )
  825. {
  826. CPortalRenderable *pPortal = NULL;
  827. const char *szType = pCurr->GetString( "portalType", "flatBasic" ); //"flatBasic" being the type commonly found in "Portal" mod
  828. //search through registered creation functions for one that makes this type of portal
  829. for( int i = m_PortalRenderableCreators.Count(); --i >= 0; )
  830. {
  831. if( FStrEq( szType, m_PortalRenderableCreators[i].portalType.String() ) )
  832. {
  833. pPortal = m_PortalRenderableCreators[i].creationFunc();
  834. break;
  835. }
  836. }
  837. if( pPortal == NULL )
  838. {
  839. AssertMsg( false, "Unable to find creation function for portal type." );
  840. Warning( "CPortalRender::HandlePortalPlaybackMessage() unable to find creation function for portal type: %s\n", szType );
  841. }
  842. else
  843. {
  844. pPortal->m_bIsPlaybackPortal = true;
  845. int k = m_RecordedPortals.AddToTail( );
  846. m_RecordedPortals[k].m_pActivePortal = pPortal;
  847. m_RecordedPortals[k].m_nPortalId = nPortalId;
  848. m_RecordedPortals[k].m_pPlaybackRenderable = pRenderable;
  849. AddPortal( pPortal );
  850. }
  851. }
  852. else
  853. {
  854. m_RecordedPortals[nIndex].m_pPlaybackRenderable = pRenderable;
  855. }
  856. foundIds.AddToTail( nPortalId );
  857. }
  858. // Delete portals that didn't appear in the list
  859. int nFoundCount = foundIds.Count();
  860. int nCount = m_RecordedPortals.Count();
  861. for ( int i = nCount; --i >= 0; )
  862. {
  863. int j;
  864. for ( j = 0; j < nFoundCount; ++j )
  865. {
  866. if ( foundIds[j] == m_RecordedPortals[i].m_nPortalId )
  867. break;
  868. }
  869. if ( j == nFoundCount )
  870. {
  871. RemovePortal( m_RecordedPortals[i].m_pActivePortal );
  872. delete m_RecordedPortals[i].m_pActivePortal;
  873. m_RecordedPortals.FastRemove(i);
  874. }
  875. }
  876. // Iterate through all the portal ids of all the portals in the keyvalues message
  877. for ( KeyValues *pCurr = pKeyValues->GetFirstTrueSubKey(); pCurr; pCurr = pCurr->GetNextTrueSubKey() )
  878. {
  879. // Update the state of the portals based on the recorded info
  880. int nPortalId = pCurr->GetInt( "portalId" );
  881. CPortalRenderable *pPortal = FindRecordedPortal( nPortalId );
  882. Assert( pPortal );
  883. pPortal->HandlePortalPlaybackMessage( pCurr );
  884. }
  885. // Make the portals update their internal state
  886. /*nCount = m_RecordedPortals.Count();
  887. for ( int i = 0; i < nCount; ++i )
  888. {
  889. m_RecordedPortals[i].m_pActivePortal->PortalMoved();
  890. m_RecordedPortals[i].m_pActivePortal->ComputeLinkMatrix();
  891. }*/
  892. }
  893. void CPortalRender::AddPortalCreationFunc( const char *szPortalType, PortalRenderableCreationFunc creationFunc )
  894. {
  895. #ifdef _DEBUG
  896. for( int i = m_PortalRenderableCreators.Count(); --i >= 0; )
  897. {
  898. AssertMsg( FStrEq( m_PortalRenderableCreators[i].portalType.String(), szPortalType ) == false, "Multiple portal renderable creation functions for same type of portal renderable." );
  899. }
  900. #endif
  901. PortalRenderableCreationFunction_t temp;
  902. temp.creationFunc = creationFunc;
  903. temp.portalType.Set( szPortalType );
  904. m_PortalRenderableCreators.AddToTail( temp );
  905. }
  906. bool Recursive_IsPortalViewID( PortalViewIDNode_t *pNode, view_id_t id )
  907. {
  908. if ( pNode->iPrimaryViewID == id )
  909. return true;
  910. for( int i = pNode->ChildNodes.Count(); --i >= 0; )
  911. {
  912. PortalViewIDNode_t *pChildNode = pNode->ChildNodes[i];
  913. if( pChildNode )
  914. {
  915. return Recursive_IsPortalViewID( pChildNode, id );
  916. }
  917. }
  918. return false;
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose: Tests the parameter view ID against ID's used by portal pixel vis queries
  922. // Input : id - id tested against used portal view ids
  923. // Output : Returns true if id matches an ID used by a portal, or it's recursive sub portals
  924. //-----------------------------------------------------------------------------
  925. bool CPortalRender::IsPortalViewID( view_id_t id )
  926. {
  927. if ( id == m_HeadPortalViewIDNode.iPrimaryViewID )
  928. return true;
  929. for ( int i = 0; i < MAX_PORTAL_RECURSIVE_VIEWS; ++i )
  930. {
  931. PortalViewIDNode_t* pNode = m_PortalViewIDNodeChain[i];
  932. if ( pNode )
  933. {
  934. // recursively search child nodes, they get their own ids.
  935. if ( Recursive_IsPortalViewID( pNode, id ) )
  936. return true;
  937. }
  938. }
  939. return false;
  940. }