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.

1773 lines
53 KiB

  1. #include "fow.h"
  2. #include "fow_radiusoccluder.h"
  3. #include "fow_viewer.h"
  4. #include "fow_trisoup.h"
  5. #include "fow_horizontalslice.h"
  6. #include "keyvalues.h"
  7. #include "utlbuffer.h"
  8. #include "filesystem.h"
  9. #include "vstdlib/jobthread.h"
  10. #include "vphysics_interface.h"
  11. #include "gametrace.h"
  12. #include "vprof.h"
  13. #include "engine/IVDebugOverlay.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include <tier0/memdbgon.h>
  16. extern IVDebugOverlay *debugoverlay;
  17. //-----------------------------------------------------------------------------
  18. // Purpose:
  19. // Input :
  20. // Output :
  21. //-----------------------------------------------------------------------------
  22. CFoW::CFoW( ) :
  23. m_Occluders( 100, 100 ),
  24. m_Viewers( 100, 100 ),
  25. m_TriSoupCollection( 50, 50 ),
  26. m_RadiusTables( 0, 0, DefLessFunc( float ) )
  27. {
  28. m_nNumberOfTeams = 0;
  29. for ( int i = 0; i < MAX_FOW_TEAMS; i++ )
  30. {
  31. m_pVisibilityGridFlags[ i ] = NULL;
  32. m_pVisibilityGridDegree[ i ] = NULL;
  33. m_pVisibilityFadeTimer[ i ] = NULL;
  34. }
  35. m_pHorizontalSlices = NULL;
  36. m_pVerticalLevels = NULL;
  37. m_flDegreeFadeRate = 2.0;
  38. m_nGridZUnits = 0;
  39. m_bInitialized = false;
  40. m_bDebugVisible = false;
  41. m_nDebugFlags = 0;
  42. m_nHorizontalGridAllocationSize = m_nVerticalGridAllocationSize = 0;
  43. m_nRadiusTableSize = 0;
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose:
  47. // Input :
  48. // Output :
  49. //-----------------------------------------------------------------------------
  50. CFoW::~CFoW( )
  51. {
  52. ClearState();
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose:
  56. //-----------------------------------------------------------------------------
  57. void CFoW::ClearState( )
  58. {
  59. for ( int i = 0; i < MAX_FOW_TEAMS; i++ )
  60. {
  61. if ( m_pVisibilityGridFlags[ i ] )
  62. {
  63. free( m_pVisibilityGridFlags[ i ] );
  64. m_pVisibilityGridFlags[ i ] = NULL;
  65. free( m_pVisibilityGridDegree[ i ] );
  66. m_pVisibilityGridDegree[ i ] = NULL;
  67. free( m_pVisibilityFadeTimer[ i ] );
  68. m_pVisibilityFadeTimer[ i ] = NULL;
  69. }
  70. }
  71. if ( m_pHorizontalSlices )
  72. {
  73. for ( int i = 0; i < m_nGridZUnits; i++ )
  74. {
  75. delete m_pHorizontalSlices[ i ];
  76. }
  77. delete m_pHorizontalSlices;
  78. m_pHorizontalSlices = NULL;
  79. }
  80. m_nGridZUnits = 0;
  81. if ( m_pVerticalLevels )
  82. {
  83. delete m_pVerticalLevels;
  84. m_pVerticalLevels = NULL;
  85. }
  86. m_Occluders.PurgeAndDeleteElements();
  87. m_Viewers.PurgeAndDeleteElements();
  88. m_TriSoupCollection.PurgeAndDeleteElements();
  89. for( unsigned int i = 0; i < m_RadiusTables.Count(); i++ )
  90. {
  91. if ( m_RadiusTables.IsValidIndex( i ) == false )
  92. {
  93. continue;
  94. }
  95. free( m_RadiusTables.Element( i ) );
  96. }
  97. m_RadiusTables.RemoveAll();
  98. m_ViewerTree.Purge();
  99. m_OccluderTree.Purge();
  100. m_nHorizontalGridAllocationSize = m_nVerticalGridAllocationSize = 0;
  101. m_nRadiusTableSize = 0;
  102. m_bInitialized = false;
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose: Sets the number of viewer teams
  106. // Input : nCount - the maximum number of teams
  107. //-----------------------------------------------------------------------------
  108. void CFoW::SetNumberOfTeams( int nCount )
  109. {
  110. Assert( nCount > 0 && nCount <= MAX_FOW_TEAMS );
  111. m_nNumberOfTeams = nCount;
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose: Sets the world mins/maxs and how big the grid sizes should be
  115. // Input : vWorldMins - the world minimums
  116. // vWorldMaxs - the world maximums
  117. // nHorizontalGridSize - the horizontal size the world should be chopped up by ( world xy size / this value ) rounded up
  118. // nVerticalGridSize - the vertical size the world should be chopped up by ( world z size / this value ) rounded up
  119. //-----------------------------------------------------------------------------
  120. void CFoW::SetSize( Vector &vWorldMins, Vector &vWorldMaxs, int nHorizontalGridSize, int nVerticalGridSize )
  121. {
  122. Assert( m_nNumberOfTeams > 0 );
  123. m_nHorizontalGridAllocationSize = m_nVerticalGridAllocationSize = 0;
  124. m_vWorldMins = vWorldMins;
  125. m_vWorldMaxs = vWorldMaxs;
  126. m_nHorizontalGridSize = nHorizontalGridSize;
  127. m_nVerticalGridSize = nVerticalGridSize;
  128. m_nGridXUnits = ( ( m_vWorldMaxs.x - m_vWorldMins.x ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
  129. m_nGridYUnits = ( ( m_vWorldMaxs.y - m_vWorldMins.y ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
  130. m_vWorldMaxs.x = m_vWorldMins.x + ( m_nGridXUnits * m_nHorizontalGridSize );
  131. m_vWorldMaxs.y = m_vWorldMins.y + ( m_nGridYUnits * m_nHorizontalGridSize );
  132. m_nTotalHorizontalUnits = m_nGridXUnits * m_nGridYUnits;
  133. int nDegreeAllocationSize = sizeof( *m_pVisibilityGridDegree[ 0 ] ) * m_nTotalHorizontalUnits;
  134. int nFlagAllocationSize = sizeof( *m_pVisibilityGridFlags[ 0 ] ) * m_nTotalHorizontalUnits;
  135. for ( int i = 0; i < m_nNumberOfTeams; i++ )
  136. {
  137. m_pVisibilityGridFlags[ i ] = ( byte * )malloc( nFlagAllocationSize );
  138. memset( m_pVisibilityGridFlags[ i ], 0, nFlagAllocationSize );
  139. m_nHorizontalGridAllocationSize += nFlagAllocationSize;
  140. m_pVisibilityGridDegree[ i ] = ( float * )malloc( nDegreeAllocationSize );
  141. m_nHorizontalGridAllocationSize += nDegreeAllocationSize;
  142. m_pVisibilityFadeTimer[ i ] = ( float * )malloc( nDegreeAllocationSize );
  143. m_nHorizontalGridAllocationSize += nDegreeAllocationSize;
  144. // memset( m_pVisibilityGridDegree[ i ], 0, nDegreeAllocationSize );
  145. for( int j = 0; j < m_nTotalHorizontalUnits; j++ )
  146. {
  147. m_pVisibilityGridDegree[ i ][ j ] = 0.0f;
  148. m_pVisibilityFadeTimer[ i ][ j ] = 0.0f;
  149. }
  150. }
  151. if ( m_nVerticalGridSize > 0 )
  152. {
  153. m_nGridZUnits = ( ( m_vWorldMaxs.z - m_vWorldMins.z ) + m_nVerticalGridSize - 1 ) / m_nVerticalGridSize;
  154. m_pVerticalLevels = ( float * )malloc( sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits );
  155. m_pHorizontalSlices = ( CFoW_HorizontalSlice ** )malloc( sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits );
  156. for ( int i = 0; i < m_nGridZUnits; i++ )
  157. {
  158. m_pHorizontalSlices[ i ] = new CFoW_HorizontalSlice();
  159. m_nVerticalGridAllocationSize += sizeof( CFoW_HorizontalSlice );
  160. m_pVerticalLevels[ i ] = m_vWorldMins.z + ( ( i + 0.75f ) * m_nVerticalGridSize );
  161. }
  162. m_nVerticalGridAllocationSize += sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits;
  163. m_nVerticalGridAllocationSize += sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits;
  164. }
  165. m_bInitialized = true;
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. // Input :
  170. //-----------------------------------------------------------------------------
  171. void CFoW::SetCustomVerticalLevels( float *pflHeightLevels, int nCount )
  172. {
  173. m_nGridZUnits = nCount;
  174. m_nVerticalGridSize = -1;
  175. m_pVerticalLevels = ( float * )malloc( sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits );
  176. m_pHorizontalSlices = ( CFoW_HorizontalSlice ** )malloc( sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits );
  177. for ( int i = 0; i < m_nGridZUnits; i++ )
  178. {
  179. m_pHorizontalSlices[ i ] = new CFoW_HorizontalSlice();
  180. m_nVerticalGridAllocationSize += sizeof( CFoW_HorizontalSlice );
  181. m_pVerticalLevels[ i ] = pflHeightLevels[ i ];
  182. }
  183. m_nVerticalGridAllocationSize += sizeof( m_pVerticalLevels[ 0 ] ) * m_nGridZUnits;
  184. m_nVerticalGridAllocationSize += sizeof( m_pHorizontalSlices[ 0 ] ) * m_nGridZUnits;
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: get the world size of the FoW
  188. // Output : vWorldMins - the world minimums
  189. // vWorldMaxs - the world maximums
  190. //-----------------------------------------------------------------------------
  191. void CFoW::GetSize( Vector &vWorldMins, Vector &vWorldMaxs )
  192. {
  193. vWorldMins = m_vWorldMins;
  194. vWorldMaxs = m_vWorldMaxs;
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Purpose: get the lower vertical coord, the grid size, and grid units
  198. // Output : nBottomZ - the world minimum z value
  199. // nGridSize - the size the world is chopped up by
  200. // nGridUnits - the number of units the world has been chopped into
  201. //-----------------------------------------------------------------------------
  202. void CFoW::GetVerticalGridInfo( int &nBottomZ, int &nGridSize, int &nGridUnits, float **pVerticalLevels )
  203. {
  204. nBottomZ = m_vWorldMins.z;
  205. nGridSize = m_nVerticalGridSize;
  206. nGridUnits = m_nGridZUnits;
  207. *pVerticalLevels = m_pVerticalLevels;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: snap the x/y coordinates to the grid
  211. // Input : vIn - the input coordinates
  212. // bGoLower - should we snap to the left/bottom or right/top of the grid
  213. // Output : vOut - the output coordinates snapped to the grid. z is unsnapped.
  214. //-----------------------------------------------------------------------------
  215. void CFoW::SnapCoordsToGrid( Vector &vIn, Vector &vOut, bool bGoLower )
  216. {
  217. if ( bGoLower )
  218. {
  219. vOut.x = m_vWorldMins.x + ( floor( ( vIn.x - m_vWorldMins.x ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
  220. vOut.y = m_vWorldMins.y + ( floor( ( vIn.y - m_vWorldMins.y ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
  221. vOut.z = vIn.z;
  222. }
  223. else
  224. {
  225. vOut.x = m_vWorldMins.x + ( ceil( ( vIn.x - m_vWorldMins.x ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
  226. vOut.y = m_vWorldMins.y + ( ceil( ( vIn.y - m_vWorldMins.y ) / m_nHorizontalGridSize ) * m_nHorizontalGridSize );
  227. vOut.z = vIn.z;
  228. }
  229. vOut.x = clamp( vOut.x, m_vWorldMins.x, m_vWorldMaxs.x );
  230. vOut.y = clamp( vOut.y, m_vWorldMins.y, m_vWorldMaxs.y );
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: return how visible a cell is ( 0.0 = not currently visible, 1.0 = fully visible )
  234. // Input : nXLoc - the world x location
  235. // nYLoc - the world y location
  236. // nTeam - which team to look up
  237. // Output : returns the visibility degree
  238. //-----------------------------------------------------------------------------
  239. float CFoW::LookupVisibilityDegree( int nXLoc, int nYLoc, int nTeam )
  240. {
  241. int x = ( nXLoc - m_vWorldMins.x ) / m_nHorizontalGridSize;
  242. int y = ( nYLoc - m_vWorldMins.y ) / m_nHorizontalGridSize;
  243. x = clamp( x, 0, m_nGridXUnits - 1 );
  244. y = clamp( y, 0, m_nGridYUnits - 1 );
  245. float flValue = m_pVisibilityGridDegree[ nTeam ][ ( x * m_nGridYUnits ) + y ];
  246. return ( flValue > 1.0f ? 1.0f : flValue );
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose: creates or returns a grid to radius table
  250. // Input : flRadius - the size of the radius
  251. // Output : returns the visibility table to go from grid to radius
  252. //-----------------------------------------------------------------------------
  253. int *CFoW::FindRadiusTable( float flRadius )
  254. {
  255. int nIndex = m_RadiusTables.Find( flRadius );
  256. if ( m_RadiusTables.IsValidIndex( nIndex ) )
  257. {
  258. return m_RadiusTables[ nIndex ];
  259. }
  260. int nGridUnits = ( ( flRadius * 2 ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
  261. nGridUnits |= 1; // always make it odd, so that we have a true center
  262. int nSize = sizeof( int ) * nGridUnits * nGridUnits;
  263. int *pVisibilityData = ( int * )malloc( nSize );
  264. memset( pVisibilityData, -1, nSize );
  265. m_nRadiusTableSize += nSize;
  266. int nOffset = ( nGridUnits / 2 ) * m_nHorizontalGridSize;
  267. int nRadiusUnits = ( ( 2 * M_PI * flRadius ) + m_nHorizontalGridSize - 1 ) / m_nHorizontalGridSize;
  268. int *pVisibility = pVisibilityData;
  269. for ( int x = 0, xPos = -nOffset; x < nGridUnits; x++, xPos += m_nHorizontalGridSize )
  270. {
  271. for ( int y = 0, yPos = -nOffset; y < nGridUnits; y++, yPos += m_nHorizontalGridSize, pVisibility++ )
  272. {
  273. float flDist = sqrt( ( float )( ( xPos * xPos ) + ( yPos * yPos ) ) );
  274. if ( flDist > flRadius )
  275. {
  276. *pVisibility = -1;
  277. continue;
  278. }
  279. float nx = xPos / flDist;
  280. float ny = yPos / flDist;
  281. float flAngle = ( 0.0f * nx ) + ( 1.0f * ny );
  282. float flRealAngle = RAD2DEG( acos( flAngle ) );
  283. if ( nx < 0.0f )
  284. {
  285. flRealAngle = 360 - flRealAngle;
  286. }
  287. flRealAngle = ( flRealAngle / 360.0f ) * nRadiusUnits;
  288. *pVisibility = ( int )flRealAngle;
  289. }
  290. }
  291. m_RadiusTables.Insert( flRadius, pVisibilityData );
  292. return pVisibilityData;
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose: adds a new viewer to the system
  296. // Input : nViewerTeam - the team the viewer is on
  297. // Output : returns the id of the new viewer
  298. //-----------------------------------------------------------------------------
  299. int CFoW::AddViewer( unsigned nViewerTeam )
  300. {
  301. int nSlotID = -1;
  302. // optimize this!
  303. for ( int i = 0; i < m_Viewers.Count(); i++ )
  304. {
  305. if ( m_Viewers[ i ] == NULL )
  306. {
  307. nSlotID = i;
  308. break;
  309. }
  310. }
  311. if ( nSlotID == -1 )
  312. {
  313. nSlotID = m_Viewers.Count();
  314. m_Viewers.AddToTail( NULL );
  315. }
  316. CFoW_Viewer *pViewer = new CFoW_Viewer( nSlotID, nViewerTeam );
  317. m_Viewers[ nSlotID ] = pViewer;
  318. InsertViewerIntoTree( nSlotID );
  319. return nSlotID;
  320. }
  321. //-----------------------------------------------------------------------------
  322. // Purpose: removes a viewer from the system
  323. // Input : nID - the id of the viewer
  324. //-----------------------------------------------------------------------------
  325. void CFoW::RemoveViewer( int nID )
  326. {
  327. if ( m_Viewers[ nID ] != NULL )
  328. {
  329. RemoveViewerFromTree( nID );
  330. delete m_Viewers[ nID ];
  331. m_Viewers[ nID ] = NULL;
  332. }
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: updates the viewer's location
  336. // Input : nID - the id of the viewer
  337. // vLocation - the new location of the viewer
  338. //-----------------------------------------------------------------------------
  339. void CFoW::UpdateViewerLocation( int nID, const Vector &vLocation )
  340. {
  341. Vector vOldLocation;
  342. Assert( m_Viewers[ nID ] );
  343. #ifdef FOW_SAFETY_DANCE
  344. if ( m_Viewers[ nID ] == NULL )
  345. {
  346. Warning( "CFoW: UpdateViewerLocation( %d, ( %g, %g %g ) ) has missing viewer\n", nID, vLocation.x, vLocation.y, vLocation.z );
  347. return;
  348. }
  349. #endif
  350. if ( m_Viewers[ nID ]->UpdateLocation( this, vLocation, &vOldLocation ) == true )
  351. {
  352. RemoveViewerFromTree( nID, &vOldLocation );
  353. InsertViewerIntoTree( nID );
  354. }
  355. }
  356. //-----------------------------------------------------------------------------
  357. // Purpose: updates the viewer's seeing radius
  358. // Input : nID - the id of the viewer
  359. // flRadius - the new radius of the viewer
  360. //-----------------------------------------------------------------------------
  361. void CFoW::UpdateViewerSize( int nID, float flRadius )
  362. {
  363. Assert( m_Viewers[ nID ] );
  364. #ifdef FOW_SAFETY_DANCE
  365. if ( m_Viewers[ nID ] == NULL )
  366. {
  367. Warning( "CFoW: UpdateViewerSize( %d, %g ) has missing viewer\n", nID, flRadius );
  368. return;
  369. }
  370. #endif
  371. RemoveViewerFromTree( nID );
  372. m_Viewers[ nID ]->UpdateSize( this, flRadius );
  373. InsertViewerIntoTree( nID );
  374. }
  375. //-----------------------------------------------------------------------------
  376. // Purpose: updates the viewer's seeing radius
  377. // Input : nID - the id of the viewer
  378. // nHeightGroup - the new height group of the viewer
  379. //-----------------------------------------------------------------------------
  380. void CFoW::UpdateViewerHeightGroup( int nID, uint8 nHeightGroup )
  381. {
  382. Assert( m_Viewers[ nID ] );
  383. #ifdef FOW_SAFETY_DANCE
  384. if ( m_Viewers[ nID ] == NULL )
  385. {
  386. Warning( "CFoW: UpdateViewerHeightGroup( %d, %d ) has missing viewer\n", nID, ( int )nHeightGroup );
  387. return;
  388. }
  389. #endif
  390. m_Viewers[ nID ]->UpdateHeightGroup( nHeightGroup );
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Purpose: adds a new radius occluder to the system
  394. // Input : nPermanent - unused
  395. // Output : returns the id of the new occluder
  396. //-----------------------------------------------------------------------------
  397. int CFoW::AddOccluder( bool nPermanent )
  398. {
  399. int nSlotID = -1;
  400. // optimize this!
  401. for ( int i = 0; i < m_Occluders.Count(); i++ )
  402. {
  403. if ( m_Occluders[ i ] == NULL )
  404. {
  405. nSlotID = i;
  406. break;
  407. }
  408. }
  409. if ( nSlotID == -1 )
  410. {
  411. nSlotID = m_Occluders.Count();
  412. m_Occluders.AddToTail( NULL );
  413. }
  414. CFoW_RadiusOccluder *pOccluder = new CFoW_RadiusOccluder( nSlotID );
  415. m_Occluders[ nSlotID ] = pOccluder;
  416. InsertOccluderIntoTree( nSlotID );
  417. return nSlotID;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: removes an occluder from the system
  421. // Input : nID - the id of the occluder
  422. //-----------------------------------------------------------------------------
  423. void CFoW::RemoveOccluder( int nID )
  424. {
  425. if ( m_Occluders[ nID ] != NULL )
  426. {
  427. RemoveOccluderFromTree( nID );
  428. DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
  429. delete m_Occluders[ nID ];
  430. m_Occluders[ nID ] = NULL;
  431. // RepopulateOccluders(); // CUtlSphereTree has no delete function for now
  432. }
  433. }
  434. void CFoW::EnableOccluder( int nID, bool bEnable )
  435. {
  436. #ifdef FOW_SAFETY_DANCE
  437. if ( m_Occluders[ nID ] == NULL )
  438. {
  439. Warning( "CFoW: EnableOccluder( %d, %s ) has missing occluder\n", nID, bEnable ? "true" : "false" );
  440. return;
  441. }
  442. #endif
  443. m_Occluders[ nID ]->SetEnable( bEnable );
  444. DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
  445. }
  446. //-----------------------------------------------------------------------------
  447. // Purpose: update an occluder's location
  448. // Input : nID - the id of the occluder
  449. // vLocation - the new location of the occluder
  450. //-----------------------------------------------------------------------------
  451. void CFoW::UpdateOccluderLocation( int nID, Vector &vLocation )
  452. {
  453. Assert( m_Occluders[ nID ] );
  454. #ifdef FOW_SAFETY_DANCE
  455. if ( m_Occluders[ nID ] == NULL )
  456. {
  457. Warning( "CFoW: UpdateOccluderLocation( %d, ( %g, %g, %g ) ) has missing occluder\n", nID, vLocation.x, vLocation.y, vLocation.z );
  458. return;
  459. }
  460. #endif
  461. RemoveOccluderFromTree( nID );
  462. DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
  463. m_Occluders[ nID ]->UpdateLocation( vLocation );
  464. // RepopulateOccluders(); // CUtlSphereTree has no delete function for now
  465. InsertOccluderIntoTree( nID );
  466. DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Purpose: update an occluder's size
  470. // Input : nID - the id of the occluder
  471. // flRadius - the new radius of the occluder
  472. //-----------------------------------------------------------------------------
  473. void CFoW::UpdateOccluderSize( int nID, float flRadius )
  474. {
  475. Assert( m_Occluders[ nID ] );
  476. #ifdef FOW_SAFETY_DANCE
  477. if ( m_Occluders[ nID ] == NULL )
  478. {
  479. Warning( "CFoW: UpdateOccluderSize( %d, %g ) has missing occluder\n", nID, flRadius );
  480. return;
  481. }
  482. #endif
  483. // RepopulateOccluders(); // CUtlSphereTree has no delete function for now
  484. RemoveOccluderFromTree( nID );
  485. DirtyViewers( m_Occluders[ nID ]->GetLocation(), ( m_Occluders[ nID ]->GetSize() > flRadius ? m_Occluders[ nID ]->GetSize() : flRadius ) );
  486. m_Occluders[ nID ]->UpdateSize( flRadius );
  487. InsertOccluderIntoTree( nID );
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose: updates the occluder's height group
  491. // Input : nID - the id of the occluder
  492. // nHeightGroup - the new height group of the occluder
  493. //-----------------------------------------------------------------------------
  494. void CFoW::UpdateOccluderHeightGroup( int nID, uint8 nHeightGroup )
  495. {
  496. Assert( m_Occluders[ nID ] );
  497. #ifdef FOW_SAFETY_DANCE
  498. if ( m_Occluders[ nID ] == NULL )
  499. {
  500. Warning( "CFoW: UpdateOccluderHeightGroup( %d, %d ) has missing occluder\n", nID, ( int )nHeightGroup );
  501. return;
  502. }
  503. #endif
  504. // RepopulateOccluders(); // CUtlSphereTree has no delete function for now
  505. DirtyViewers( m_Occluders[ nID ]->GetLocation(), m_Occluders[ nID ]->GetSize() );
  506. m_Occluders[ nID ]->UpdateHeightGroup( nHeightGroup );
  507. }
  508. //-----------------------------------------------------------------------------
  509. // Purpose: internal function called by viewers to radius occlude nearby objects
  510. // Input : nViewerID - the id of the viewer to obstruct
  511. //-----------------------------------------------------------------------------
  512. void CFoW::ObstructOccludersNearViewer( int nViewerID )
  513. {
  514. Assert( m_Viewers[ nViewerID ] );
  515. CFoW_Viewer *pViewer = m_Viewers[ nViewerID ];
  516. Sphere_t TestSphere( pViewer->GetLocation().x, pViewer->GetLocation().y, pViewer->GetLocation().z, pViewer->GetSize() );
  517. CFoW_RadiusOccluder *FixedPointerArray[ FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK ];
  518. CUtlVector< void * > FoundOccluders( ( void ** )FixedPointerArray, FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK );
  519. int RealCount = m_OccluderTree.IntersectWithSphere( TestSphere, true, FoundOccluders, FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK );
  520. if ( RealCount > FOW_MAX_RADIUS_OCCLUDERS_TO_CHECK )
  521. {
  522. // we overflowed, what should we do?
  523. Assert( 0 );
  524. }
  525. // Msg( "Slice Counts: %d / %d\n", FoundOccluders.Count(), RealCount );
  526. for ( int i = 0; i < FoundOccluders.Count(); i++ )
  527. {
  528. FixedPointerArray[ i ]->ObstructViewerRadius( this, pViewer );
  529. }
  530. }
  531. //-----------------------------------------------------------------------------
  532. // Purpose:
  533. // Input :
  534. //-----------------------------------------------------------------------------
  535. void CFoW::SetWorldCollision( CPhysCollide *pCollideable, IPhysicsCollision *pPhysCollision )
  536. {
  537. for( int x = 0; x < m_nGridXUnits; x++ )
  538. {
  539. float flXPos = ( x * m_nHorizontalGridSize ) + m_vWorldMins.x + ( m_nHorizontalGridSize / 2.0f );
  540. for( int y = 0; y < m_nGridYUnits; y++ )
  541. {
  542. float flYPos = ( y * m_nHorizontalGridSize ) + m_vWorldMins.y + ( m_nHorizontalGridSize / 2.0f );
  543. Vector vStart( flXPos, flYPos, 99999.0f ), vEnd( flXPos, flYPos, -99999.0f );
  544. Vector vResultOrigin;
  545. QAngle vResultAngles;
  546. trace_t TraceResult;
  547. pPhysCollision->TraceBox( vStart, vEnd, vec3_origin, vec3_origin, pCollideable, vResultOrigin, vResultAngles, &TraceResult );
  548. }
  549. }
  550. }
  551. //-----------------------------------------------------------------------------
  552. // Purpose: adds a tri soup collection to the system
  553. // Output : returns the id of the new tri soup collection
  554. //-----------------------------------------------------------------------------
  555. int CFoW::AddTriSoup( )
  556. {
  557. int nSlotID = -1;
  558. // optimize this!
  559. for ( int i = 0; i < m_TriSoupCollection.Count(); i++ )
  560. {
  561. if ( m_TriSoupCollection[ i ] == NULL )
  562. {
  563. nSlotID = i;
  564. break;
  565. }
  566. }
  567. if ( nSlotID == -1 )
  568. {
  569. nSlotID = m_TriSoupCollection.Count();
  570. m_TriSoupCollection.AddToTail( NULL );
  571. }
  572. CFoW_TriSoupCollection *pTriSoup = new CFoW_TriSoupCollection( nSlotID );
  573. m_TriSoupCollection[ nSlotID ] = pTriSoup;
  574. return nSlotID;
  575. }
  576. //-----------------------------------------------------------------------------
  577. // Purpose: removes a tri soup collection from the system
  578. // Input : nID - the id of the tri soup
  579. //-----------------------------------------------------------------------------
  580. void CFoW::RemoveTriSoup( int nID )
  581. {
  582. if ( m_TriSoupCollection[ nID ] != NULL )
  583. {
  584. delete m_TriSoupCollection[ nID ];
  585. m_TriSoupCollection[ nID ] = NULL;
  586. }
  587. }
  588. //-----------------------------------------------------------------------------
  589. // Purpose: clears all entries from the collection ( useful for hammer editing only )
  590. // Input : nID - the id of the tri soup
  591. //-----------------------------------------------------------------------------
  592. void CFoW::ClearTriSoup( int nID )
  593. {
  594. Assert( m_TriSoupCollection[ nID ] );
  595. m_TriSoupCollection[ nID ]->Clear();
  596. for ( int i = 0; i < m_nGridZUnits; i++ )
  597. {
  598. m_pHorizontalSlices[ i ]->Clear();
  599. }
  600. for ( int i = 0; i < m_TriSoupCollection.Count(); i++ )
  601. {
  602. if ( m_TriSoupCollection[ i ] )
  603. {
  604. m_TriSoupCollection[ i ]->RepopulateOccluders( this );
  605. }
  606. }
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: adds a tri to the collection. this is immediately split up into the horizontal slices. very slow!
  610. // Input : nID - the id of the tri soup
  611. // vPointA - a point on the tri
  612. // vPointB - a point on the tri
  613. // vPointC - a point on the tri
  614. //-----------------------------------------------------------------------------
  615. void CFoW::AddTri( int nID, Vector &vPointA, Vector &vPointB, Vector &vPointC )
  616. {
  617. Assert( m_TriSoupCollection[ nID ] );
  618. m_TriSoupCollection[ nID ]->AddTri( this, vPointA, vPointB, vPointC );
  619. }
  620. //-----------------------------------------------------------------------------
  621. // Purpose: get access to a tri soup collection object
  622. // Input : nID - the id of the tri soup
  623. // Output : returns the tri soup collection object
  624. //-----------------------------------------------------------------------------
  625. CFoW_TriSoupCollection *CFoW::GetTriSoup( int nID )
  626. {
  627. Assert( m_TriSoupCollection[ nID ] );
  628. return m_TriSoupCollection[ nID ];
  629. }
  630. //-----------------------------------------------------------------------------
  631. // Purpose: add a line occulder from a horizontal slice
  632. // Input : pOccluder - the line occluder to add
  633. // nSliceNum - which slice to add the occluder on
  634. //-----------------------------------------------------------------------------
  635. void CFoW::AddTriSoupOccluder( CFoW_LineOccluder *pOccluder, int nSliceNum )
  636. {
  637. m_pHorizontalSlices[ nSliceNum ]->AddHorizontalOccluder( pOccluder );
  638. }
  639. //-----------------------------------------------------------------------------
  640. // Purpose: get the slice index given the vertical position
  641. // Input : flZPos - the world z position to find the slice for
  642. // Output : returns the slice index or -1 if the position is out of range
  643. //-----------------------------------------------------------------------------
  644. int CFoW::GetHorizontalSlice( float flZPos )
  645. {
  646. if ( m_nVerticalGridSize == 0 || m_pHorizontalSlices == NULL )
  647. {
  648. return -1;
  649. }
  650. #if 0
  651. int nIndex = ( int )( ( flZPos - m_vWorldMins.z ) / m_nVerticalGridSize );
  652. if ( nIndex < 0 )
  653. { // we are getting a z position outside of our world size - potentially bad
  654. return 0;
  655. }
  656. else if ( nIndex >= m_nGridZUnits )
  657. { // we are getting a z position outside of our world size - potentially bad
  658. return m_nGridZUnits - 1;
  659. }
  660. #endif
  661. for ( int nSlice = 0; nSlice < m_nGridZUnits; nSlice++ )
  662. {
  663. if ( flZPos < m_pVerticalLevels[ nSlice ] )
  664. {
  665. return nSlice;
  666. }
  667. }
  668. return -1;
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose:
  672. // Input :
  673. // Output :
  674. //-----------------------------------------------------------------------------
  675. void PrepVisibilityThreaded( CFoW *pFoW )
  676. {
  677. pFoW->PrepVisibility();
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Purpose:
  681. // Input :
  682. // Output :
  683. //-----------------------------------------------------------------------------
  684. void CalcLocalizedVisibilityThreaded( CFoW *pFoW, CFoW_Viewer *pViewer )
  685. {
  686. pViewer->CalcLocalizedVisibility( pFoW );
  687. }
  688. // #define TIME_ME 1
  689. //-----------------------------------------------------------------------------
  690. // Purpose: solve the visibility for all teams and all viewers - slow!
  691. // Input : flFrameTime - the time since the last visibility solve. The amount
  692. // of change in the visibility degree is dependent upon this value.
  693. //-----------------------------------------------------------------------------
  694. void CFoW::SolveVisibility( float flFrameTime )
  695. {
  696. VPROF_BUDGET( "CFoW::SolveVisibility", VPROF_BUDGETGROUP_OTHER_UNACCOUNTED );
  697. #if 1
  698. #ifdef TIME_ME
  699. uint32 nThreadedTime = Plat_MSTime();
  700. #endif // #ifdef TIME_ME
  701. int nRealCount = 1;
  702. for ( int i = 0; i < m_Viewers.Count(); i++ )
  703. {
  704. if ( m_Viewers[ i ] == NULL )
  705. {
  706. continue;
  707. }
  708. #ifdef FOW_SAFETY_DANCE
  709. if ( m_Viewers[ i ]->GetSize() <= 1.0f )
  710. {
  711. Warning( "CFoW: Viewer %d has invalid radius!\n", i );
  712. continue;
  713. }
  714. #endif
  715. if ( m_Viewers[ i ] != NULL && m_Viewers[ i ]->IsDirty() == true )
  716. {
  717. nRealCount++;
  718. }
  719. }
  720. CJob **pJobs = ( CJob ** )stackalloc( sizeof( CJob * ) * nRealCount );
  721. CThreadEvent **pHandles = ( CThreadEvent ** )stackalloc( sizeof( CThreadEvent * ) * nRealCount );
  722. nRealCount = 0;
  723. pJobs[ nRealCount ] = new CFunctorJob( CreateFunctor( ::PrepVisibilityThreaded, this ) );
  724. pJobs[ nRealCount ]->SetFlags( JF_QUEUE );
  725. g_pThreadPool->AddJob( pJobs[ nRealCount ] );
  726. pHandles[ nRealCount ] = pJobs[ nRealCount ]->AccessEvent();
  727. nRealCount++;
  728. // PrepVisibilityThreaded( this );
  729. for ( int i = 0; i < m_Viewers.Count(); i++ )
  730. {
  731. if ( m_Viewers[ i ] == NULL )
  732. continue;
  733. if ( m_Viewers[ i ]->GetSize() <= 1.0f )
  734. {
  735. continue;
  736. }
  737. if ( m_Viewers[ i ] != NULL && m_Viewers[ i ]->IsDirty() == true )
  738. {
  739. pJobs[ nRealCount ] = new CFunctorJob( CreateFunctor( ::CalcLocalizedVisibilityThreaded, this, m_Viewers[ i ] ) );
  740. pJobs[ nRealCount ]->SetFlags( JF_QUEUE );
  741. g_pThreadPool->AddJob( pJobs[ nRealCount ] );
  742. pHandles[ nRealCount ] = pJobs[ nRealCount ]->AccessEvent();
  743. nRealCount++;
  744. // CalcLocalizedVisibilityThreaded( this, m_Viewers[ i ] );
  745. }
  746. }
  747. g_pThreadPool->YieldWait( pHandles, nRealCount, true, TT_INFINITE );
  748. #ifdef TIME_ME
  749. uint32 nMergeTime = Plat_MSTime();
  750. #endif // #ifdef TIME_ME
  751. for ( int i = 0; i < m_Viewers.Count(); i++ )
  752. {
  753. if ( m_Viewers[ i ] )
  754. {
  755. MergeViewerVisibility( i );
  756. }
  757. }
  758. #ifdef TIME_ME
  759. uint32 nUpdateTime = Plat_MSTime();
  760. #endif // #ifdef TIME_ME
  761. UpdateVisibleAmounts( flFrameTime );
  762. #ifdef TIME_ME
  763. uint32 nFinishTime = Plat_MSTime();
  764. Msg( "Thread: %d, Merge: %d, Update: %d, Total %d\n", nMergeTime - nThreadedTime, nUpdateTime - nMergeTime, nFinishTime - nUpdateTime, nFinishTime - nThreadedTime );
  765. #endif // #ifdef TIME_ME
  766. #else
  767. uint32 Time1 = Plat_MSTime();
  768. PrepVisibility();
  769. uint32 Time2 = Plat_MSTime();
  770. double LocalTime = 0.0;
  771. double MergeTime = 0.0;
  772. for ( int i = 0; i < m_Viewers.Count(); i++ )
  773. {
  774. if ( m_Viewers[ i ] )
  775. {
  776. double t1 = Plat_FloatTime();
  777. m_Viewers[ i ]->CalcLocalizedVisibility( this );
  778. double t2 = Plat_FloatTime();
  779. MergeViewerVisibility( i );
  780. double t3 = Plat_FloatTime();
  781. LocalTime += ( t2 - t1 );
  782. MergeTime += ( t3 - t2 );
  783. }
  784. }
  785. uint32 Time3 = Plat_MSTime();
  786. UpdateVisibleAmounts( flFrameTime );
  787. uint32 Time4 = Plat_MSTime();
  788. Msg( "Prep: %d, Local %lg, Merge: %lg, Update: %d, Total: %d\n", Time2-Time1, LocalTime * 1000, MergeTime * 1000, Time4-Time3, Time4 - Time1 );
  789. #endif
  790. }
  791. //-----------------------------------------------------------------------------
  792. // Purpose: returns the visibility info of a location to a team
  793. // Input : nViewerTeam - the team that is doing the viewing of this location
  794. // vLocation - the world location to get the results
  795. // Output : returns bits associated with the visilbity of the grid location
  796. //-----------------------------------------------------------------------------
  797. uint8 CFoW::GetLocationInfo( unsigned nViewerTeam, const Vector &vLocation )
  798. {
  799. int nIndex = GetGridIndex( vLocation, 0.0f, 0.0f, true );
  800. #ifdef FOW_SAFETY_DANCE
  801. if ( nIndex < 0 || nIndex >= m_nTotalHorizontalUnits )
  802. {
  803. Warning( "CFoW: GetLocationInfo() called with invalid view location of %g, %g, %g\n", vLocation.x, vLocation.y, vLocation.z );
  804. return FOW_VG_INVALID;
  805. }
  806. #endif
  807. byte *pDestPos = m_pVisibilityGridFlags[ nViewerTeam ] + nIndex;
  808. return ( *pDestPos );
  809. }
  810. //-----------------------------------------------------------------------------
  811. // Purpose: returns the visibility info of a location to a team
  812. // Input : nViewerTeam - the team that is doing the viewing of this location
  813. // vLocation - the world location to get the results
  814. // Output : returns bits associated with the visilbity of the grid location
  815. //-----------------------------------------------------------------------------
  816. float CFoW::GetLocationVisibilityDegree( unsigned nViewerTeam, const Vector &vLocation, float flRadius )
  817. {
  818. if ( flRadius <= 1.0f )
  819. {
  820. int nGridX, nGridY;
  821. GetGridUnits( vLocation, 0.0f, 0.0f, true, nGridX, nGridY );
  822. int nIndex = ( ( nGridX * m_nGridYUnits ) + nGridY );
  823. #ifdef FOW_SAFETY_DANCE
  824. if ( nIndex < 0 || nIndex >= m_nTotalHorizontalUnits )
  825. {
  826. Warning( "CFoW: GetLocationVisibilityDegree() called with invalid view location of %g, %g, %g\n", vLocation.x, vLocation.y, vLocation.z );
  827. return 0.0f;
  828. }
  829. #endif
  830. float *pDestPos = m_pVisibilityGridDegree[ nViewerTeam ] + nIndex;
  831. return ( ( *pDestPos ) > 1.0f ? 1.0f : ( *pDestPos ) );
  832. }
  833. float flBestDegree = 0.0f;
  834. int nMinGridX, nMinGridY;
  835. int nMaxGridX, nMaxGridY;
  836. Vector vDelta( flRadius + 16.0f, flRadius + 16.0f, 0 );
  837. Vector vMin = vLocation - vDelta;
  838. Vector vMax = vLocation + vDelta;
  839. GetGridUnits( vMin, 0.0f, 0.0f, true, nMinGridX, nMinGridY );
  840. GetGridUnits( vMax, 0.0f, 0.0f, true, nMaxGridX, nMaxGridY );
  841. int nCount = 0;
  842. float flTotal = 0.0f;
  843. for( int nXOffset = nMinGridX; nXOffset <= nMaxGridX; nXOffset++ )
  844. {
  845. if ( nXOffset < 0 || nXOffset >= m_nGridXUnits )
  846. {
  847. continue;
  848. }
  849. for( int nYOffset = nMinGridY; nYOffset <= nMaxGridY; nYOffset++ )
  850. {
  851. if ( nYOffset < 0 || nYOffset >= m_nGridYUnits )
  852. {
  853. continue;
  854. }
  855. int nLocation = ( ( nXOffset * m_nGridYUnits ) + nYOffset );
  856. float *pDestPos = m_pVisibilityGridDegree[ nViewerTeam ] + nLocation;
  857. flTotal += ( ( *pDestPos ) > 1.0f ? 1.0f : ( *pDestPos ) );
  858. nCount++;
  859. #if 0
  860. if ( ( *pDestPos ) > flBestDegree )
  861. {
  862. flBestDegree = ( *pDestPos );
  863. if ( flBestDegree == 1.0f )
  864. {
  865. return 1.0f;
  866. }
  867. }
  868. #endif
  869. }
  870. }
  871. if ( nCount > 0 )
  872. {
  873. flBestDegree = flTotal / nCount;
  874. }
  875. else
  876. {
  877. #ifdef FOW_SAFETY_DANCE
  878. Warning( "CFoW: GetLocationVisibilityDegree() called with invalid view location of %g, %g, %g\n", vLocation.x, vLocation.y, vLocation.z );
  879. #endif // #ifdef FOW_SAFETY_DANCE
  880. }
  881. return flBestDegree;
  882. }
  883. //-----------------------------------------------------------------------------
  884. // Purpose: adds an viewer to the sphere tree
  885. // Input: nIndex - the index of the viewer
  886. //-----------------------------------------------------------------------------
  887. void CFoW::InsertViewerIntoTree( int nIndex )
  888. {
  889. if ( m_Viewers[ nIndex ] != NULL )
  890. {
  891. Sphere_t Bounds;
  892. Bounds.AsVector3D() = m_Viewers[ nIndex ]->GetLocation();
  893. Bounds.w = m_Viewers[ nIndex ]->GetSize();
  894. m_ViewerTree.Insert( (void *)m_Viewers[ nIndex ], &Bounds );
  895. }
  896. }
  897. //-----------------------------------------------------------------------------
  898. // Purpose: removes an viewer from the sphere tree
  899. // Input: nIndex - the index of the viewer
  900. //-----------------------------------------------------------------------------
  901. void CFoW::RemoveViewerFromTree( int nIndex, Vector *pvOldLocation )
  902. {
  903. if ( m_Viewers[ nIndex ] != NULL )
  904. {
  905. Sphere_t Bounds;
  906. if ( pvOldLocation != NULL )
  907. {
  908. Bounds.AsVector3D() = *pvOldLocation;
  909. }
  910. else
  911. {
  912. Bounds.AsVector3D() = m_Viewers[ nIndex ]->GetLocation();
  913. }
  914. Bounds.w = m_Viewers[ nIndex ]->GetSize();
  915. m_ViewerTree.Remove( (void *)m_Viewers[ nIndex ], &Bounds );
  916. }
  917. }
  918. void CFoW::DirtyViewers( Vector &vLocation, float flRadius )
  919. {
  920. // CFoW_RadiusOccluder *pOcculder = m_Occluders[ nOccluderID ];
  921. Sphere_t TestSphere( vLocation.x, vLocation.y, vLocation.z, flRadius );
  922. CFoW_Viewer *FixedPointerArray[ FOW_MAX_VIEWERS_TO_CHECK ];
  923. CUtlVector< void * > FoundViewers( ( void ** )FixedPointerArray, FOW_MAX_VIEWERS_TO_CHECK );
  924. int RealCount = m_ViewerTree.IntersectWithSphere( TestSphere, true, FoundViewers, FOW_MAX_VIEWERS_TO_CHECK );
  925. if ( RealCount > FOW_MAX_VIEWERS_TO_CHECK )
  926. {
  927. // we overflowed, what should we do?
  928. Assert( 0 );
  929. }
  930. for ( int i = 0; i < FoundViewers.Count(); i++ )
  931. {
  932. FixedPointerArray[ i ]->Dirty();
  933. }
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Purpose: adds all occluders back into the visibility tree
  937. //-----------------------------------------------------------------------------
  938. void CFoW::RepopulateOccluders( )
  939. {
  940. m_OccluderTree.RemoveAll();
  941. for ( int i = 0; i < m_Occluders.Count(); i++ )
  942. {
  943. if ( m_Occluders[ i ] != NULL )
  944. {
  945. Sphere_t Bounds;
  946. Bounds.AsVector3D() = m_Occluders[ i ]->GetLocation();
  947. Bounds.w = m_Occluders[ i ]->GetSize();
  948. m_OccluderTree.Insert( (void *)m_Occluders[ i ], &Bounds );
  949. }
  950. }
  951. }
  952. //-----------------------------------------------------------------------------
  953. // Purpose: adds an occluder to the sphere tree
  954. // Input: nIndex - the index of the occluder
  955. //-----------------------------------------------------------------------------
  956. void CFoW::InsertOccluderIntoTree( int nIndex )
  957. {
  958. if ( m_Occluders[ nIndex ] != NULL )
  959. {
  960. Sphere_t Bounds;
  961. Bounds.AsVector3D() = m_Occluders[ nIndex ]->GetLocation();
  962. Bounds.w = m_Occluders[ nIndex ]->GetSize();
  963. m_OccluderTree.Insert( (void *)m_Occluders[ nIndex ], &Bounds );
  964. }
  965. }
  966. //-----------------------------------------------------------------------------
  967. // Purpose: removes an occluder from the sphere tree
  968. // Input: nIndex - the index of the occluder
  969. //-----------------------------------------------------------------------------
  970. void CFoW::RemoveOccluderFromTree( int nIndex )
  971. {
  972. if ( m_Occluders[ nIndex ] != NULL )
  973. {
  974. Sphere_t Bounds;
  975. Bounds.AsVector3D() = m_Occluders[ nIndex ]->GetLocation();
  976. Bounds.w = m_Occluders[ nIndex ]->GetSize();
  977. m_OccluderTree.Remove( (void *)m_Occluders[ nIndex ], &Bounds );
  978. }
  979. }
  980. //-----------------------------------------------------------------------------
  981. // Purpose: defaults the viewing grids
  982. //-----------------------------------------------------------------------------
  983. void CFoW::PrepVisibility( )
  984. {
  985. int nSize = m_nGridXUnits * m_nGridYUnits;
  986. for ( int i = 0; i < m_nNumberOfTeams; i++ )
  987. {
  988. byte *pFlagPtr = m_pVisibilityGridFlags[ i ];
  989. byte *pFlagEndPtr = pFlagPtr + nSize;
  990. for ( ; pFlagPtr < pFlagEndPtr; pFlagPtr++ )
  991. {
  992. if ( ( ( *pFlagPtr ) & ( FOW_VG_IS_VISIBLE ) ) == FOW_VG_IS_VISIBLE )
  993. {
  994. ( *pFlagPtr ) &= ~FOW_VG_IS_VISIBLE;
  995. if ( ( ( *pFlagPtr ) & ( FOW_VG_WAS_VISIBLE ) ) == 0 )
  996. {
  997. ( *pFlagPtr ) |= FOW_VG_WAS_VISIBLE;
  998. }
  999. }
  1000. }
  1001. }
  1002. }
  1003. //-----------------------------------------------------------------------------
  1004. // Purpose: updates the viewer grids
  1005. // Input : flFrameTime - the time since the last visibility solve. The amount
  1006. // of change in the visibility degree is dependent upon this value.
  1007. //-----------------------------------------------------------------------------
  1008. void CFoW::UpdateVisibleAmounts( float flFrameTime )
  1009. {
  1010. int nSize = m_nGridXUnits * m_nGridYUnits;
  1011. flFrameTime /= m_flDegreeFadeRate;
  1012. for ( int i = 0; i < m_nNumberOfTeams; i++ )
  1013. {
  1014. byte *pFlagPtr = m_pVisibilityGridFlags[ i ];
  1015. byte *pFlagEndPtr = pFlagPtr + nSize;
  1016. float *pDegreePtr = m_pVisibilityGridDegree[ i ];
  1017. float *pFadeTimePtr = m_pVisibilityFadeTimer[ i ];
  1018. for ( ; pFlagPtr < pFlagEndPtr; pFlagPtr++, pDegreePtr++, pFadeTimePtr++ )
  1019. {
  1020. if ( ( ( *pFlagPtr ) & FOW_VG_IS_VISIBLE ) != 0 )
  1021. {
  1022. if ( ( *pDegreePtr ) < FOW_OVER_VISIBILITY )
  1023. {
  1024. ( *pDegreePtr ) += flFrameTime;
  1025. if ( ( *pDegreePtr ) > FOW_OVER_VISIBILITY )
  1026. {
  1027. ( *pDegreePtr ) = FOW_OVER_VISIBILITY;
  1028. }
  1029. }
  1030. ( *pFadeTimePtr ) = FOW_FADE_DELAY;
  1031. }
  1032. else
  1033. {
  1034. if ( ( *pFadeTimePtr ) > 0.0f )
  1035. {
  1036. // worry about going negative and using up the remainder?
  1037. ( *pFadeTimePtr ) -= flFrameTime;
  1038. }
  1039. else if ( ( *pDegreePtr ) > 0.0f )
  1040. {
  1041. ( *pDegreePtr ) -= flFrameTime;
  1042. if ( ( *pDegreePtr ) < 0.0f )
  1043. {
  1044. ( *pDegreePtr ) = 0.0f;
  1045. }
  1046. }
  1047. }
  1048. }
  1049. }
  1050. }
  1051. //-----------------------------------------------------------------------------
  1052. // Purpose: given the coords and an offset to move BACK, finds the grid index
  1053. // Input : vCoords - the world coordinates ( z is ignored )
  1054. // flXOffset - the x offset to SUBTRACT from the world coordinates
  1055. // flXOffset - the y offset to SUBTRACT from the world coordinates
  1056. // bGoLower - should we snap to the left/bottom or right/top of the grid
  1057. // Output : returns the index into the grids. returns -1 if it is invalid.
  1058. //-----------------------------------------------------------------------------
  1059. int CFoW::GetGridIndex( const Vector &vCoords, float flXOffset, float flYOffset, bool bGoLower )
  1060. {
  1061. int nGridX, nGridY;
  1062. GetGridUnits( vCoords, flXOffset, flYOffset, bGoLower, nGridX, nGridY );
  1063. #if 0
  1064. if ( nGridX < 0 || nGridX >= m_nGridXUnits ||
  1065. nGridY < 0 || nGridY >= m_nGridYUnits )
  1066. {
  1067. return -1;
  1068. }
  1069. #endif
  1070. return ( nGridX * m_nGridYUnits ) + nGridY;
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Purpose: given the coords and an offset to move BACK, finds the grid location
  1074. // Input : vCoords - the world coordinates ( z is ignored )
  1075. // flXOffset - the x offset to SUBTRACT from the world coordinates
  1076. // flXOffset - the y offset to SUBTRACT from the world coordinates
  1077. // bGoLower - should we snap to the left/bottom or right/top of the grid
  1078. // Output : nGridX - the x grid location
  1079. // nGridY - the y grid location
  1080. //-----------------------------------------------------------------------------
  1081. void CFoW::GetGridUnits( const Vector &vCoords, float flXOffset, float flYOffset, bool bGoLower, int &nGridX, int &nGridY )
  1082. {
  1083. if ( bGoLower )
  1084. {
  1085. nGridX = floor( ( vCoords.x - flXOffset - m_vWorldMins.x ) / m_nHorizontalGridSize );
  1086. nGridY = floor( ( vCoords.y - flYOffset - m_vWorldMins.y ) / m_nHorizontalGridSize );
  1087. }
  1088. else
  1089. {
  1090. nGridX = ceil( ( vCoords.x - flXOffset - m_vWorldMins.x ) / m_nHorizontalGridSize );
  1091. nGridY = ceil( ( vCoords.y - flYOffset - m_vWorldMins.y ) / m_nHorizontalGridSize );
  1092. }
  1093. }
  1094. //-----------------------------------------------------------------------------
  1095. // Purpose:
  1096. // Input :
  1097. // Output :
  1098. //-----------------------------------------------------------------------------
  1099. void CFoW::CenterCoordToGrid( Vector &vCoords )
  1100. {
  1101. int nGridX, nGridY;
  1102. GetGridUnits( vCoords, 0.0f, 0.0f, true, nGridX, nGridY );
  1103. vCoords.x = m_vWorldMins.x + ( nGridX * m_nHorizontalGridSize ) + ( m_nHorizontalGridSize / 2.0f );
  1104. vCoords.y = m_vWorldMins.y + ( nGridY * m_nHorizontalGridSize ) + ( m_nHorizontalGridSize / 2.0f );
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Purpose:
  1108. // Input :
  1109. // Output :
  1110. //-----------------------------------------------------------------------------
  1111. void CFoW::SetDebugVisibility( bool bVisible )
  1112. {
  1113. m_bDebugVisible = bVisible;
  1114. }
  1115. //-----------------------------------------------------------------------------
  1116. // Purpose:
  1117. // Input :
  1118. // Output :
  1119. //-----------------------------------------------------------------------------
  1120. void CFoW::EnableDebugFlags( unsigned nFlags )
  1121. {
  1122. m_nDebugFlags |= nFlags;
  1123. }
  1124. //-----------------------------------------------------------------------------
  1125. // Purpose:
  1126. // Input :
  1127. // Output :
  1128. //-----------------------------------------------------------------------------
  1129. void CFoW::DisableDebugFlags( unsigned nFlags )
  1130. {
  1131. m_nDebugFlags &= ~nFlags;
  1132. }
  1133. //-----------------------------------------------------------------------------
  1134. // Purpose: merge a local viewer's visibility to the global grid
  1135. // Input : nID - the id of the viewer to merge in
  1136. //-----------------------------------------------------------------------------
  1137. void CFoW::MergeViewerVisibility( int nID )
  1138. {
  1139. int nStartX, nStartY, nEndX, nEndY, nWidth, nHeight;
  1140. CFoW_Viewer *pViewer = m_Viewers[ nID ];
  1141. if ( !pViewer )
  1142. {
  1143. return;
  1144. }
  1145. nWidth = nHeight = pViewer->GetGridUnits();
  1146. nStartX = 0;
  1147. nStartY = 0;
  1148. GetGridUnits( pViewer->GetLocation(), pViewer->GetSize(), pViewer->GetSize(), true, nStartX, nStartY );
  1149. nEndX = nStartX + nWidth;
  1150. nEndY = nStartY + nHeight;
  1151. byte *pSrcPos = pViewer->GetVisibility();
  1152. int nIndex = GetGridIndex( pViewer->GetLocation(), pViewer->GetSize(), pViewer->GetSize(), true );
  1153. byte *pDestPos = m_pVisibilityGridFlags[ pViewer->GetTeam() ] + nIndex;
  1154. int nYDestStride = m_nGridYUnits - nHeight;
  1155. int nYSrcStride = 0;
  1156. if ( nStartX < 0 )
  1157. {
  1158. pDestPos += ( -nStartX ) * m_nGridYUnits;
  1159. pSrcPos += ( -nStartX ) * nHeight;
  1160. nStartX = 0;
  1161. }
  1162. if ( nEndX > m_nGridXUnits )
  1163. {
  1164. nEndX -= ( nEndX - m_nGridXUnits );
  1165. }
  1166. if ( nStartY < 0 )
  1167. {
  1168. pDestPos += ( -nStartY );
  1169. pSrcPos += ( -nStartY );
  1170. nYDestStride += ( -nStartY );
  1171. nYSrcStride += ( -nStartY );
  1172. nStartY = 0;
  1173. }
  1174. if ( nEndY > m_nGridYUnits )
  1175. {
  1176. nYDestStride += ( nEndY - m_nGridYUnits );
  1177. nYSrcStride += ( nEndY - m_nGridYUnits );
  1178. nEndY -= ( nEndY - m_nGridYUnits );
  1179. }
  1180. uint8 nViewerHeightGroup = pViewer->GetHeightGroup();
  1181. int nCount = 0;
  1182. for ( int x = nStartX; x < nEndX; x++, pDestPos += nYDestStride, pSrcPos += nYSrcStride)
  1183. {
  1184. for ( int y = nStartY; y < nEndY; y++, pSrcPos++, pDestPos++ )
  1185. {
  1186. nCount++;
  1187. *pDestPos |= *pSrcPos;
  1188. if ( ( ( *pSrcPos ) & FOW_VG_IS_VISIBLE ) != 0 && ( ( *pDestPos ) & FOW_VG_MAX_HEIGHT_GROUP ) < nViewerHeightGroup )
  1189. {
  1190. ( *pDestPos ) = ( ( *pDestPos ) & ( ~FOW_VG_MAX_HEIGHT_GROUP ) ) | nViewerHeightGroup;
  1191. }
  1192. // handle height group in bits
  1193. }
  1194. }
  1195. #ifdef FOW_SAFETY_DANCE
  1196. if ( nCount == 0 )
  1197. { // either radius that is too small ( or invalid ) or the location is off the grid
  1198. Warning( "CFoW: MergeViewerVisibility() Viewer %d has no contribution at location of %g, %g, %g\n", nID, pViewer->GetLocation().x, pViewer->GetLocation().y, pViewer->GetLocation().z );
  1199. }
  1200. #endif
  1201. }
  1202. //-----------------------------------------------------------------------------
  1203. // Purpose:
  1204. // Input :
  1205. // Output :
  1206. //-----------------------------------------------------------------------------
  1207. void CFoW::DrawDebugInfo( Vector &vLocation, float flViewRadius )
  1208. {
  1209. if ( m_bDebugVisible == false )
  1210. {
  1211. return;
  1212. }
  1213. // debugoverlay->AddBoxOverlay( Vector( 0.0f, 0.0f, 0.0f ), Vector( -512.0f, -512.0f, -512.0f ), Vector( 512.0f, 512.0f, 512.0f ), QAngle( 0, 0, 0 ), 0, 255, 0, 16, 0 );
  1214. // debugoverlay->AddSphereOverlay( Vector( 0.0f, 0.0f, 0.0f ), 512.0f, 10, 10, 255, 0, 0, 127, 0 );
  1215. if ( ( m_nDebugFlags & ( FOW_DEBUG_SHOW_GRID ) ) != 0 )
  1216. {
  1217. const float flGridOffset = 4.0f;
  1218. for( int x = 0; x < m_nGridXUnits; x++ )
  1219. {
  1220. float flRealStartX = ( x * m_nHorizontalGridSize ) + m_vWorldMins.x + flGridOffset;
  1221. float flRealEndX = flRealStartX + m_nHorizontalGridSize - flGridOffset - flGridOffset;
  1222. float flCenterX = ( flRealStartX + flRealEndX ) / 2.0f;
  1223. for( int y = 0; y < m_nGridYUnits; y++ )
  1224. {
  1225. float flRealStartY = ( y * m_nHorizontalGridSize ) + m_vWorldMins.y + flGridOffset;
  1226. float flRealEndY = flRealStartY + m_nHorizontalGridSize - flGridOffset - flGridOffset;
  1227. float flCenterY = ( flRealStartY + flRealEndY ) / 2.0f;
  1228. Vector vDiff = Vector( flCenterX, flCenterY, 0.0f ) - vLocation;
  1229. if ( vDiff.Length2D() > flViewRadius + m_nHorizontalGridSize )
  1230. {
  1231. continue;
  1232. }
  1233. int r, g, b;
  1234. int nLocation = ( ( x * m_nGridYUnits ) + y );
  1235. float *pDestPos = m_pVisibilityGridDegree[ 0 ] + nLocation;
  1236. float flValue = ( ( *pDestPos ) > 1.0f ? 1.0f : ( *pDestPos ) );
  1237. g = 255 * flValue;
  1238. r = 255 - g;
  1239. b = 0;
  1240. debugoverlay->AddLineOverlay( Vector( flRealStartX, flRealStartY, 0.0f ), Vector( flRealEndX, flRealStartY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
  1241. debugoverlay->AddLineOverlay( Vector( flRealEndX, flRealStartY, 0.0f ), Vector( flRealEndX, flRealEndY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
  1242. debugoverlay->AddLineOverlay( Vector( flRealEndX, flRealEndY, 0.0f ), Vector( flRealStartX, flRealEndY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
  1243. debugoverlay->AddLineOverlay( Vector( flRealStartX, flRealEndY, 0.0f ), Vector( flRealStartX, flRealStartY, 0.0f ), r, g, b, true, FOW_DEBUG_VIEW_TIME );
  1244. }
  1245. }
  1246. }
  1247. for ( int i = 0; i < m_Viewers.Count(); i++ )
  1248. {
  1249. if ( m_Viewers[ i ] == NULL )
  1250. {
  1251. continue;
  1252. }
  1253. m_Viewers[ i ]->DrawDebugInfo( vLocation, flViewRadius, m_nDebugFlags );
  1254. }
  1255. for ( int i = 0; i < m_Occluders.Count(); i++ )
  1256. {
  1257. if ( m_Occluders[ i ] == NULL )
  1258. {
  1259. continue;
  1260. }
  1261. m_Occluders[ i ]->DrawDebugInfo( vLocation, flViewRadius, m_nDebugFlags );
  1262. }
  1263. }
  1264. //-----------------------------------------------------------------------------
  1265. // Purpose:
  1266. // Input :
  1267. // Output :
  1268. //-----------------------------------------------------------------------------
  1269. void CFoW::PrintStats( )
  1270. {
  1271. int nNumViewers = 0;
  1272. size_t nViewerSize = 0;
  1273. int nNumOccluders = 0;
  1274. size_t nOccluderSize = 0;
  1275. int nNumTriSoupCollections = 0;
  1276. size_t nTriSoupCollectionSize = 0;
  1277. int nNumLineOccluders = 0;
  1278. size_t nLineOccluderSize = 0;
  1279. size_t nTotal = 0;
  1280. for ( int i = 0; i < m_Viewers.Count(); i++ )
  1281. {
  1282. if ( m_Viewers[ i ] == NULL )
  1283. {
  1284. continue;
  1285. }
  1286. nNumViewers++;
  1287. nViewerSize += sizeof( CFoW_Viewer );
  1288. nViewerSize += m_Viewers[ i ]->GetAllocatedMemory();
  1289. }
  1290. for ( int i = 0; i < m_Occluders.Count(); i++ )
  1291. {
  1292. if ( m_Occluders[ i ] == NULL )
  1293. {
  1294. continue;
  1295. }
  1296. nNumOccluders++;
  1297. nOccluderSize += sizeof( CFoW_RadiusOccluder );
  1298. }
  1299. for( int i = 0; i < m_TriSoupCollection.Count(); i++ )
  1300. {
  1301. nNumTriSoupCollections++;
  1302. nTriSoupCollectionSize += sizeof( CFoW_TriSoupCollection );
  1303. for( int j = 0; j < m_TriSoupCollection[ i ]->GetNumOccluders(); j++ )
  1304. {
  1305. // CFoW_LineOccluder *pOccluder = m_TriSoupCollection[ i ]->GetOccluder( j );
  1306. nNumLineOccluders++;
  1307. nLineOccluderSize += sizeof( CFoW_LineOccluder );
  1308. }
  1309. }
  1310. Msg( "FoW Stats\n" );
  1311. Msg( " Num Active Viewers: %d\n", nNumViewers );
  1312. Msg( " Num Active Radius Occluders: %d\n", nNumOccluders );
  1313. Msg( " Num Tri Soup Collections: %d\n", nNumTriSoupCollections );
  1314. Msg( " Num Active Line Occluders: %d\n", nNumLineOccluders );
  1315. Msg( "FoW Memory\n" );
  1316. Msg( " Horizontal Grid Allocation Size: %d\n", m_nHorizontalGridAllocationSize );
  1317. Msg( " Veritcal Grid Allocation Size: %d\n", m_nVerticalGridAllocationSize );
  1318. Msg( " Radius Table Size: %d\n", m_nRadiusTableSize );
  1319. Msg( " Viewers Size: %d\n", nViewerSize );
  1320. Msg( " Radius Occluders Size: %d\n", nOccluderSize );
  1321. Msg( " Tri Soup Collection Size: %d\n", nTriSoupCollectionSize );
  1322. Msg( " Line Occluders Size: %d\n", nLineOccluderSize );
  1323. nTotal = m_nHorizontalGridAllocationSize + m_nVerticalGridAllocationSize + m_nRadiusTableSize + nViewerSize + nOccluderSize + nTriSoupCollectionSize + nLineOccluderSize;
  1324. Msg( " --------------------------------------\n");
  1325. Msg( " Approximate Total Size: %d\n", nTotal );
  1326. }
  1327. //-----------------------------------------------------------------------------
  1328. // Purpose:
  1329. // Input :
  1330. // Output :
  1331. //-----------------------------------------------------------------------------
  1332. void CFoW::GenerateVMF( IFileSystem *pFileSystem, const char *pszFileName )
  1333. {
  1334. KeyValues *kv = new KeyValues( NULL );
  1335. char temp[ 128 ];
  1336. int nCount = 1;
  1337. KeyValues *pWorldKV = new KeyValues( "world" );
  1338. pWorldKV->SetInt( "id", nCount );
  1339. nCount++;
  1340. pWorldKV->SetInt( "mapversion", 22 );
  1341. pWorldKV->SetString( "classname", "worldspawn" );
  1342. pWorldKV->SetInt( "fow", 1 );
  1343. sprintf( temp, "%g %g %g", m_vWorldMins.x, m_vWorldMins.y, m_vWorldMins.z );
  1344. pWorldKV->SetString( "m_vWorldMins", temp );
  1345. sprintf( temp, "%g %g %g", m_vWorldMaxs.x, m_vWorldMaxs.y, m_vWorldMaxs.z );
  1346. pWorldKV->SetString( "m_vWorldMaxs", temp );
  1347. pWorldKV->SetInt( "m_nHorizontalGridSize", m_nHorizontalGridSize );
  1348. pWorldKV->SetInt( "m_nVerticalGridSize", m_nVerticalGridSize );
  1349. pWorldKV->SetInt( "m_nGridXUnits", m_nGridXUnits );
  1350. pWorldKV->SetInt( "m_nGridYUnits", m_nGridYUnits );
  1351. pWorldKV->SetInt( "m_nGridZUnits", m_nGridZUnits );
  1352. for ( int i = 0; i < m_nGridZUnits; i++ )
  1353. {
  1354. sprintf( temp, "m_pVerticalLevels_%d", i );
  1355. pWorldKV->SetFloat( temp, m_pVerticalLevels[ i ] );
  1356. }
  1357. kv->AddSubKey( pWorldKV );
  1358. for( int i = 0; i < m_Viewers.Count(); i++ )
  1359. {
  1360. CFoW_Viewer *pViewer = m_Viewers[ i ];
  1361. if ( pViewer )
  1362. {
  1363. KeyValues *pViewerKV = new KeyValues( "entity" );
  1364. pViewerKV->SetInt( "id", nCount );
  1365. nCount++;
  1366. pViewerKV->SetString( "classname", "env_viewer" );
  1367. pViewerKV->SetFloat( "radius", pViewer->GetSize() );
  1368. sprintf( temp, "%g %g %g", pViewer->GetLocation().x, pViewer->GetLocation().y, pViewer->GetLocation().z );
  1369. pViewerKV->SetString( "origin", temp );
  1370. pViewerKV->SetInt( "height_group", ( int )pViewer->GetHeightGroup() );
  1371. pViewerKV->SetInt( "team", ( int )pViewer->GetTeam() );
  1372. kv->AddSubKey( pViewerKV );
  1373. }
  1374. }
  1375. for( int i = 0; i < m_Occluders.Count(); i++ )
  1376. {
  1377. CFoW_RadiusOccluder *pOccluder = m_Occluders[ i ];
  1378. if ( pOccluder )
  1379. {
  1380. KeyValues *pOccluderKV = new KeyValues( "entity" );
  1381. pOccluderKV->SetInt( "id", nCount );
  1382. nCount++;
  1383. pOccluderKV->SetString( "classname", "env_occluder" );
  1384. pOccluderKV->SetFloat( "radius", pOccluder->GetSize() );
  1385. sprintf( temp, "%g %g %g", pOccluder->GetLocation().x, pOccluder->GetLocation().y, pOccluder->GetLocation().z );
  1386. pOccluderKV->SetString( "origin", temp );
  1387. pOccluderKV->SetInt( "height_group", ( int )pOccluder->GetHeightGroup() );
  1388. kv->AddSubKey( pOccluderKV );
  1389. }
  1390. }
  1391. for( int i = 0; i < m_TriSoupCollection.Count(); i++ )
  1392. {
  1393. for( int j = 0; j < m_TriSoupCollection[ i ]->GetNumOccluders(); j++ )
  1394. {
  1395. CFoW_LineOccluder *pOccluder = m_TriSoupCollection[ i ]->GetOccluder( j );
  1396. if ( pOccluder )
  1397. {
  1398. KeyValues *pOccluderKV = new KeyValues( "entity" );
  1399. pOccluderKV->SetInt( "id", nCount );
  1400. nCount++;
  1401. pOccluderKV->SetString( "classname", "env_line_occluder" );
  1402. sprintf( temp, "%g %g", pOccluder->GetStart().x, pOccluder->GetStart().y );
  1403. pOccluderKV->SetString( "start", temp );
  1404. sprintf( temp, "%g %g", pOccluder->GetEnd().x, pOccluder->GetEnd().y );
  1405. pOccluderKV->SetString( "end", temp );
  1406. sprintf( temp, "%g %g %g", pOccluder->GetPlaneNormal().x, pOccluder->GetPlaneNormal().y, pOccluder->GetPlaneDistance() );
  1407. pOccluderKV->SetString( "plane", temp );
  1408. pOccluderKV->SetInt( "slice_num", pOccluder->GetSliceNum() );
  1409. kv->AddSubKey( pOccluderKV );
  1410. }
  1411. }
  1412. }
  1413. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  1414. for ( KeyValues *pWriteKV = kv->GetFirstSubKey(); pWriteKV != NULL; pWriteKV = pWriteKV->GetNextKey() )
  1415. {
  1416. pWriteKV->RecursiveSaveToFile( buf, 0 );
  1417. }
  1418. pFileSystem->WriteFile( pszFileName, NULL, buf );
  1419. }
  1420. #include <tier0/memdbgoff.h>
  1421. /*
  1422. RJ Optimization Ideas:
  1423. 1. void CFoW_RadiusOccluder::ObstructViewerGrid( CFoW *FoW, CFoW_Viewer *Viewer )
  1424. Don't need the extending sides for point in front of plane checking ( see commented out section )
  1425. 2. void CFoW_Viewer::DefaultViewingArea( CFoW *FoW )
  1426. Do this only initially, set the flag FOW_VG_DEFAULT_VISIBLE, then use the flag from that point on.
  1427. 3. for line blocker, calc start and end angles and sweep between ( rather than doing 360 degree sweep )
  1428. 4. only recalc if an item has moved to a new grid location
  1429. 5. 360 entry tables for cos / acos lookups
  1430. 6. multithread main calc, or sub thread it further? the float grid update can be easily slit up
  1431. 7. obvious radius square calcs to avoid sqrt()
  1432. DONE
  1433. tree to only check radius of things near by
  1434. */