summaryrefslogtreecommitdiff
path: root/game/server/cstrike/cs_nav_generate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/cstrike/cs_nav_generate.cpp')
-rw-r--r--game/server/cstrike/cs_nav_generate.cpp269
1 files changed, 269 insertions, 0 deletions
diff --git a/game/server/cstrike/cs_nav_generate.cpp b/game/server/cstrike/cs_nav_generate.cpp
new file mode 100644
index 0000000..18e9ffa
--- /dev/null
+++ b/game/server/cstrike/cs_nav_generate.cpp
@@ -0,0 +1,269 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// nav_generate.cpp
+// Auto-generate a Navigation Mesh by sampling the current map
+// Author: Michael S. Booth ([email protected]), 2003
+
+#include "cbase.h"
+#include "util_shared.h"
+#include "nav_mesh.h"
+#include "cs_nav_area.h"
+#include "cs_nav_node.h"
+#include "cs_nav_pathfind.h"
+#include "viewport_panel_names.h"
+
+enum { MAX_BLOCKED_AREAS = 256 };
+static unsigned int blockedID[ MAX_BLOCKED_AREAS ];
+static int blockedIDCount = 0;
+static float lastMsgTime = 0.0f;
+
+
+//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." );
+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." );
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Shortest path cost, paying attention to "blocked" areas
+ */
+class ApproachAreaCost
+{
+public:
+ // HPE_TODO[pmf]: check that these new parameters are okay to be ignored
+ float operator() ( CNavArea *area, CNavArea *fromArea, const CNavLadder *ladder, const CFuncElevator *elevator, float length )
+ {
+ // check if this area is "blocked"
+ for( int i=0; i<blockedIDCount; ++i )
+ {
+ if (area->GetID() == blockedID[i])
+ {
+ return -1.0f;
+ }
+ }
+
+ if (fromArea == NULL)
+ {
+ // first area in path, no cost
+ return 0.0f;
+ }
+ else
+ {
+ // compute distance traveled along path so far
+ float dist;
+
+ if (ladder)
+ {
+ dist = ladder->m_length;
+ }
+ else
+ {
+ dist = (area->GetCenter() - fromArea->GetCenter()).Length();
+ }
+
+ float cost = dist + fromArea->GetCostSoFar();
+
+ return cost;
+ }
+ }
+};
+
+/*
+ * Determine the set of "approach areas".
+ * An approach area is an area representing a place where players
+ * move into/out of our local neighborhood of areas.
+ * @todo Optimize by search from eye outward and modifying pathfinder to treat all links as bi-directional
+ */
+void CCSNavArea::ComputeApproachAreas( void )
+{
+ m_approachCount = 0;
+
+ if (nav_quicksave.GetBool())
+ return;
+
+ // use the center of the nav area as the "view" point
+ Vector eye = m_center;
+ if (TheNavMesh->GetGroundHeight( eye, &eye.z ) == false)
+ return;
+
+ // approximate eye position
+ if (GetAttributes() & NAV_MESH_CROUCH)
+ eye.z += 0.9f * HalfHumanHeight;
+ else
+ eye.z += 0.9f * HumanHeight;
+
+ enum { MAX_PATH_LENGTH = 256 };
+ CNavArea *path[ MAX_PATH_LENGTH ];
+ ApproachAreaCost cost;
+
+ enum SearchType
+ {
+ FROM_EYE, ///< start search from our eyepoint outward to farArea
+ TO_EYE, ///< start search from farArea beack towards our eye
+ SEARCH_FINISHED
+ };
+
+ //
+ // In order to *completely* enumerate all of the approach areas, we
+ // need to search from our eyepoint outward, as well as from outwards
+ // towards our eyepoint
+ //
+ for( int searchType = FROM_EYE; searchType != SEARCH_FINISHED; ++searchType )
+ {
+ //
+ // In order to enumerate all of the approach areas, we need to
+ // run the algorithm many times, once for each "far away" area
+ // and keep the union of the approach area sets
+ //
+ int it;
+ for( it = 0; it < TheNavAreas.Count(); ++it )
+ {
+ CNavArea *farArea = TheNavAreas[ it ];
+
+ blockedIDCount = 0;
+
+ // skip the small areas
+ const float minSize = 200.0f; // 150
+ Extent extent;
+ farArea->GetExtent(&extent);
+ if (extent.SizeX() < minSize || extent.SizeY() < minSize)
+ {
+ continue;
+ }
+
+ // if we can see 'farArea', try again - the whole point is to go "around the bend", so to speak
+ if (farArea->IsVisible( eye ))
+ {
+ continue;
+ }
+
+ //
+ // Keep building paths to farArea and blocking them off until we
+ // cant path there any more.
+ // As areas are blocked off, all exits will be enumerated.
+ //
+ while( m_approachCount < MAX_APPROACH_AREAS )
+ {
+ CNavArea *from, *to;
+
+ if (searchType == FROM_EYE)
+ {
+ // find another path *to* 'farArea'
+ // we must pathfind from us in order to pick up one-way paths OUT OF our area
+ from = this;
+ to = farArea;
+ }
+ else // TO_EYE
+ {
+ // find another path *from* 'farArea'
+ // we must pathfind to us in order to pick up one-way paths INTO our area
+ from = farArea;
+ to = this;
+ }
+
+ // build the actual path
+ if (NavAreaBuildPath( from, to, NULL, cost ) == false)
+ {
+ break;
+ }
+
+ // find number of areas on path
+ int count = 0;
+ CNavArea *area;
+ for( area = to; area; area = area->GetParent() )
+ {
+ ++count;
+ }
+
+ if (count > MAX_PATH_LENGTH)
+ {
+ count = MAX_PATH_LENGTH;
+ }
+
+ // if the path is only two areas long, there can be no approach points
+ if (count <= 2)
+ {
+ break;
+ }
+
+ // build path starting from eye
+ int i = 0;
+
+ if (searchType == FROM_EYE)
+ {
+ for( area = to; i < count && area; area = area->GetParent() )
+ {
+ path[ count-i-1 ] = area;
+ ++i;
+ }
+ }
+ else // TO_EYE
+ {
+ for( area = to; i < count && area; area = area->GetParent() )
+ {
+ path[ i++ ] = area;
+ }
+ }
+
+ // traverse path to find first area we cannot see (skip the first area)
+ for( i=1; i<count; ++i )
+ {
+ // if we see this area, continue on
+ if (path[i]->IsVisible( eye ))
+ {
+ continue;
+ }
+
+ // we can't see this area - mark this area as "blocked" and unusable by subsequent approach paths
+ if (blockedIDCount == MAX_BLOCKED_AREAS)
+ {
+ Msg( "Overflow computing approach areas for area #%d.\n", GetID());
+ return;
+ }
+
+ // if the area to be blocked is actually farArea, block the one just prior
+ // (blocking farArea will cause all subsequent pathfinds to fail)
+ int block = (path[i] == farArea) ? i-1 : i;
+
+ // dont block the start area, or all subsequence pathfinds will fail
+ if (block == 0)
+ {
+ continue;
+ }
+
+ blockedID[ blockedIDCount++ ] = path[ block ]->GetID();
+
+ // store new approach area if not already in set
+ int a;
+ for( a=0; a<m_approachCount; ++a )
+ {
+ if (m_approach[a].here.area == path[block-1])
+ {
+ break;
+ }
+ }
+
+ if (a == m_approachCount)
+ {
+ m_approach[ m_approachCount ].prev.area = (block >= 2) ? path[block-2] : NULL;
+
+ m_approach[ m_approachCount ].here.area = path[block-1];
+ m_approach[ m_approachCount ].prevToHereHow = path[block-1]->GetParentHow();
+
+ m_approach[ m_approachCount ].next.area = path[block];
+ m_approach[ m_approachCount ].hereToNextHow = path[block]->GetParentHow();
+
+ ++m_approachCount;
+ }
+
+ // we are done with this path
+ break;
+ }
+ }
+ }
+ }
+}