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.

467 lines
14 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. // TODO: Should all of this Map_Vis* stuff be an interface?
  8. //
  9. #include "quakedef.h"
  10. #include "gl_model_private.h"
  11. #include "view_shared.h"
  12. #include "cmodel_engine.h"
  13. #include "tier0/vprof.h"
  14. #include "utllinkedlist.h"
  15. #include "ivrenderview.h"
  16. #include "tier0/cache_hints.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. static ConVar r_novis( "r_novis","0", FCVAR_CHEAT , "Turn off the PVS." );
  20. static ConVar r_lockpvs( "r_lockpvs", "0", FCVAR_CHEAT, "Lock the PVS so you can fly around and inspect what is being drawn." );
  21. static ConVar r_portal_use_pvs_optimization( "r_portal_use_pvs_optimization", "1", 0, "Enables an optimization that allows portals to be culled when outside of the PVS." );
  22. bool g_bNoClipEnabled = false;
  23. // ----------------------------------------------------------------------
  24. // Renderer interface to vis
  25. // ----------------------------------------------------------------------
  26. int r_visframecount = 0;
  27. //-----------------------------------------------------------------------------
  28. // Purpose: For each cluster to be OR'd into vis, remember the origin, the last viewcluster
  29. // for that origin and the current one, so we can tell when vis is dirty and needs to be
  30. // recomputed
  31. //-----------------------------------------------------------------------------
  32. typedef struct
  33. {
  34. Vector origin;
  35. int viewcluster;
  36. int oldviewcluster;
  37. } VISCLUSTER;
  38. //-----------------------------------------------------------------------------
  39. // Purpose: Stores info for updating vis data for the map
  40. //-----------------------------------------------------------------------------
  41. typedef struct
  42. {
  43. // Number of relevant vis clusters
  44. int nClusters;
  45. // Last number ( if != nClusters, recompute vis )
  46. int oldnClusters;
  47. // List of clusters to merge together for final vis
  48. VISCLUSTER rgVisClusters[ MAX_VIS_LEAVES ];
  49. // Composite vis data
  50. byte rgCurrentVis[ MAX_MAP_LEAFS/8 ];
  51. bool bSkyVisible;
  52. bool bForceFullSky;
  53. } VISINFO;
  54. static VISINFO vis;
  55. // I think this is enough. We should have enough here to cover what we might have in a frame, including:
  56. // 1) water reflection
  57. // 2) camera/monitor (actually, this is merged with the regular world)
  58. // 3) 3dskybox
  59. // 4) regular world
  60. const int VISCACHE_SIZE = 8;
  61. class VisCacheEntry
  62. {
  63. public:
  64. VisCacheEntry() { nClusters = 0; }
  65. int nClusters;
  66. int originclusters[MAX_VIS_LEAVES];
  67. CUtlVector< unsigned short > leaflist;
  68. CUtlVector< unsigned short > nodelist;
  69. };
  70. static CUtlLinkedList< VisCacheEntry > viscache( 0, VISCACHE_SIZE );
  71. static void SortVisViewClusters()
  72. {
  73. for (int i = 1; i < vis.nClusters; ++i)
  74. {
  75. int t = vis.rgVisClusters[i].viewcluster;
  76. int j = i;
  77. while (j > 0 && vis.rgVisClusters[j-1].viewcluster > t)
  78. {
  79. vis.rgVisClusters[j].viewcluster = vis.rgVisClusters[j-1].viewcluster;
  80. --j;
  81. }
  82. vis.rgVisClusters[j].viewcluster = t;
  83. }
  84. }
  85. static void VisMark_Cached( const VisCacheEntry &cache, const worldbrushdata_t &worldbrush )
  86. {
  87. int count, visframe;
  88. visframe = r_visframecount;
  89. count = cache.leaflist.Count();
  90. const unsigned short * RESTRICT pSrc = cache.leaflist.Base();
  91. #if defined( _X360 ) || defined( _PS3 )
  92. const int offsetLeaf = offsetof(mleaf_t, visframe);
  93. const int offsetNode = offsetof(mnode_t, visframe);
  94. #endif
  95. while ( count >= 8 )
  96. {
  97. #if defined( _X360 ) || defined( _PS3 )
  98. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[0]), offsetLeaf );
  99. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[1]), offsetLeaf );
  100. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[2]), offsetLeaf );
  101. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[3]), offsetLeaf );
  102. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[4]), offsetLeaf );
  103. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[5]), offsetLeaf );
  104. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[6]), offsetLeaf );
  105. PREFETCH_128( (void *)(worldbrush.leafs + pSrc[7]), offsetLeaf );
  106. #endif
  107. worldbrush.leafs[pSrc[0]].visframe = visframe;
  108. worldbrush.leafs[pSrc[1]].visframe = visframe;
  109. worldbrush.leafs[pSrc[2]].visframe = visframe;
  110. worldbrush.leafs[pSrc[3]].visframe = visframe;
  111. worldbrush.leafs[pSrc[4]].visframe = visframe;
  112. worldbrush.leafs[pSrc[5]].visframe = visframe;
  113. worldbrush.leafs[pSrc[6]].visframe = visframe;
  114. worldbrush.leafs[pSrc[7]].visframe = visframe;
  115. pSrc += 8;
  116. count -= 8;
  117. }
  118. while ( count )
  119. {
  120. worldbrush.leafs[pSrc[0]].visframe = visframe;
  121. count--;
  122. pSrc++;
  123. }
  124. count = cache.nodelist.Count();
  125. pSrc = cache.nodelist.Base();
  126. while ( count >= 8 )
  127. {
  128. #if defined( _X360 ) || defined( _PS3 )
  129. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[0]), offsetNode );
  130. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[1]), offsetNode );
  131. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[2]), offsetNode );
  132. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[3]), offsetNode );
  133. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[4]), offsetNode );
  134. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[5]), offsetNode );
  135. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[6]), offsetNode );
  136. PREFETCH_128( (void *)(worldbrush.nodes + pSrc[7]), offsetNode );
  137. #endif
  138. worldbrush.nodes[pSrc[0]].visframe = visframe;
  139. worldbrush.nodes[pSrc[1]].visframe = visframe;
  140. worldbrush.nodes[pSrc[2]].visframe = visframe;
  141. worldbrush.nodes[pSrc[3]].visframe = visframe;
  142. worldbrush.nodes[pSrc[4]].visframe = visframe;
  143. worldbrush.nodes[pSrc[5]].visframe = visframe;
  144. worldbrush.nodes[pSrc[6]].visframe = visframe;
  145. worldbrush.nodes[pSrc[7]].visframe = visframe;
  146. pSrc += 8;
  147. count -= 8;
  148. }
  149. while ( count )
  150. {
  151. worldbrush.nodes[pSrc[0]].visframe = visframe;
  152. count--;
  153. pSrc++;
  154. }
  155. }
  156. static void VisCache_Build( VisCacheEntry &cache, const worldbrushdata_t &worldbrush )
  157. {
  158. VPROF_INCREMENT_COUNTER( "VisCache misses", 1 );
  159. int i;
  160. mleaf_t *leaf;
  161. int cluster;
  162. cache.nClusters = vis.nClusters;
  163. for (i = 0; i < vis.nClusters; ++i)
  164. {
  165. cache.originclusters[i] = vis.rgVisClusters[i].viewcluster;
  166. }
  167. cache.leaflist.RemoveAll();
  168. cache.nodelist.RemoveAll();
  169. int visframe = r_visframecount;
  170. for ( i = 0, leaf = worldbrush.leafs ; i < worldbrush.numleafs ; i++, leaf++)
  171. {
  172. MEM_ALLOC_CREDIT();
  173. cluster = leaf->cluster;
  174. if ( cluster == -1 )
  175. continue;
  176. if (vis.rgCurrentVis[cluster>>3] & (1<<(cluster&7)))
  177. {
  178. leaf->visframe = visframe;
  179. cache.leaflist.AddToTail( i );
  180. mnode_t *node = leaf->parent;
  181. while (node && node->visframe != visframe)
  182. {
  183. cache.nodelist.AddToTail( node - worldbrush.nodes );
  184. node->visframe = visframe;
  185. node = node->parent;
  186. }
  187. }
  188. }
  189. }
  190. bool Map_AreAnyLeavesVisible( const worldbrushdata_t &worldbrush, int *leafList, int nLeaves )
  191. {
  192. for ( int i=0; i < nLeaves; i++ )
  193. {
  194. const mleaf_t *leaf = &worldbrush.leafs[leafList[i]];
  195. int cluster = leaf->cluster;
  196. if ( cluster == -1 )
  197. continue;
  198. if ( vis.rgCurrentVis[cluster>>3] & (1<<(cluster&7)) )
  199. return true;
  200. }
  201. return false;
  202. }
  203. //-----------------------------------------------------------------------------
  204. // Purpose: Mark the leaves and nodes that are in the PVS for the current
  205. // cluster(s)
  206. // Input : *worldmodel -
  207. //-----------------------------------------------------------------------------
  208. void Map_VisMark( bool forcenovis, model_t *worldmodel )
  209. {
  210. VPROF( "Map_VisMark" );
  211. int i, c;
  212. // development aid to let you run around and see exactly where
  213. // the pvs ends
  214. if ( r_lockpvs.GetInt() )
  215. {
  216. return;
  217. }
  218. SortVisViewClusters();
  219. bool outsideWorld = false;
  220. for ( i = 0; i < vis.nClusters; i++ )
  221. {
  222. if ( vis.rgVisClusters[ i ].viewcluster != vis.rgVisClusters[ i ].oldviewcluster )
  223. {
  224. break;
  225. }
  226. }
  227. // No changes
  228. if ( i >= vis.nClusters && !forcenovis && ( vis.nClusters == vis.oldnClusters ) )
  229. {
  230. return;
  231. }
  232. // Update vis frame marker
  233. r_visframecount++;
  234. // Update cluster history
  235. vis.oldnClusters = vis.nClusters;
  236. for ( i = 0; i < vis.nClusters; i++ )
  237. {
  238. vis.rgVisClusters[ i ].oldviewcluster = vis.rgVisClusters[ i ].viewcluster;
  239. // Outside world?
  240. //
  241. // When using the Portal2 visibility optimization, it is possible that one or more of the
  242. // vis origins will be outside of the world. We should still try and compute visibility
  243. // from all of the clusters which are valid instead of flagging everything as visible by default.
  244. //
  245. // When noclip is enabled, we can't use this optimization because we're outside of the world
  246. // and yet we want to be able to see everything.
  247. if ( vis.rgVisClusters[ i ].viewcluster == -1 && ( !r_portal_use_pvs_optimization.GetBool() || g_bNoClipEnabled ) )
  248. {
  249. outsideWorld = true;
  250. break;
  251. }
  252. }
  253. #ifdef USE_CONVARS
  254. if ( r_novis.GetInt() || forcenovis || outsideWorld )
  255. {
  256. // mark everything
  257. for (i=0 ; i<worldmodel->brush.pShared->numleafs ; i++)
  258. {
  259. worldmodel->brush.pShared->leafs[i].visframe = r_visframecount;
  260. }
  261. for (i=0 ; i<worldmodel->brush.pShared->numnodes ; i++)
  262. {
  263. worldmodel->brush.pShared->nodes[i].visframe = r_visframecount;
  264. }
  265. return;
  266. }
  267. #endif
  268. // There should always be at least one origin and that's the default render origin in most cases
  269. assert( vis.nClusters >= 1 );
  270. CM_Vis( vis.rgCurrentVis, sizeof( vis.rgCurrentVis ), vis.rgVisClusters[ 0 ].viewcluster, DVIS_PVS );
  271. // Get cluster count
  272. c = ( CM_NumClusters() + 31 ) / 32 ;
  273. // Merge in any extra clusters
  274. for ( i = 1; i < vis.nClusters; i++ )
  275. {
  276. byte mapVis[ MAX_MAP_CLUSTERS/8 ];
  277. CM_Vis( mapVis, sizeof( mapVis ), vis.rgVisClusters[ i ].viewcluster, DVIS_PVS );
  278. // Copy one dword at a time ( could use memcpy )
  279. for ( int j = 0 ; j < c ; j++ )
  280. {
  281. ((int *)vis.rgCurrentVis)[ j ] |= ((int *)mapVis)[ j ];
  282. }
  283. }
  284. // search the cache for a pre-built list of leaves and nodes that matches
  285. // the desired vis setup, and use that to mark the map if found
  286. for (i = viscache.Head(); i != viscache.InvalidIndex(); i = viscache.Next(i))
  287. {
  288. VisCacheEntry &cache = viscache[i];
  289. if (cache.nClusters != vis.nClusters) continue;
  290. for (c = 0; c < cache.nClusters; ++c)
  291. {
  292. if (cache.originclusters[c] != vis.rgVisClusters[c].viewcluster)
  293. {
  294. // NJS: This is a nasty goto, but avoids a nasty branch mispredict below
  295. // (if a break and if are used instead)
  296. goto next_cache_check;
  297. }
  298. }
  299. viscache.LinkToHead( i );
  300. VisMark_Cached( cache, *worldmodel->brush.pShared );
  301. return;
  302. next_cache_check:;
  303. }
  304. // if we get here, we need to update the cache with a new entry
  305. if (viscache.Count() < VISCACHE_SIZE)
  306. {
  307. viscache.AddToHead();
  308. }
  309. else
  310. {
  311. viscache.LinkToHead( viscache.Tail() );
  312. }
  313. // this also will mark the visleafs in order to build the cache data
  314. VisCache_Build( viscache[viscache.Head()], *worldmodel->brush.pShared );
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: Purpose: Setup vis for the specified map
  318. // Input : *worldmodel - the map
  319. // visorigincount - how many origins to merge together ( usually 1, can be 0 if forcenovis is true )
  320. // origins[][3] - array of origins to merge together
  321. // forcenovis - if set to true, ignore all origins and just mark everything as visible ( SLOW rendering!!! )
  322. //-----------------------------------------------------------------------------
  323. void Map_VisSetup( model_t *worldmodel, int visorigincount, const Vector origins[], bool forcenovis /*=false*/, unsigned int &returnFlags )
  324. {
  325. assert( visorigincount <= MAX_VIS_LEAVES );
  326. // Don't crash if the client .dll tries to do something weird/dumb
  327. vis.nClusters = MIN( visorigincount, MAX_VIS_LEAVES );
  328. vis.bForceFullSky = false;
  329. vis.bSkyVisible = false;
  330. returnFlags = 0;
  331. for ( int i = 0; i < vis.nClusters; i++ )
  332. {
  333. int leafIndex = CM_PointLeafnum( origins[ i ] );
  334. int flags = CM_LeafFlags( leafIndex );
  335. if ( flags & ( LEAF_FLAGS_SKY | LEAF_FLAGS_SKY2D ) )
  336. {
  337. vis.bSkyVisible = true;
  338. }
  339. if ( flags & LEAF_FLAGS_RADIAL )
  340. {
  341. vis.bForceFullSky = true;
  342. returnFlags |= IVRenderView::VIEW_SETUP_VIS_EX_RETURN_FLAGS_USES_RADIAL_VIS;
  343. }
  344. vis.rgVisClusters[ i ].viewcluster = CM_LeafCluster( leafIndex );
  345. VectorCopy( origins[ i ], vis.rgVisClusters[ i ].origin );
  346. }
  347. if ( !vis.bSkyVisible )
  348. {
  349. vis.bForceFullSky = false;
  350. }
  351. Map_VisMark( forcenovis, worldmodel );
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Purpose: Clear / reset vis data
  355. //-----------------------------------------------------------------------------
  356. void Map_VisClear( void )
  357. {
  358. vis.nClusters = 1;
  359. vis.oldnClusters = 1;
  360. for ( int i = 0; i < MAX_VIS_LEAVES; i++ )
  361. {
  362. vis.rgVisClusters[ i ].oldviewcluster = -2;
  363. VectorClear( vis.rgVisClusters[ i ].origin );
  364. vis.rgVisClusters[ i ].viewcluster = -2;
  365. }
  366. viscache.RemoveAll();
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose: Returns the current vis bitfield
  370. // Output : byte
  371. //-----------------------------------------------------------------------------
  372. byte *Map_VisCurrent( void )
  373. {
  374. return vis.rgCurrentVis;
  375. }
  376. //-----------------------------------------------------------------------------
  377. // Purpose: Returns the first viewcluster ( usually it's the only )
  378. // Output : int
  379. //-----------------------------------------------------------------------------
  380. int Map_VisCurrentCluster( void )
  381. {
  382. // BUGBUG: The client DLL can hit this assert during a level transition
  383. // because the temporary entities do visibility calculations during the
  384. // wrong part of the frame loop (i.e. before a view has been set up!)
  385. Assert( vis.rgVisClusters[ 0 ].viewcluster >= 0 );
  386. if ( vis.rgVisClusters[ 0 ].viewcluster < 0 )
  387. {
  388. static int visclusterwarningcount = 0;
  389. if ( ++visclusterwarningcount <= 5 )
  390. {
  391. ConDMsg( "Map_VisCurrentCluster() < 0!\n" );
  392. }
  393. }
  394. return vis.rgVisClusters[ 0 ].viewcluster;
  395. }
  396. bool Map_VisSkyVisible()
  397. {
  398. return vis.bSkyVisible;
  399. }
  400. bool Map_VisForceFullSky()
  401. {
  402. return vis.bForceFullSky;
  403. }