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.

417 lines
12 KiB

  1. //========= Copyright � 2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the regular grid nav as required by DOTA. Builds, renders,
  4. // and saves out the nav. Traversable edges and cells are determined by
  5. // picking into the world.
  6. //
  7. //=============================================================================//
  8. #include "stdafx.h"
  9. #include "gridnav.h"
  10. #include "render3dms.h"
  11. #include "mapdoc.h"
  12. #include "filesystem.h"
  13. #include "resource.h"
  14. #include "progdlg.h"
  15. bool CGridNav::sm_bEnabled = false;
  16. float CGridNav::sm_flEdgeSize = 0.0f;
  17. float CGridNav::sm_flOffsetX = 0.0f;
  18. float CGridNav::sm_flOffsetY = 0.0f;
  19. float CGridNav::sm_flTraceHeight = 0.0f;
  20. CGridNav::CGridNav()
  21. : m_vLatestCameraPos( Vector( 0.0f, 0.0f, 0.0f ) )
  22. , m_vLatestCameraDir( Vector( 0.0f, 0.0f, 0.0f ) )
  23. , m_bNeedsCameraRecompute( true )
  24. , m_flTimeCameraLastMoved( 0.0f )
  25. , m_nTicksCameraStill( 0 )
  26. , m_bPreviewActive( false )
  27. {
  28. }
  29. void CGridNav::Init( bool bEnabled, float flEdgeSize, float flOffsetX, float flOffsetY, float flTraceHeight )
  30. {
  31. sm_bEnabled = bEnabled;
  32. sm_flEdgeSize = flEdgeSize;
  33. sm_flOffsetX = flOffsetX;
  34. sm_flOffsetY = flOffsetY;
  35. sm_flTraceHeight = flTraceHeight;
  36. }
  37. void CGridNav::Render( CRender3D *pRender, const Vector &vViewPos, const Vector &vViewDir )
  38. {
  39. const float flEdgeSize = sm_flEdgeSize;
  40. const float flHalfEdgeSize = flEdgeSize * 0.5f;
  41. const float flHalfEdgeSizeBuffered = flHalfEdgeSize * 0.95f;
  42. EditorRenderMode_t oldRenderMode = pRender->GetCurrentRenderMode();
  43. Color oldDrawColor;
  44. pRender->GetDrawColor( oldDrawColor );
  45. pRender->SetRenderMode( RENDER_MODE_WIREFRAME );
  46. FOR_EACH_VEC( m_CurrentCells, it )
  47. {
  48. const CGridNavCell &cell = m_CurrentCells[it];
  49. const Color drawColor = ( cell.m_bTraversable ? Color( 0, 255, 0 ) : Color( 255, 0, 0 ) );
  50. const float CELL_DRAW_LEVITATION = 5.0f;
  51. const int i = cell.m_nGridPosX;
  52. const int j = cell.m_nGridPosY;
  53. const Vector vCenter( i * sm_flEdgeSize + sm_flOffsetX, j * sm_flEdgeSize + sm_flOffsetY, cell.m_flHeight + CELL_DRAW_LEVITATION );
  54. const float d = flHalfEdgeSizeBuffered;
  55. const Vector p1( vCenter.x - d, vCenter.y - d, vCenter.z );
  56. const Vector p2( vCenter.x + d, vCenter.y - d, vCenter.z );
  57. const Vector p3( vCenter.x + d, vCenter.y + d, vCenter.z );
  58. const Vector p4( vCenter.x - d, vCenter.y + d, vCenter.z );
  59. pRender->SetDrawColor( drawColor );
  60. pRender->DrawLine( p1, p2 );
  61. pRender->DrawLine( p2, p3 );
  62. pRender->DrawLine( p3, p4 );
  63. pRender->DrawLine( p4, p1 );
  64. }
  65. pRender->SetRenderMode( oldRenderMode );
  66. pRender->SetDrawColor( oldDrawColor );
  67. }
  68. void CGridNav::Update( CMapDoc *pMapDoc, const Vector &vViewPos, const Vector &vViewDir )
  69. {
  70. Assert( pMapDoc );
  71. bool cameraMoving = ( vViewPos != m_vLatestCameraPos || vViewDir != m_vLatestCameraDir );
  72. m_vLatestCameraPos = vViewPos;
  73. m_vLatestCameraDir = vViewDir;
  74. if ( cameraMoving )
  75. {
  76. m_bNeedsCameraRecompute = true;
  77. m_flTimeCameraLastMoved = pMapDoc->GetTime();
  78. m_nTicksCameraStill = 0;
  79. return;
  80. }
  81. if ( !m_bNeedsCameraRecompute )
  82. return;
  83. ++m_nTicksCameraStill;
  84. // don't process until we've been still for long enough both in real time and frame count
  85. if ( pMapDoc->GetTime() - m_flTimeCameraLastMoved < 0.2f || m_nTicksCameraStill < 10 )
  86. {
  87. return;
  88. }
  89. // the camera is still, recompute working set of nav cells
  90. m_bNeedsCameraRecompute = false;
  91. Vector vPickHitPos;
  92. if ( !pMapDoc->PickTrace( vViewPos, vViewDir, &vPickHitPos ) )
  93. return;
  94. m_CurrentCells.RemoveAll();
  95. const int CELL_GRAB_RADIUS = 15;
  96. const float flEdgeSize = sm_flEdgeSize;
  97. const float flHalfEdgeSize = flEdgeSize * 0.5f;
  98. const float flCenterToCornerLen = sqrtf( 2.f ) * flHalfEdgeSize;
  99. const int nCenterI = CoordToGridPosX( vPickHitPos.x );
  100. const int nCenterJ = CoordToGridPosY( vPickHitPos.y );
  101. for ( int j = -CELL_GRAB_RADIUS; j <= CELL_GRAB_RADIUS; ++j )
  102. {
  103. const int curJ = nCenterJ + j;
  104. const float y = GridPosYToCoordCenter( curJ );
  105. for ( int i = -CELL_GRAB_RADIUS; i <= CELL_GRAB_RADIUS; ++i )
  106. {
  107. const int curI = nCenterI + i;
  108. const float x = GridPosXToCoordCenter( curI );
  109. const Vector vTracePos( x, y, sm_flTraceHeight );
  110. Vector vTraceHitPos;
  111. bool bHitClip = false;
  112. if ( !pMapDoc->DropTraceOnDisplacementsAndClips( vTracePos, &vTraceHitPos, &bHitClip ) )
  113. continue;
  114. const float centerHeight = vTraceHitPos.z;
  115. float maxHeight = centerHeight;
  116. // reject cells with centers outside a tolerance of the view vector
  117. const Vector vViewToCenter = vTraceHitPos - vViewPos;
  118. const Vector vViewToCenterDir = vViewToCenter.Normalized();
  119. if ( DotProduct( vViewToCenterDir, vViewDir ) < 0.8f )
  120. {
  121. continue;
  122. }
  123. const float d = flHalfEdgeSize;
  124. Vector cornerTracePoints[] = {
  125. Vector( vTracePos.x - d, vTracePos.y + d, vTracePos.z ),
  126. Vector( vTracePos.x - d, vTracePos.y - d, vTracePos.z ),
  127. Vector( vTracePos.x + d, vTracePos.y + d, vTracePos.z ),
  128. Vector( vTracePos.x + d, vTracePos.y - d, vTracePos.z )
  129. };
  130. bool bTracesOk = true;
  131. bool bSlopesWalkable = true;
  132. for ( int nCorner = 0; nCorner < 4; ++nCorner )
  133. {
  134. Vector vCornerTraceHitPos;
  135. bool bCornerHitClip;
  136. if ( !pMapDoc->DropTraceOnDisplacementsAndClips( cornerTracePoints[nCorner], &vCornerTraceHitPos, &bCornerHitClip ) )
  137. {
  138. bTracesOk = false;
  139. break;
  140. }
  141. else
  142. {
  143. bHitClip = bHitClip || bCornerHitClip;
  144. maxHeight = max( vCornerTraceHitPos.z, maxHeight );
  145. float flDelta = fabs( vCornerTraceHitPos.z - centerHeight );
  146. if ( flDelta > flCenterToCornerLen ) // slope > 45 degrees
  147. {
  148. bSlopesWalkable = false;
  149. }
  150. }
  151. }
  152. if ( !bTracesOk )
  153. continue; // this cell is invalid because we encountered a bad trace, try next cell
  154. CGridNavCell newCell;
  155. newCell.m_nGridPosX = curI;
  156. newCell.m_nGridPosY = curJ;
  157. newCell.m_bTraversable = !bHitClip && bSlopesWalkable;
  158. newCell.m_flHeight = maxHeight;
  159. m_CurrentCells.AddToTail( newCell );
  160. }
  161. }
  162. }
  163. void CGridNav::GenerateGridNavFile( const char *pFileFullPath )
  164. {
  165. Assert( pFileFullPath );
  166. CMapDoc *pMapDoc = CMapDoc::GetActiveMapDoc();
  167. if ( !pMapDoc )
  168. return;
  169. // Error if we can't open the file for writing
  170. if ( g_pFullFileSystem->FileExists( pFileFullPath, NULL ) && !g_pFullFileSystem->IsFileWritable( pFileFullPath, NULL ) )
  171. {
  172. //AfxMessageBox( NULL, "Grid nav file already exists and is not writable. Unable to generate grid nav.", "Error", MB_OK );
  173. AfxMessageBox( "Grid nav file already exists and is not writable. Unable to generate grid nav.");
  174. return;
  175. }
  176. // Progress dialog
  177. CProgressDlg *pProgress = new CProgressDlg;
  178. pProgress->Create();
  179. pProgress->SetStep( 1 );
  180. pProgress->SetWindowText( "Constructing Navigation Grid..." );
  181. pProgress->SetRange( 0, 100 );
  182. // find the edges. Edge test order: NORTH, EAST, SOUTH, WEST. Assumes origin is on map.
  183. const float EDGE_TEST_MAX = 100000.0f;
  184. const float EDGE_TEST_TERMINATE_INTERVAL = 1.0f;
  185. int nGridMinX, nGridMaxX, nGridMinY, nGridMaxY;
  186. int *result[4] = { &nGridMaxY, &nGridMaxX, &nGridMinY, &nGridMinX };
  187. for ( int nDir = 0; nDir < 4; ++nDir )
  188. {
  189. const int nTestAxis = ( nDir + 1 ) % 2;
  190. const int nStillAxis = 1 - nTestAxis;
  191. const float flSign = ( nDir <= 1 ? 1.0f : -1.0f );
  192. float pos[2];
  193. float flCurDelta = EDGE_TEST_MAX * 0.5f;
  194. float flCurDist = EDGE_TEST_MAX * 0.5f;
  195. float flMaxDist = 0.0f;
  196. pos[nStillAxis] = 0.0f;
  197. while ( flCurDelta > EDGE_TEST_TERMINATE_INTERVAL )
  198. {
  199. pos[nTestAxis] = flCurDist * flSign;
  200. // trace here
  201. Vector vTracePos( pos[0], pos[1], sm_flTraceHeight );
  202. float moveDir = -1.0f;
  203. if ( pMapDoc->DropTraceOnDisplacementsAndClips( vTracePos, NULL, NULL ) )
  204. {
  205. // we hit something, must move forward
  206. moveDir = 1.0f;
  207. flMaxDist = flCurDist;
  208. }
  209. flCurDelta *= 0.5f;
  210. flCurDist += flCurDelta * moveDir;
  211. }
  212. (*result[nDir]) = ( nTestAxis == 0 ? CoordToGridPosX( flMaxDist * flSign ) : CoordToGridPosY( flMaxDist * flSign ) );
  213. }
  214. int nGridWidth = nGridMaxX - nGridMinX + 1;
  215. int nGridHeight = nGridMaxY - nGridMinY + 1;
  216. byte writeByte = 0;
  217. int nWriteBitPos = 0;
  218. // open the file for writing
  219. CUtlBuffer fileBuffer( 1024, 1024 );
  220. // .gnv file header
  221. const unsigned int GRID_NAV_MAGIC_NUMBER = 0xFADEBEAD;
  222. fileBuffer.PutInt( GRID_NAV_MAGIC_NUMBER );
  223. fileBuffer.PutFloat( sm_flEdgeSize );
  224. fileBuffer.PutFloat( sm_flOffsetX );
  225. fileBuffer.PutFloat( sm_flOffsetY );
  226. fileBuffer.PutInt( nGridWidth );
  227. fileBuffer.PutInt( nGridHeight );
  228. fileBuffer.PutInt( nGridMinX );
  229. fileBuffer.PutInt( nGridMinY );
  230. const int nMaxProgressVal = nGridHeight * nGridWidth - 1;
  231. const float flEdgeSize = sm_flEdgeSize;
  232. const float flHalfEdgeSize = flEdgeSize * 0.5f;
  233. const float flCenterToCornerLen = sqrtf( 2.f ) * flHalfEdgeSize;
  234. for( int j = 0; j < nGridHeight; ++j )
  235. {
  236. int curJ = nGridMinY + j;
  237. float flCenterY = GridPosYToCoordCenter( curJ );
  238. float prevHeights[2] = { 0.0f, 0.0f }; // northeast, southeast corners
  239. bool prevHitClip[2] = { false, false };
  240. bool bPrevOk = false;
  241. for( int i = 0; i < nGridWidth; ++i )
  242. {
  243. const int nProgressVal = ( 100 * ( j * nGridWidth + i ) ) / nMaxProgressVal;
  244. pProgress->SetPos( nProgressVal );
  245. int curI = nGridMinX + i;
  246. float flCenterX = GridPosXToCoordCenter( curI );
  247. bool bSlopesWalkable = true;
  248. bool bTracesOk = true;
  249. // trace to find the center of this cell
  250. const Vector vTracePos( flCenterX, flCenterY, sm_flTraceHeight );
  251. Vector vTraceHitPos;
  252. bool bHitClip = false;
  253. if ( !pMapDoc->DropTraceOnDisplacementsAndClips( vTracePos, &vTraceHitPos, &bHitClip ) )
  254. {
  255. bTracesOk = false;
  256. }
  257. else
  258. {
  259. const float centerHeight = vTraceHitPos.z;
  260. const float d = flHalfEdgeSize;
  261. Vector cornerTracePoints[] = {
  262. Vector( vTracePos.x - d, vTracePos.y + d, vTracePos.z ),
  263. Vector( vTracePos.x - d, vTracePos.y - d, vTracePos.z ),
  264. Vector( vTracePos.x + d, vTracePos.y + d, vTracePos.z ),
  265. Vector( vTracePos.x + d, vTracePos.y - d, vTracePos.z )
  266. };
  267. for ( int nCorner = 0; nCorner < 4; ++nCorner )
  268. {
  269. // after passing west corners that could have been previously computed, reset ok flag
  270. if ( nCorner == 2 )
  271. {
  272. bPrevOk = true;
  273. }
  274. Vector vCornerTraceHitPos;
  275. bool bCornerHitClip;
  276. // see if we already have the trace results from the previous cell
  277. if ( nCorner <= 1 && bPrevOk )
  278. {
  279. vCornerTraceHitPos = cornerTracePoints[nCorner];
  280. vCornerTraceHitPos.z = prevHeights[nCorner];
  281. bCornerHitClip = prevHitClip[nCorner];
  282. }
  283. else
  284. {
  285. // trace
  286. if ( !pMapDoc->DropTraceOnDisplacementsAndClips( cornerTracePoints[nCorner], &vCornerTraceHitPos, &bCornerHitClip ) )
  287. {
  288. bTracesOk = false;
  289. break;
  290. }
  291. }
  292. // on east 2 corners, store result for next cell
  293. if ( nCorner >= 2 )
  294. {
  295. prevHeights[nCorner-2] = vCornerTraceHitPos.z;
  296. prevHitClip[nCorner-2] = bCornerHitClip;
  297. }
  298. bHitClip = bHitClip || bCornerHitClip;
  299. float flDelta = fabs( vCornerTraceHitPos.z - centerHeight );
  300. if ( flDelta > flCenterToCornerLen ) // slope > 45 degrees
  301. {
  302. bSlopesWalkable = false;
  303. }
  304. }
  305. }
  306. bool bTraversable = !bHitClip && bSlopesWalkable;
  307. if ( !bTracesOk )
  308. {
  309. // this cell is invalid because we encountered a bad trace
  310. bTraversable = false;
  311. bPrevOk = false;
  312. }
  313. // add resulting bit
  314. if ( bTraversable )
  315. {
  316. writeByte |= ( 1 << nWriteBitPos );
  317. }
  318. ++nWriteBitPos;
  319. // write when byte is full
  320. if ( nWriteBitPos >= 8 )
  321. {
  322. fileBuffer.PutUnsignedChar( writeByte );
  323. writeByte = 0;
  324. nWriteBitPos = 0;
  325. }
  326. }
  327. }
  328. // write any trailing bits
  329. if ( nWriteBitPos != 0 )
  330. {
  331. fileBuffer.PutUnsignedChar( writeByte );
  332. }
  333. // write to file
  334. if ( !g_pFullFileSystem->WriteFile( pFileFullPath, NULL, fileBuffer ) )
  335. {
  336. Warning( "Unable to save %d bytes to %s\n", fileBuffer.Size(), pFileFullPath );
  337. }
  338. // Destroy the progress meter
  339. pProgress->DestroyWindow();
  340. delete pProgress;
  341. }