Team Fortress 2 Source Code as on 22/4/2020
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.

285 lines
8.1 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_simplify.cpp
  9. #include "cbase.h"
  10. #include "nav_mesh.h"
  11. #include "nav_node.h"
  12. // NOTE: This has to be the last file included!
  13. #include "tier0/memdbgon.h"
  14. extern ConVar nav_snap_to_grid;
  15. extern ConVar nav_split_place_on_ground;
  16. extern ConVar nav_coplanar_slope_limit;
  17. extern ConVar nav_coplanar_slope_limit_displacement;
  18. //--------------------------------------------------------------------------------------------------------
  19. static bool ReduceToComponentAreas( CNavArea *area, bool addToSelectedSet )
  20. {
  21. if ( !area )
  22. return false;
  23. bool splitAlongX;
  24. float splitEdge;
  25. const float minSplitSize = 2.0f; // ensure the first split is larger than this
  26. float sizeX = area->GetSizeX();
  27. float sizeY = area->GetSizeY();
  28. CNavArea *first = NULL;
  29. CNavArea *second = NULL;
  30. CNavArea *third = NULL;
  31. CNavArea *fourth = NULL;
  32. bool didSplit = false;
  33. if ( sizeX > GenerationStepSize )
  34. {
  35. splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).x, GenerationStepSize );
  36. if ( splitEdge < area->GetCorner( NORTH_WEST ).x + minSplitSize )
  37. splitEdge += GenerationStepSize;
  38. splitAlongX = false;
  39. didSplit = area->SplitEdit( splitAlongX, splitEdge, &first, &second );
  40. }
  41. if ( sizeY > GenerationStepSize )
  42. {
  43. splitEdge = RoundToUnits( area->GetCorner( NORTH_WEST ).y, GenerationStepSize );
  44. if ( splitEdge < area->GetCorner( NORTH_WEST ).y + minSplitSize )
  45. splitEdge += GenerationStepSize;
  46. splitAlongX = true;
  47. if ( didSplit )
  48. {
  49. didSplit = first->SplitEdit( splitAlongX, splitEdge, &third, &fourth );
  50. didSplit = second->SplitEdit( splitAlongX, splitEdge, &first, &second );
  51. }
  52. else
  53. {
  54. didSplit = area->SplitEdit( splitAlongX, splitEdge, &first, &second );
  55. }
  56. }
  57. if ( !didSplit )
  58. return false;
  59. if ( addToSelectedSet )
  60. {
  61. TheNavMesh->AddToSelectedSet( first );
  62. TheNavMesh->AddToSelectedSet( second );
  63. TheNavMesh->AddToSelectedSet( third );
  64. TheNavMesh->AddToSelectedSet( fourth );
  65. }
  66. ReduceToComponentAreas( first, addToSelectedSet );
  67. ReduceToComponentAreas( second, addToSelectedSet );
  68. ReduceToComponentAreas( third, addToSelectedSet );
  69. ReduceToComponentAreas( fourth, addToSelectedSet );
  70. return true;
  71. }
  72. //--------------------------------------------------------------------------------------------------------
  73. CON_COMMAND_F( nav_chop_selected, "Chops all selected areas into their component 1x1 areas", FCVAR_CHEAT )
  74. {
  75. if ( !UTIL_IsCommandIssuedByServerAdmin() || engine->IsDedicatedServer() )
  76. return;
  77. TheNavMesh->StripNavigationAreas();
  78. TheNavMesh->SetMarkedArea( NULL );
  79. NavAreaCollector collector;
  80. TheNavMesh->ForAllSelectedAreas( collector );
  81. for ( int i=0; i<collector.m_area.Count(); ++i )
  82. {
  83. ReduceToComponentAreas( collector.m_area[i], true );
  84. }
  85. Msg( "%d areas chopped into %d\n", collector.m_area.Count(), TheNavMesh->GetSelecteSetSize() );
  86. }
  87. //--------------------------------------------------------------------------------------------------------
  88. void CNavMesh::RemoveNodes( void )
  89. {
  90. FOR_EACH_VEC( TheNavAreas, it )
  91. {
  92. TheNavAreas[ it ]->ResetNodes();
  93. }
  94. // destroy navigation nodes created during map generation
  95. CNavNode::CleanupGeneration();
  96. }
  97. //--------------------------------------------------------------------------------------------------------
  98. void CNavMesh::GenerateNodes( const Extent &bounds )
  99. {
  100. m_simplifyGenerationExtent = bounds;
  101. m_seedIdx = 0;
  102. Assert( m_generationMode == GENERATE_SIMPLIFY );
  103. while ( SampleStep() )
  104. {
  105. // do nothing
  106. }
  107. }
  108. //--------------------------------------------------------------------------------------------------------
  109. // Simplifies the selected set by reducing to 1x1 areas and re-merging them up with loosened tolerances
  110. void CNavMesh::SimplifySelectedAreas( void )
  111. {
  112. // Save off somve cvars: we need to place nodes on ground, we need snap to grid set, and we loosen slope tolerances
  113. m_generationMode = GENERATE_SIMPLIFY;
  114. bool savedSplitPlaceOnGround = nav_split_place_on_ground.GetBool();
  115. nav_split_place_on_ground.SetValue( 1 );
  116. float savedCoplanarSlopeDisplacementLimit = nav_coplanar_slope_limit_displacement.GetFloat();
  117. nav_coplanar_slope_limit_displacement.SetValue( MIN( 0.5f, savedCoplanarSlopeDisplacementLimit ) );
  118. float savedCoplanarSlopeLimit = nav_coplanar_slope_limit.GetFloat();
  119. nav_coplanar_slope_limit.SetValue( MIN( 0.5f, savedCoplanarSlopeLimit ) );
  120. int savedGrid = nav_snap_to_grid.GetInt();
  121. nav_snap_to_grid.SetValue( 1 );
  122. StripNavigationAreas();
  123. SetMarkedArea( NULL );
  124. NavAreaCollector collector;
  125. ForAllSelectedAreas( collector );
  126. // Select walkable seeds and re-generate nodes in the bounds
  127. ClearWalkableSeeds();
  128. Extent bounds;
  129. bounds.lo.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  130. bounds.hi.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  131. for ( int i=0; i<collector.m_area.Count(); ++i )
  132. {
  133. Extent areaExtent;
  134. CNavArea *area = collector.m_area[i];
  135. area->GetExtent( &areaExtent );
  136. areaExtent.lo.z -= HalfHumanHeight;
  137. areaExtent.hi.z += 2 * HumanHeight;
  138. bounds.Encompass( areaExtent );
  139. Vector center = area->GetCenter();
  140. center.x = SnapToGrid( center.x );
  141. center.y = SnapToGrid( center.y );
  142. Vector normal;
  143. if ( FindGroundForNode( &center, &normal ) )
  144. {
  145. AddWalkableSeed( center, normal );
  146. center.z += HumanHeight;
  147. bounds.Encompass( center );
  148. }
  149. }
  150. RemoveNodes();
  151. GenerateNodes( bounds );
  152. ClearWalkableSeeds();
  153. // Split nav areas up into 1x1 component areas
  154. for ( int i=0; i<collector.m_area.Count(); ++i )
  155. {
  156. ReduceToComponentAreas( collector.m_area[i], true );
  157. }
  158. // Assign nodes to each component area
  159. FOR_EACH_VEC( m_selectedSet, it )
  160. {
  161. CNavArea *area = m_selectedSet[ it ];
  162. Vector corner = area->GetCorner( NORTH_EAST );
  163. Vector normal;
  164. if ( FindGroundForNode( &corner, &normal ) )
  165. {
  166. area->m_node[ NORTH_EAST ] = CNavNode::GetNode( corner );
  167. if ( area->m_node[ NORTH_EAST ] )
  168. {
  169. area->m_node[ NORTH_WEST ] = area->m_node[ NORTH_EAST ]->GetConnectedNode( WEST );
  170. area->m_node[ SOUTH_EAST ] = area->m_node[ NORTH_EAST ]->GetConnectedNode( SOUTH );
  171. if ( area->m_node[ SOUTH_EAST ] )
  172. {
  173. area->m_node[ SOUTH_WEST ] = area->m_node[ SOUTH_EAST ]->GetConnectedNode( WEST );
  174. if ( area->m_node[ NORTH_WEST ] && area->m_node[ SOUTH_WEST ] )
  175. {
  176. area->AssignNodes( area );
  177. }
  178. }
  179. }
  180. }
  181. Assert ( area->m_node[ NORTH_EAST ] && area->m_node[ NORTH_WEST ] && area->m_node[ SOUTH_EAST ] && area->m_node[ SOUTH_WEST ] );
  182. if ( !( area->m_node[ NORTH_EAST ] && area->m_node[ NORTH_WEST ] && area->m_node[ SOUTH_EAST ] && area->m_node[ SOUTH_WEST ] ) )
  183. {
  184. Warning( "Area %d didn't get any nodes!\n", area->GetID() );
  185. }
  186. }
  187. // Run a subset of incremental generation on the component areas
  188. MergeGeneratedAreas();
  189. SquareUpAreas();
  190. MarkJumpAreas();
  191. SplitAreasUnderOverhangs();
  192. MarkStairAreas();
  193. StichAndRemoveJumpAreas();
  194. HandleObstacleTopAreas();
  195. FixUpGeneratedAreas();
  196. // Re-select the new areas
  197. ClearSelectedSet();
  198. FOR_EACH_VEC( TheNavAreas, i )
  199. {
  200. CNavArea *area = TheNavAreas[i];
  201. if ( area->HasNodes() )
  202. {
  203. AddToSelectedSet( area );
  204. }
  205. }
  206. #ifndef _DEBUG
  207. // leave nodes in debug for testing
  208. RemoveNodes();
  209. #endif
  210. m_generationMode = GENERATE_NONE;
  211. nav_split_place_on_ground.SetValue( savedSplitPlaceOnGround );
  212. nav_coplanar_slope_limit_displacement.SetValue( savedCoplanarSlopeDisplacementLimit );
  213. nav_coplanar_slope_limit.SetValue( savedCoplanarSlopeLimit );
  214. nav_snap_to_grid.SetValue( savedGrid );
  215. }
  216. //--------------------------------------------------------------------------------------------------------
  217. CON_COMMAND_F( nav_simplify_selected, "Chops all selected areas into their component 1x1 areas and re-merges them together into larger areas", FCVAR_CHEAT )
  218. {
  219. if ( !UTIL_IsCommandIssuedByServerAdmin() || engine->IsDedicatedServer() )
  220. return;
  221. int selectedSetSize = TheNavMesh->GetSelecteSetSize();
  222. if ( selectedSetSize == 0 )
  223. {
  224. Msg( "nav_simplify_selected only works on the selected set\n" );
  225. return;
  226. }
  227. TheNavMesh->SimplifySelectedAreas();
  228. Msg( "%d areas simplified - %d remain\n", selectedSetSize, TheNavMesh->GetSelecteSetSize() );
  229. }