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.

488 lines
12 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_node.cpp
  9. // AI Navigation Nodes
  10. // Author: Michael S. Booth ([email protected]), January 2003
  11. #include "cbase.h"
  12. #include "nav_node.h"
  13. #include "nav_colors.h"
  14. #include "nav_mesh.h"
  15. #include "tier1/utlhash.h"
  16. #include "tier1/generichash.h"
  17. // NOTE: This has to be the last file included!
  18. #include "tier0/memdbgon.h"
  19. NavDirType Opposite[ NUM_DIRECTIONS ] = { SOUTH, WEST, NORTH, EAST };
  20. CNavNode *CNavNode::m_list = NULL;
  21. unsigned int CNavNode::m_listLength = 0;
  22. unsigned int CNavNode::m_nextID = 1;
  23. extern Vector NavTraceMins;
  24. extern Vector NavTraceMaxs;
  25. //--------------------------------------------------------------------------------------------------------------
  26. // Node hash
  27. class CNodeHashFuncs
  28. {
  29. public:
  30. CNodeHashFuncs( int ) {}
  31. bool operator()( const CNavNode *pLhs, const CNavNode *pRhs ) const
  32. {
  33. return pRhs->GetPosition()->AsVector2D() == pLhs->GetPosition()->AsVector2D();
  34. }
  35. unsigned int operator()( const CNavNode *pItem ) const
  36. {
  37. return Hash8( &pItem->GetPosition()->AsVector2D() );
  38. }
  39. };
  40. CUtlHash<CNavNode *, CNodeHashFuncs, CNodeHashFuncs> *g_pNavNodeHash;
  41. //--------------------------------------------------------------------------------------------------------------
  42. /**
  43. * Constructor
  44. */
  45. CNavNode::CNavNode( const Vector &pos, const Vector &normal, CNavNode *parent, bool isOnDisplacement )
  46. {
  47. m_pos = pos;
  48. m_normal = normal;
  49. m_id = m_nextID++;
  50. int i;
  51. for( i=0; i<NUM_DIRECTIONS; ++i )
  52. {
  53. m_to[ i ] = NULL;
  54. m_obstacleHeight[ i ] = 0;
  55. m_obstacleStartDist[ i ] = 0;
  56. m_obstacleEndDist[ i ] = 0;
  57. }
  58. for ( i=0; i<NUM_CORNERS; ++i )
  59. {
  60. m_crouch[ i ] = false;
  61. m_isBlocked[ i ] = false;
  62. }
  63. m_visited = 0;
  64. m_parent = parent;
  65. m_next = m_list;
  66. m_list = this;
  67. m_listLength++;
  68. m_isCovered = false;
  69. m_area = NULL;
  70. m_attributeFlags = 0;
  71. m_isOnDisplacement = isOnDisplacement;
  72. if ( !g_pNavNodeHash )
  73. {
  74. g_pNavNodeHash = new CUtlHash<CNavNode *, CNodeHashFuncs, CNodeHashFuncs>( 16*1024 );
  75. }
  76. bool bDidInsert;
  77. UtlHashHandle_t hHash = g_pNavNodeHash->Insert( this, &bDidInsert );
  78. if ( !bDidInsert )
  79. {
  80. CNavNode *pExistingNode = g_pNavNodeHash->Element( hHash );
  81. m_nextAtXY = pExistingNode;
  82. g_pNavNodeHash->Element( hHash ) = this;
  83. }
  84. else
  85. {
  86. m_nextAtXY = NULL;
  87. }
  88. }
  89. CNavNode::~CNavNode()
  90. {
  91. }
  92. //--------------------------------------------------------------------------------------------------------------
  93. void CNavNode::CleanupGeneration()
  94. {
  95. delete g_pNavNodeHash;
  96. g_pNavNodeHash = NULL;
  97. CNavNode *node, *next;
  98. for( node = CNavNode::m_list; node; node = next )
  99. {
  100. next = node->m_next;
  101. delete node;
  102. }
  103. CNavNode::m_list = NULL;
  104. CNavNode::m_listLength = 0;
  105. CNavNode::m_nextID = 1;
  106. }
  107. //--------------------------------------------------------------------------------------------------------------
  108. #if DEBUG_NAV_NODES
  109. ConVar nav_show_nodes( "nav_show_nodes", "0", FCVAR_CHEAT );
  110. ConVar nav_show_node_id( "nav_show_node_id", "0", FCVAR_CHEAT );
  111. ConVar nav_test_node( "nav_test_node", "0", FCVAR_CHEAT );
  112. ConVar nav_test_node_crouch( "nav_test_node_crouch", "0", FCVAR_CHEAT );
  113. ConVar nav_test_node_crouch_dir( "nav_test_node_crouch_dir", "4", FCVAR_CHEAT );
  114. ConVar nav_show_node_grid( "nav_show_node_grid", "0", FCVAR_CHEAT );
  115. #endif // DEBUG_NAV_NODES
  116. //--------------------------------------------------------------------------------------------------------------
  117. void CNavNode::Draw( void )
  118. {
  119. #if DEBUG_NAV_NODES
  120. if ( !nav_show_nodes.GetBool() )
  121. return;
  122. int r = 0, g = 0, b = 0;
  123. if ( m_isCovered )
  124. {
  125. if ( GetAttributes() & NAV_MESH_CROUCH )
  126. {
  127. b = 255;
  128. }
  129. else
  130. {
  131. r = 255;
  132. }
  133. }
  134. else
  135. {
  136. if ( GetAttributes() & NAV_MESH_CROUCH )
  137. {
  138. b = 255;
  139. }
  140. g = 255;
  141. }
  142. NDebugOverlay::Cross3D( m_pos, 2, r, g, b, true, 0.1f );
  143. if ( (!m_isCovered && nav_show_node_id.GetBool()) || (m_isCovered && nav_show_node_id.GetInt() < 0) )
  144. {
  145. char text[16];
  146. Q_snprintf( text, sizeof( text ), "%d", m_id );
  147. NDebugOverlay::Text( m_pos, text, true, 0.1f );
  148. }
  149. if ( (unsigned int)(nav_test_node.GetInt()) == m_id )
  150. {
  151. TheNavMesh->TestArea( this, 1, 1 );
  152. nav_test_node.SetValue( 0 );
  153. }
  154. if ( (unsigned int)(nav_test_node_crouch.GetInt()) == m_id )
  155. {
  156. CheckCrouch();
  157. nav_test_node_crouch.SetValue( 0 );
  158. }
  159. if ( GetAttributes() & NAV_MESH_CROUCH )
  160. {
  161. int i;
  162. for( i=0; i<NUM_CORNERS; i++ )
  163. {
  164. if ( m_isBlocked[i] || m_crouch[i] )
  165. {
  166. Vector2D dir;
  167. CornerToVector2D( (NavCornerType)i, &dir );
  168. const float scale = 3.0f;
  169. Vector scaled( dir.x * scale, dir.y * scale, 0 );
  170. if ( m_isBlocked[i] )
  171. {
  172. NDebugOverlay::HorzArrow( m_pos, m_pos + scaled, 0.5, 255, 0, 0, 255, true, 0.1f );
  173. }
  174. else
  175. {
  176. NDebugOverlay::HorzArrow( m_pos, m_pos + scaled, 0.5, 0, 0, 255, 255, true, 0.1f );
  177. }
  178. }
  179. }
  180. }
  181. if ( nav_show_node_grid.GetBool() )
  182. {
  183. for ( int i = NORTH; i < NUM_DIRECTIONS; i++ )
  184. {
  185. CNavNode *nodeNext = GetConnectedNode( (NavDirType) i );
  186. if ( nodeNext )
  187. {
  188. NDebugOverlay::Line( *GetPosition(), *nodeNext->GetPosition(), 255, 255, 0, false, 0.1f );
  189. float obstacleHeight = m_obstacleHeight[i];
  190. if ( obstacleHeight > 0 )
  191. {
  192. float z = GetPosition()->z + obstacleHeight;
  193. Vector from = *GetPosition();
  194. Vector to = from;
  195. AddDirectionVector( &to, (NavDirType) i, m_obstacleStartDist[i] );
  196. NDebugOverlay::Line( from, to, 255, 0, 255, false, 0.1f );
  197. from = to;
  198. to.z = z;
  199. NDebugOverlay::Line( from, to, 255, 0, 255, false, 0.1f );
  200. from = to;
  201. to = *GetPosition();
  202. to.z = z;
  203. AddDirectionVector( &to, (NavDirType) i, m_obstacleEndDist[i] );
  204. NDebugOverlay::Line( from, to, 255, 0, 255, false, 0.1f );
  205. }
  206. }
  207. }
  208. }
  209. #endif // DEBUG_NAV_NODES
  210. }
  211. //--------------------------------------------------------------------------------------------------------
  212. // return ground height above node in given corner direction (NUM_CORNERS for highest in any direction)
  213. float CNavNode::GetGroundHeightAboveNode( NavCornerType cornerType ) const
  214. {
  215. if ( cornerType >= 0 && cornerType < NUM_CORNERS )
  216. return m_groundHeightAboveNode[ cornerType ];
  217. float blockedHeight = 0.0f;
  218. for ( int i=0; i<NUM_CORNERS; ++i )
  219. {
  220. blockedHeight = MAX( blockedHeight, m_groundHeightAboveNode[i] );
  221. }
  222. return blockedHeight;
  223. }
  224. //--------------------------------------------------------------------------------------------------------------
  225. /**
  226. * Look up to JumpCrouchHeight in the air to see if we can fit a whole HumanHeight box
  227. */
  228. bool CNavNode::TestForCrouchArea( NavCornerType cornerNum, const Vector& mins, const Vector& maxs, float *groundHeightAboveNode )
  229. {
  230. CTraceFilterWalkableEntities filter( NULL, COLLISION_GROUP_PLAYER_MOVEMENT, WALK_THRU_EVERYTHING );
  231. trace_t tr;
  232. Vector start( m_pos );
  233. Vector end( start );
  234. end.z += JumpCrouchHeight;
  235. UTIL_TraceHull( start, end, NavTraceMins, NavTraceMaxs, MASK_NPCSOLID_BRUSHONLY, &filter, &tr );
  236. float maxHeight = tr.endpos.z - start.z;
  237. Vector realMaxs( maxs );
  238. for ( float height = 0; height <= maxHeight; height += 1.0f )
  239. {
  240. start = m_pos;
  241. start.z += height;
  242. realMaxs.z = HumanCrouchHeight;
  243. UTIL_TraceHull( start, start, mins, realMaxs, MASK_NPCSOLID_BRUSHONLY, &filter, &tr );
  244. if ( !tr.startsolid )
  245. {
  246. *groundHeightAboveNode = start.z - m_pos.z;
  247. // We found a crouch-sized space. See if we can stand up.
  248. realMaxs.z = HumanHeight;
  249. UTIL_TraceHull( start, start, mins, realMaxs, MASK_NPCSOLID_BRUSHONLY, &filter, &tr );
  250. if ( !tr.startsolid )
  251. {
  252. // We found a crouch-sized space. See if we can stand up.
  253. #if DEBUG_NAV_NODES
  254. if ( (unsigned int)(nav_test_node_crouch.GetInt()) == GetID() )
  255. {
  256. NDebugOverlay::Box( start, mins, maxs, 0, 255, 255, 100, 100 );
  257. }
  258. #endif // DEBUG_NAV_NODES
  259. return true;
  260. }
  261. #if DEBUG_NAV_NODES
  262. if ( (unsigned int)(nav_test_node_crouch.GetInt()) == GetID() )
  263. {
  264. NDebugOverlay::Box( start, mins, maxs, 255, 0, 0, 100, 100 );
  265. }
  266. #endif // DEBUG_NAV_NODES
  267. return false;
  268. }
  269. }
  270. *groundHeightAboveNode = JumpCrouchHeight;
  271. m_isBlocked[ cornerNum ] = true;
  272. return false;
  273. }
  274. //--------------------------------------------------------------------------------------------------------------
  275. void CNavNode::CheckCrouch( void )
  276. {
  277. // For each direction, trace upwards from our best ground height to VEC_HULL_MAX.z to see if we have standing room.
  278. for ( int i=0; i<NUM_CORNERS; ++i )
  279. {
  280. #if DEBUG_NAV_NODES
  281. if ( nav_test_node_crouch_dir.GetInt() != NUM_CORNERS && i != nav_test_node_crouch_dir.GetInt() )
  282. continue;
  283. #endif // DEBUG_NAV_NODES
  284. NavCornerType corner = (NavCornerType)i;
  285. Vector2D cornerVec;
  286. CornerToVector2D( corner, &cornerVec );
  287. // Build a mins/maxs pair for the HumanWidth x HalfHumanWidth box facing the appropriate direction
  288. Vector mins( 0, 0, 0 );
  289. Vector maxs( 0, 0, 0 );
  290. if ( cornerVec.x < 0 )
  291. {
  292. mins.x = -HalfHumanWidth;
  293. }
  294. else if ( cornerVec.x > 0 )
  295. {
  296. maxs.x = HalfHumanWidth;
  297. }
  298. if ( cornerVec.y < 0 )
  299. {
  300. mins.y = -HalfHumanWidth;
  301. }
  302. else if ( cornerVec.y > 0 )
  303. {
  304. maxs.y = HalfHumanWidth;
  305. }
  306. maxs.z = HumanHeight;
  307. // now make sure that mins is smaller than maxs
  308. for ( int j=0; j<3; ++j )
  309. {
  310. if ( mins[j] > maxs[j] )
  311. {
  312. float tmp = mins[j];
  313. mins[j] = maxs[j];
  314. maxs[j] = tmp;
  315. }
  316. }
  317. if ( !TestForCrouchArea( corner, mins, maxs, &m_groundHeightAboveNode[i] ) )
  318. {
  319. SetAttributes( NAV_MESH_CROUCH );
  320. m_crouch[corner] = true;
  321. }
  322. }
  323. }
  324. //--------------------------------------------------------------------------------------------------------------
  325. /**
  326. * Create a connection FROM this node TO the given node, in the given direction
  327. */
  328. void CNavNode::ConnectTo( CNavNode *node, NavDirType dir, float obstacleHeight, float obstacleStartDist, float obstacleEndDist )
  329. {
  330. Assert( obstacleStartDist >= 0 && obstacleStartDist <= GenerationStepSize );
  331. Assert( obstacleEndDist >= 0 && obstacleStartDist <= GenerationStepSize );
  332. Assert( obstacleStartDist < obstacleEndDist );
  333. m_to[ dir ] = node;
  334. m_obstacleHeight[ dir ] = obstacleHeight;
  335. m_obstacleStartDist[ dir ] = obstacleStartDist;
  336. m_obstacleEndDist[ dir ] = obstacleEndDist;
  337. }
  338. //--------------------------------------------------------------------------------------------------------------
  339. /**
  340. * Return node at given position.
  341. * @todo Need a hash table to make this lookup fast
  342. */
  343. CNavNode *CNavNode::GetNode( const Vector &pos )
  344. {
  345. const float tolerance = 0.45f * GenerationStepSize; // 1.0f
  346. CNavNode *pNode = NULL;
  347. if ( g_pNavNodeHash )
  348. {
  349. static CNavNode lookup;
  350. lookup.m_pos = pos;
  351. UtlHashHandle_t hNode = g_pNavNodeHash->Find( &lookup );
  352. if ( hNode != g_pNavNodeHash->InvalidHandle() )
  353. {
  354. for( pNode = g_pNavNodeHash->Element( hNode ); pNode; pNode = pNode->m_nextAtXY )
  355. {
  356. float dz = fabs( pNode->m_pos.z - pos.z );
  357. if (dz < tolerance)
  358. {
  359. break;
  360. }
  361. }
  362. }
  363. }
  364. #ifdef DEBUG_NODE_HASH
  365. CNavNode *pTestNode = NULL;
  366. for( CNavNode *node = m_list; node; node = node->m_next )
  367. {
  368. float dx = fabs( node->m_pos.x - pos.x );
  369. float dy = fabs( node->m_pos.y - pos.y );
  370. float dz = fabs( node->m_pos.z - pos.z );
  371. if (dx < tolerance && dy < tolerance && dz < tolerance)
  372. {
  373. pTestNode = node;
  374. break;
  375. }
  376. }
  377. AssertFatal( pTestNode == pNode );
  378. #endif
  379. return pNode;
  380. }
  381. //--------------------------------------------------------------------------------------------------------------
  382. /**
  383. * Return true if this node is bidirectionally linked to
  384. * another node in the given direction
  385. */
  386. BOOL CNavNode::IsBiLinked( NavDirType dir ) const
  387. {
  388. if (m_to[ dir ] && m_to[ dir ]->m_to[ Opposite[dir] ] == this)
  389. {
  390. return true;
  391. }
  392. return false;
  393. }
  394. //--------------------------------------------------------------------------------------------------------------
  395. /**
  396. * Return true if this node is the NW corner of a quad of nodes
  397. * that are all bidirectionally linked.
  398. */
  399. BOOL CNavNode::IsClosedCell( void ) const
  400. {
  401. if (IsBiLinked( SOUTH ) &&
  402. IsBiLinked( EAST ) &&
  403. m_to[ EAST ]->IsBiLinked( SOUTH ) &&
  404. m_to[ SOUTH ]->IsBiLinked( EAST ) &&
  405. m_to[ EAST ]->m_to[ SOUTH ] == m_to[ SOUTH ]->m_to[ EAST ])
  406. {
  407. return true;
  408. }
  409. return false;
  410. }