Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

623 lines
18 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "render_pch.h"
  8. #include "client.h"
  9. #include "debug_leafvis.h"
  10. #include "con_nprint.h"
  11. #include "tier0/fasttimer.h"
  12. #include "r_areaportal.h"
  13. #include "cmodel_engine.h"
  14. #include "con_nprint.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. ConVar r_ClipAreaPortals( "r_ClipAreaPortals", "1", FCVAR_CHEAT );
  18. ConVar r_DrawPortals( "r_DrawPortals", "0", FCVAR_CHEAT );
  19. ConVar r_ClipAreaFrustums( "r_ClipAreaFrustums", "1", FCVAR_CHEAT );
  20. CUtlVector<CPortalRect> g_PortalRects;
  21. bool g_bViewerInSolidSpace = false;
  22. // ------------------------------------------------------------------------------------ //
  23. // Classes.
  24. // ------------------------------------------------------------------------------------ //
  25. #define MAX_PORTAL_VERTS 32
  26. // ------------------------------------------------------------------------------------ //
  27. // Globals.
  28. // ------------------------------------------------------------------------------------ //
  29. // Visible areas from the client DLL + occluded areas using area portals.
  30. #if defined(_PS3)
  31. unsigned char g_RenderAreaBits[32] ALIGN16;
  32. #else
  33. unsigned char g_RenderAreaBits[32];
  34. #endif
  35. // Used to prevent it from coming back into portals while flowing through them.
  36. static unsigned char g_AreaStack[32];
  37. static uint32 g_AreaCounter[MAX_MAP_AREAS];
  38. static CPortalRect g_AreaRect[MAX_MAP_AREAS];
  39. // Frustums for each area for the current frame. Used to cull out leaves.
  40. CUtlVector< Frustum_t, CUtlMemoryAligned< Frustum_t,16 > > g_AreaFrustum;
  41. // List of areas marked visible this frame.
  42. static unsigned short g_VisibleAreas[MAX_MAP_AREAS];
  43. static int g_nVisibleAreas;
  44. // Tied to g_AreaCounter.
  45. static uint32 g_GlobalCounter = 1;
  46. // ------------------------------------------------------------------------------------ //
  47. // Functions.
  48. // ------------------------------------------------------------------------------------ //
  49. void R_Areaportal_LevelInit()
  50. {
  51. g_AreaFrustum.SetCount( host_state.worldbrush->m_nAreas );
  52. V_memset( g_AreaCounter, 0, sizeof(g_AreaCounter) );
  53. g_GlobalCounter = 1;
  54. }
  55. void R_Areaportal_LevelShutdown()
  56. {
  57. g_AreaFrustum.Purge();
  58. g_PortalRects.Purge();
  59. }
  60. static inline void R_SetBit( unsigned char *pBits, int bit )
  61. {
  62. pBits[bit>>3] |= (1 << (bit&7));
  63. }
  64. static inline void R_ClearBit( unsigned char *pBits, int bit )
  65. {
  66. pBits[bit>>3] &= ~(1 << (bit&7));
  67. }
  68. static inline unsigned char R_TestBit( unsigned char *pBits, int bit )
  69. {
  70. return pBits[bit>>3] & (1 << (bit&7));
  71. }
  72. struct portalclip_t
  73. {
  74. portalclip_t()
  75. {
  76. lists[0] = v0;
  77. lists[1] = v1;
  78. }
  79. Vector v0[MAX_PORTAL_VERTS];
  80. Vector v1[MAX_PORTAL_VERTS];
  81. Vector *lists[2];
  82. };
  83. // Transforms and clips the portal's verts to the view frustum. Returns false
  84. // if the verts lie outside the frustum.
  85. static inline bool GetPortalScreenExtents( dareaportal_t *pPortal,
  86. portalclip_t * RESTRICT clip, CPortalRect &portalRect , float *pReflectionWaterHeight, VPlane *pFrustumPlanes )
  87. {
  88. portalRect.left = portalRect.bottom = 1e24;
  89. portalRect.right = portalRect.top = -1e24;
  90. bool bValidExtents = false;
  91. worldbrushdata_t *pBrushData = host_state.worldbrush;
  92. int nStartVerts = MIN( pPortal->m_nClipPortalVerts, MAX_PORTAL_VERTS );
  93. // NOTE: We need two passes to deal with reflection. We need to compute
  94. // the screen extents for both the reflected + non-reflected area portals
  95. // and make bounds that surrounds them both.
  96. int nPassCount = ( pReflectionWaterHeight != NULL ) ? 2 : 1;
  97. for ( int j = 0; j < nPassCount; ++j )
  98. {
  99. int i;
  100. for( i=0; i < nStartVerts; i++ )
  101. {
  102. clip->v0[i] = pBrushData->m_pClipPortalVerts[pPortal->m_FirstClipPortalVert+i];
  103. // 2nd pass is to compute the reflected areaportal position
  104. if ( j == 1 )
  105. {
  106. clip->v0[i].z = 2.0f * ( *pReflectionWaterHeight ) - clip->v0[i].z;
  107. }
  108. }
  109. int iCurList = 0;
  110. bool bAllClipped = false;
  111. for( int iPlane=0; iPlane < 4; iPlane++ )
  112. {
  113. Vector *pIn = clip->lists[iCurList];
  114. Vector *pOut = clip->lists[!iCurList];
  115. int nOutVerts = 0;
  116. int iPrev = nStartVerts - 1;
  117. float flPrevDot = pFrustumPlanes[iPlane].m_Normal.Dot( pIn[iPrev] ) - pFrustumPlanes[iPlane].m_Dist;
  118. for( int iCur=0; iCur < nStartVerts; iCur++ )
  119. {
  120. float flCurDot = pFrustumPlanes[iPlane].m_Normal.Dot( pIn[iCur] ) - pFrustumPlanes[iPlane].m_Dist;
  121. if( (flCurDot > 0) != (flPrevDot > 0) )
  122. {
  123. if( nOutVerts < MAX_PORTAL_VERTS )
  124. {
  125. // Add the vert at the intersection.
  126. float t = flPrevDot / (flPrevDot - flCurDot);
  127. VectorLerp( pIn[iPrev], pIn[iCur], t, pOut[nOutVerts] );
  128. ++nOutVerts;
  129. }
  130. }
  131. // Add this vert?
  132. if( flCurDot > 0 )
  133. {
  134. if( nOutVerts < MAX_PORTAL_VERTS )
  135. {
  136. pOut[nOutVerts] = pIn[iCur];
  137. ++nOutVerts;
  138. }
  139. }
  140. flPrevDot = flCurDot;
  141. iPrev = iCur;
  142. }
  143. if( nOutVerts == 0 )
  144. {
  145. // If they're all behind, then this portal is clipped out.
  146. bAllClipped = true;
  147. break;
  148. }
  149. nStartVerts = nOutVerts;
  150. iCurList = !iCurList;
  151. }
  152. if ( bAllClipped )
  153. continue;
  154. // Project all the verts and figure out the screen extents.
  155. Vector screenPos;
  156. Assert( iCurList == 0 );
  157. for( i=0; i < nStartVerts; i++ )
  158. {
  159. Vector &point = clip->v0[i];
  160. g_EngineRenderer->ClipTransform( point, &screenPos );
  161. portalRect.left = fpmin( screenPos.x, portalRect.left );
  162. portalRect.bottom = fpmin( screenPos.y, portalRect.bottom );
  163. portalRect.top = fpmax( screenPos.y, portalRect.top );
  164. portalRect.right = fpmax( screenPos.x, portalRect.right );
  165. }
  166. bValidExtents = true;
  167. }
  168. if ( !bValidExtents )
  169. {
  170. portalRect.left = portalRect.bottom = 0;
  171. portalRect.right = portalRect.top = 0;
  172. }
  173. return bValidExtents;
  174. }
  175. // Fill in the intersection between the two rectangles.
  176. inline bool GetRectIntersection( CPortalRect const *pRect1, CPortalRect const *pRect2, CPortalRect *pOut )
  177. {
  178. pOut->left = fpmax( pRect1->left, pRect2->left );
  179. pOut->right = fpmin( pRect1->right, pRect2->right );
  180. if( pOut->left >= pOut->right )
  181. return false;
  182. pOut->bottom = fpmax( pRect1->bottom, pRect2->bottom );
  183. pOut->top = fpmin( pRect1->top, pRect2->top );
  184. if( pOut->bottom >= pOut->top )
  185. return false;
  186. return true;
  187. }
  188. static void R_FlowThroughArea( int area, const Vector &vecVisOrigin, const CPortalRect *pClipRect,
  189. const VisOverrideData_t* pVisData, float *pReflectionWaterHeight )
  190. {
  191. #ifndef DEDICATED
  192. // Update this area's frustum.
  193. if( g_AreaCounter[area] != g_GlobalCounter )
  194. {
  195. g_VisibleAreas[g_nVisibleAreas] = area;
  196. ++g_nVisibleAreas;
  197. g_AreaCounter[area] = g_GlobalCounter;
  198. g_AreaRect[area] = *pClipRect;
  199. }
  200. else
  201. {
  202. // Expand the areaportal's rectangle to include the new cliprect.
  203. CPortalRect *pFrustumRect = &g_AreaRect[area];
  204. pFrustumRect->left = fpmin( pFrustumRect->left, pClipRect->left );
  205. pFrustumRect->bottom = fpmin( pFrustumRect->bottom, pClipRect->bottom );
  206. pFrustumRect->top = fpmax( pFrustumRect->top, pClipRect->top );
  207. pFrustumRect->right = fpmax( pFrustumRect->right, pClipRect->right );
  208. }
  209. // Mark this area as visible.
  210. R_SetBit( g_RenderAreaBits, area );
  211. // Set that we're in this area on the stack.
  212. R_SetBit( g_AreaStack, area );
  213. worldbrushdata_t *pBrushData = host_state.worldbrush;
  214. Assert( area < host_state.worldbrush->m_nAreas );
  215. darea_t *pArea = &host_state.worldbrush->m_pAreas[area];
  216. // temp buffer for clipping
  217. portalclip_t clipTmp;
  218. VPlane frustumPlanes[FRUSTUM_NUMPLANES];
  219. g_Frustum.GetPlanes( frustumPlanes );
  220. // Check all areas that connect to this area.
  221. for( int iAreaPortal=0; iAreaPortal < pArea->numareaportals; iAreaPortal++ )
  222. {
  223. Assert( pArea->firstareaportal + iAreaPortal < pBrushData->m_nAreaPortals );
  224. dareaportal_t *pAreaPortal = &pBrushData->m_pAreaPortals[ pArea->firstareaportal + iAreaPortal ];
  225. // Don't flow back into a portal on the stack.
  226. if( R_TestBit( g_AreaStack, pAreaPortal->otherarea ) )
  227. continue;
  228. // If this portal is closed, don't go through it.
  229. if ( !R_TestBit( GetBaseLocalClient().m_chAreaPortalBits, pAreaPortal->m_PortalKey ) )
  230. continue;
  231. // Make sure the viewer is on the right side of the portal to see through it.
  232. cplane_t *pPlane = &pBrushData->planes[ pAreaPortal->planenum ];
  233. // Use the specified vis origin to test backface culling, or the main view if none was specified
  234. float flDist = pPlane->normal.Dot( vecVisOrigin ) - pPlane->dist;
  235. if( flDist < -0.1f )
  236. continue;
  237. // If the client doesn't want this area visible, don't try to flow into it.
  238. if( !R_TestBit( GetBaseLocalClient().m_chAreaBits, pAreaPortal->otherarea ) )
  239. continue;
  240. CPortalRect portalRect;
  241. bool portalVis = true;
  242. // don't try to clip portals if the viewer is practically in the plane
  243. float fDistTolerance = (pVisData)?(pVisData->m_fDistToAreaPortalTolerance):(0.1f);
  244. if ( flDist > fDistTolerance )
  245. {
  246. portalVis = GetPortalScreenExtents( pAreaPortal, &clipTmp, portalRect, pReflectionWaterHeight, frustumPlanes );
  247. }
  248. else
  249. {
  250. portalRect.left = -1;
  251. portalRect.top = 1;
  252. portalRect.right = 1;
  253. portalRect.bottom = -1; // note top/bottom reversed!
  254. //portalVis=true - not needed, default
  255. }
  256. if( portalVis )
  257. {
  258. CPortalRect intersection;
  259. if( GetRectIntersection( &portalRect, pClipRect, &intersection ) )
  260. {
  261. #ifdef USE_CONVARS
  262. if( r_DrawPortals.GetInt() )
  263. {
  264. g_PortalRects.AddToTail( intersection );
  265. }
  266. #endif
  267. // Ok, we can see into this area.
  268. R_FlowThroughArea( pAreaPortal->otherarea, vecVisOrigin, &intersection, pVisData, pReflectionWaterHeight );
  269. }
  270. }
  271. }
  272. // Mark that we're leaving this area.
  273. R_ClearBit( g_AreaStack, area );
  274. #endif
  275. }
  276. static void IncrementGlobalCounter()
  277. {
  278. if( g_GlobalCounter == 0xFFFFFFFF )
  279. {
  280. for( int i=0; i < g_AreaFrustum.Count(); i++ )
  281. g_AreaCounter[i] = 0;
  282. g_GlobalCounter = 1;
  283. }
  284. else
  285. {
  286. g_GlobalCounter++;
  287. }
  288. }
  289. ConVar r_snapportal( "r_snapportal", "-1" );
  290. static void R_SetupVisibleAreaFrustums()
  291. {
  292. #ifndef DEDICATED
  293. const CViewSetup &viewSetup = g_EngineRenderer->ViewGetCurrent();
  294. CPortalRect viewWindow;
  295. if( viewSetup.m_bOrtho )
  296. {
  297. viewWindow.right = viewSetup.m_OrthoRight;
  298. viewWindow.left = viewSetup.m_OrthoLeft;
  299. viewWindow.top = viewSetup.m_OrthoTop;
  300. viewWindow.bottom = viewSetup.m_OrthoBottom;
  301. }
  302. else
  303. {
  304. // Assuming a view plane distance of 1, figure out the boundaries of a window
  305. // the view would project into given the FOV.
  306. float xFOV = g_EngineRenderer->GetFov() * 0.5f;
  307. float yFOV = g_EngineRenderer->GetFovY() * 0.5f;
  308. viewWindow.right = tan( DEG2RAD( xFOV ) );
  309. viewWindow.left = -viewWindow.right;
  310. viewWindow.top = tan( DEG2RAD( yFOV ) );
  311. viewWindow.bottom = -viewWindow.top;
  312. }
  313. Vector viewOrigin = CurrentViewOrigin();
  314. Vector forward = CurrentViewForward();
  315. Vector right = CurrentViewRight();
  316. Vector up = CurrentViewUp();
  317. VPlane planes[FRUSTUM_NUMPLANES];
  318. // Now scale the portals as specified in the normalized view frustum (-1,-1,1,1)
  319. // into our view window and generate planes out of that.
  320. for( int i=0; i < g_nVisibleAreas; i++ )
  321. {
  322. CPortalRect *pRect = &g_AreaRect[ g_VisibleAreas[i] ];
  323. Frustum_t *pFrustum = &g_AreaFrustum[g_VisibleAreas[i]];
  324. CPortalRect portalWindow;
  325. portalWindow.left = RemapVal( pRect->left, -1, 1, viewWindow.left, viewWindow.right );
  326. portalWindow.right = RemapVal( pRect->right, -1, 1, viewWindow.left, viewWindow.right );
  327. portalWindow.top = RemapVal( pRect->top, -1, 1, viewWindow.bottom, viewWindow.top );
  328. portalWindow.bottom = RemapVal( pRect->bottom, -1, 1, viewWindow.bottom, viewWindow.top );
  329. if( viewSetup.m_bOrtho )
  330. {
  331. // Left and right planes...
  332. float orgOffset = DotProduct(viewOrigin, right);
  333. planes[FRUSTUM_LEFT].Init( right, portalWindow.left + orgOffset );
  334. planes[FRUSTUM_RIGHT].Init( -right, -portalWindow.right - orgOffset );
  335. // Top and bottom planes...
  336. orgOffset = DotProduct(viewOrigin, up);
  337. planes[FRUSTUM_TOP].Init( up, portalWindow.top + orgOffset );
  338. planes[FRUSTUM_BOTTOM].Init( -up, -portalWindow.bottom - orgOffset );
  339. planes[FRUSTUM_NEARZ].Init( forward, 0 );
  340. planes[FRUSTUM_FARZ].Init( -forward, -1e6f );
  341. pFrustum->SetPlanes(planes);
  342. }
  343. else
  344. {
  345. Vector normal;
  346. // right side
  347. normal = portalWindow.right * forward - right;
  348. VectorNormalize(normal);
  349. planes[FRUSTUM_RIGHT].Init( normal, DotProduct(normal,viewOrigin) );
  350. // left side
  351. normal = CurrentViewRight() - portalWindow.left * forward;
  352. VectorNormalize(normal);
  353. planes[FRUSTUM_LEFT].Init( normal, DotProduct(normal,viewOrigin) );
  354. // top
  355. normal = portalWindow.top * forward - up;
  356. VectorNormalize(normal);
  357. planes[FRUSTUM_TOP].Init( normal, DotProduct(normal,viewOrigin) );
  358. // bottom
  359. normal = up - portalWindow.bottom * forward;
  360. VectorNormalize(normal);
  361. planes[FRUSTUM_BOTTOM].Init( normal, DotProduct(normal,viewOrigin) );
  362. // nearz
  363. planes[FRUSTUM_NEARZ].Init( forward, DotProduct(forward, viewOrigin) + viewSetup.zNear );
  364. // farz
  365. planes[FRUSTUM_FARZ].Init( -forward, DotProduct(-forward, viewOrigin) - viewSetup.zFar );
  366. pFrustum->SetPlanes(planes);
  367. }
  368. }
  369. // DEBUG: Code to visualize the areaportal frustums in 3D
  370. // Useful for debugging
  371. if ( r_snapportal.GetInt() >= 0 )
  372. {
  373. extern void CSGFrustum( Frustum_t &frustum );
  374. for ( int i = 0; i < g_nVisibleAreas; i++ )
  375. {
  376. if ( g_VisibleAreas[i] == r_snapportal.GetInt() )
  377. {
  378. Frustum_t *pFrustum = &g_AreaFrustum[ g_VisibleAreas[i] ];
  379. pFrustum->SetPlane( FRUSTUM_NEARZ, forward,
  380. DotProduct(forward, viewOrigin) );
  381. pFrustum->SetPlane( FRUSTUM_FARZ, -forward,
  382. DotProduct(-forward, viewOrigin + forward*500) );
  383. r_snapportal.SetValue( -1 );
  384. CSGFrustum( *pFrustum );
  385. }
  386. }
  387. }
  388. #endif
  389. }
  390. // culls a node to the frustum or area frustum
  391. bool R_CullNode( mnode_t *pNode )
  392. {
  393. if ( !g_bViewerInSolidSpace && pNode->area > 0 )
  394. {
  395. // First make sure its whole area is even visible.
  396. if( !R_IsAreaVisible( pNode->area ) )
  397. return true;
  398. return CullNodeSIMD( g_AreaFrustum[pNode->area], pNode );
  399. }
  400. return CullNodeSIMD( g_Frustum, pNode );
  401. }
  402. static ConVar r_portalscloseall( "r_portalscloseall", "0" );
  403. static ConVar r_portalsopenall( "r_portalsopenall", "0", FCVAR_CHEAT, "Open all portals" );
  404. static ConVar r_ShowViewerArea( "r_ShowViewerArea", "0" );
  405. void R_SetupAreaBits( int iForceViewLeaf /* = -1 */, const VisOverrideData_t* pVisData /* = NULL */, float *pWaterReflectionHeight /* = NULL */ )
  406. {
  407. IncrementGlobalCounter();
  408. Vector vVisOrigin = pVisData ? pVisData->m_vecVisOrigin : g_EngineRenderer->ViewOrigin();
  409. // Clear the visible area bits.
  410. memset( g_RenderAreaBits, 0, sizeof( g_RenderAreaBits ) );
  411. memset( g_AreaStack, 0, sizeof( g_AreaStack ) );
  412. // Our initial clip rect is the whole screen.
  413. CPortalRect rect;
  414. rect.left = rect.bottom = -1;
  415. rect.top = rect.right = 1;
  416. // Flow through areas starting at the one we're in.
  417. int leaf = iForceViewLeaf;
  418. // If view point override wasn't specified, use the current view origin
  419. if ( iForceViewLeaf == -1 )
  420. {
  421. leaf = CM_PointLeafnum( vVisOrigin );
  422. }
  423. g_bViewerInSolidSpace = false;
  424. if( r_portalscloseall.GetBool() )
  425. {
  426. if ( GetBaseLocalClient().m_bAreaBitsValid )
  427. {
  428. // Clear the visible area bits.
  429. memset( g_RenderAreaBits, 0, sizeof( g_RenderAreaBits ) );
  430. int area = host_state.worldbrush->leafs[leaf].area;
  431. R_SetBit( g_RenderAreaBits, area );
  432. g_VisibleAreas[0] = area;
  433. g_nVisibleAreas = 1;
  434. g_AreaCounter[area] = g_GlobalCounter;
  435. g_AreaRect[area] = rect;
  436. }
  437. else
  438. {
  439. g_bViewerInSolidSpace = true;
  440. }
  441. }
  442. else
  443. {
  444. int ss_Slot = GET_ACTIVE_SPLITSCREEN_SLOT();
  445. if ( host_state.worldbrush->leafs[leaf].contents & CONTENTS_SOLID ||
  446. GetBaseLocalClient().ishltv ||
  447. #if defined( REPLAY_ENABLED )
  448. GetBaseLocalClient().isreplay ||
  449. #endif
  450. !GetBaseLocalClient().m_bAreaBitsValid ||
  451. r_portalsopenall.GetBool() )
  452. {
  453. // Draw everything if we're in solid space or if r_portalsopenall is true (used for building cubemaps)
  454. g_bViewerInSolidSpace = true;
  455. if ( r_ShowViewerArea.GetInt() )
  456. Con_NPrintf( 3 + ss_Slot, "(%d), Viewer area: (solid space)", ss_Slot );
  457. }
  458. else
  459. {
  460. int area = host_state.worldbrush->leafs[leaf].area;
  461. if ( r_ShowViewerArea.GetInt() )
  462. Con_NPrintf( 3 + ss_Slot, "(%d) Viewer area: %d", ss_Slot, area );
  463. g_nVisibleAreas = 0;
  464. if ( pVisData && pVisData->m_bTrimFrustumToPortalCorners && r_ClipAreaFrustums.GetBool() )
  465. {
  466. const float flDistToPortalTolerance = 16.0f;
  467. // If the current view origin is within some perpendicular distance of the exit portal AND within the radius of the portal,
  468. // don't attempt to optimize the area rect/frustum
  469. Vector vViewOriginToPortalOrigin = CurrentViewOrigin() - pVisData->m_vPortalOrigin;
  470. if ( fabsf( vViewOriginToPortalOrigin.Dot( pVisData->m_vPortalForward ) ) > flDistToPortalTolerance ||
  471. vViewOriginToPortalOrigin.Length() > pVisData->m_flPortalRadius )
  472. {
  473. // invert the rectangle and then grow it to fit the portal
  474. rect.left = rect.bottom = 1;
  475. rect.right = rect.top = -1;
  476. for ( int i = 0; i < 4; ++ i )
  477. {
  478. Vector vScreenPos;
  479. g_EngineRenderer->ClipTransform( pVisData->m_vPortalCorners[ i ], &vScreenPos );
  480. rect.left = fpmin( vScreenPos.x, rect.left );
  481. rect.bottom = fpmin( vScreenPos.y, rect.bottom );
  482. rect.right = fpmax( vScreenPos.x, rect.right );
  483. rect.top = fpmax( vScreenPos.y, rect.top );
  484. }
  485. rect.left = fpmax( rect.left, -1.0f );
  486. rect.bottom = fpmax( rect.bottom, -1.0f );
  487. rect.right = fpmin( rect.right, 1.0f );
  488. rect.top = fpmin( rect.top, 1.0f );
  489. }
  490. }
  491. R_FlowThroughArea( area, vVisOrigin, &rect, pVisData, pWaterReflectionHeight );
  492. }
  493. }
  494. if ( !g_bViewerInSolidSpace )
  495. {
  496. R_SetupVisibleAreaFrustums();
  497. }
  498. }
  499. bool R_ShouldUseAreaFrustum( int area )
  500. {
  501. if ( g_AreaCounter[area] == g_GlobalCounter )
  502. return true;
  503. else
  504. return false;
  505. }
  506. const Frustum_t* GetAreaFrustum( int area )
  507. {
  508. if ( g_AreaCounter[area] == g_GlobalCounter )
  509. return &g_AreaFrustum[area];
  510. else
  511. return &g_Frustum;
  512. }
  513. int GetAllAreaFrustums( Frustum_t **pFrustumList, int listMax )
  514. {
  515. int count = g_AreaFrustum.Count();
  516. count = MIN(listMax,count);
  517. for ( int i = 0; i < count; i++ )
  518. {
  519. if ( g_AreaCounter[i] == g_GlobalCounter )
  520. {
  521. pFrustumList[i] = &g_AreaFrustum[i];
  522. }
  523. else
  524. {
  525. pFrustumList[i] = &g_Frustum;
  526. }
  527. }
  528. return count;
  529. }