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.

269 lines
6.8 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_generate.cpp
  9. // Auto-generate a Navigation Mesh by sampling the current map
  10. // Author: Michael S. Booth ([email protected]), 2003
  11. #include "cbase.h"
  12. #include "util_shared.h"
  13. #include "nav_mesh.h"
  14. #include "cs_nav_area.h"
  15. #include "cs_nav_node.h"
  16. #include "cs_nav_pathfind.h"
  17. #include "viewport_panel_names.h"
  18. enum { MAX_BLOCKED_AREAS = 256 };
  19. static unsigned int blockedID[ MAX_BLOCKED_AREAS ];
  20. static int blockedIDCount = 0;
  21. static float lastMsgTime = 0.0f;
  22. //ConVar nav_slope_limit( "nav_slope_limit", "0.7", FCVAR_GAMEDLL, "The ground unit normal's Z component must be greater than this for nav areas to be generated." );
  23. ConVar nav_restart_after_analysis( "nav_restart_after_analysis", "1", FCVAR_GAMEDLL, "When nav nav_restart_after_analysis finishes, restart the server. Turning this off can cause crashes, but is useful for incremental generation." );
  24. //--------------------------------------------------------------------------------------------------------------
  25. /**
  26. * Shortest path cost, paying attention to "blocked" areas
  27. */
  28. class ApproachAreaCost
  29. {
  30. public:
  31. // HPE_TODO[pmf]: check that these new parameters are okay to be ignored
  32. float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length )
  33. {
  34. // check if this area is "blocked"
  35. for( int i=0; i<blockedIDCount; ++i )
  36. {
  37. if (area->GetID() == blockedID[i])
  38. {
  39. return -1.0f;
  40. }
  41. }
  42. if (fromArea == NULL)
  43. {
  44. // first area in path, no cost
  45. return 0.0f;
  46. }
  47. else
  48. {
  49. // compute distance traveled along path so far
  50. float dist;
  51. if (ladder)
  52. {
  53. dist = ladder->m_length;
  54. }
  55. else
  56. {
  57. dist = (area->GetCenter() - fromArea->GetCenter()).Length();
  58. }
  59. float cost = dist + fromArea->GetCostSoFar();
  60. return cost;
  61. }
  62. }
  63. };
  64. /*
  65. * Determine the set of "approach areas".
  66. * An approach area is an area representing a place where players
  67. * move into/out of our local neighborhood of areas.
  68. * @todo Optimize by search from eye outward and modifying pathfinder to treat all links as bi-directional
  69. */
  70. void CCSNavArea::ComputeApproachAreas( void )
  71. {
  72. m_approachCount = 0;
  73. if (nav_quicksave.GetBool())
  74. return;
  75. // use the center of the nav area as the "view" point
  76. Vector eye = GetCenter();
  77. if (TheNavMesh->GetGroundHeight( eye, &eye.z ) == false)
  78. return;
  79. // approximate eye position
  80. if (GetAttributes() & NAV_MESH_CROUCH)
  81. eye.z += 0.9f * HalfHumanHeight;
  82. else
  83. eye.z += 0.9f * HumanHeight;
  84. enum { MAX_PATH_LENGTH = 256 };
  85. CNavArea *path[ MAX_PATH_LENGTH ];
  86. ApproachAreaCost cost;
  87. enum SearchType
  88. {
  89. FROM_EYE, ///< start search from our eyepoint outward to farArea
  90. TO_EYE, ///< start search from farArea beack towards our eye
  91. SEARCH_FINISHED
  92. };
  93. //
  94. // In order to *completely* enumerate all of the approach areas, we
  95. // need to search from our eyepoint outward, as well as from outwards
  96. // towards our eyepoint
  97. //
  98. for( int searchType = FROM_EYE; searchType != SEARCH_FINISHED; ++searchType )
  99. {
  100. //
  101. // In order to enumerate all of the approach areas, we need to
  102. // run the algorithm many times, once for each "far away" area
  103. // and keep the union of the approach area sets
  104. //
  105. int it;
  106. for( it = 0; it < TheNavAreas.Count(); ++it )
  107. {
  108. CNavArea *farArea = TheNavAreas[ it ];
  109. blockedIDCount = 0;
  110. // skip the small areas
  111. const float minSize = 200.0f; // 150
  112. Extent extent;
  113. farArea->GetExtent(&extent);
  114. if (extent.SizeX() < minSize || extent.SizeY() < minSize)
  115. {
  116. continue;
  117. }
  118. // if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak
  119. if (farArea->IsVisible( eye ))
  120. {
  121. continue;
  122. }
  123. //
  124. // Keep building paths to farArea and blocking them off until we
  125. // cant path there any more.
  126. // As areas are blocked off, all exits will be enumerated.
  127. //
  128. while( m_approachCount < MAX_APPROACH_AREAS )
  129. {
  130. CNavArea *from, *to;
  131. if (searchType == FROM_EYE)
  132. {
  133. // find another path *to* 'farArea'
  134. // we must pathfind from us in order to pick up one-way paths OUT OF our area
  135. from = this;
  136. to = farArea;
  137. }
  138. else // TO_EYE
  139. {
  140. // find another path *from* 'farArea'
  141. // we must pathfind to us in order to pick up one-way paths INTO our area
  142. from = farArea;
  143. to = this;
  144. }
  145. // build the actual path
  146. if (NavAreaBuildPath( from, to, NULL, cost ) == false)
  147. {
  148. break;
  149. }
  150. // find number of areas on path
  151. int count = 0;
  152. CNavArea *area;
  153. for( area = to; area; area = area->GetParent() )
  154. {
  155. ++count;
  156. }
  157. if (count > MAX_PATH_LENGTH)
  158. {
  159. count = MAX_PATH_LENGTH;
  160. }
  161. // if the path is only two areas long, there can be no approach points
  162. if (count <= 2)
  163. {
  164. break;
  165. }
  166. // build path starting from eye
  167. int i = 0;
  168. if (searchType == FROM_EYE)
  169. {
  170. for( area = to; i < count && area; area = area->GetParent() )
  171. {
  172. path[ count-i-1 ] = area;
  173. ++i;
  174. }
  175. }
  176. else // TO_EYE
  177. {
  178. for( area = to; i < count && area; area = area->GetParent() )
  179. {
  180. path[ i++ ] = area;
  181. }
  182. }
  183. // traverse path to find first area we cannot see (skip the first area)
  184. for( i=1; i<count; ++i )
  185. {
  186. // if we see this area, continue on
  187. if (path[i]->IsVisible( eye ))
  188. {
  189. continue;
  190. }
  191. // we can't see this area - mark this area as "blocked" and unusable by subsequent approach paths
  192. if (blockedIDCount == MAX_BLOCKED_AREAS)
  193. {
  194. Msg( "Overflow computing approach areas for area #%d.\n", GetID());
  195. return;
  196. }
  197. // if the area to be blocked is actually farArea, block the one just prior
  198. // (blocking farArea will cause all subsequent pathfinds to fail)
  199. int block = (path[i] == farArea) ? i-1 : i;
  200. // dont block the start area, or all subsequence pathfinds will fail
  201. if (block == 0)
  202. {
  203. continue;
  204. }
  205. blockedID[ blockedIDCount++ ] = path[ block ]->GetID();
  206. // store new approach area if not already in set
  207. int a;
  208. for( a=0; a<m_approachCount; ++a )
  209. {
  210. if (m_approach[a].here.area == path[block-1])
  211. {
  212. break;
  213. }
  214. }
  215. if (a == m_approachCount)
  216. {
  217. m_approach[ m_approachCount ].prev.area = (block >= 2) ? path[block-2] : NULL;
  218. m_approach[ m_approachCount ].here.area = path[block-1];
  219. m_approach[ m_approachCount ].prevToHereHow = path[block-1]->GetParentHow();
  220. m_approach[ m_approachCount ].next.area = path[block];
  221. m_approach[ m_approachCount ].hereToNextHow = path[block]->GetParentHow();
  222. ++m_approachCount;
  223. }
  224. // we are done with this path
  225. break;
  226. }
  227. }
  228. }
  229. }
  230. }