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.
1011 lines
40 KiB
1011 lines
40 KiB
//========= Copyright Š 1996-2005, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
// nav_mesh.h |
|
// The Navigation Mesh interface |
|
// Author: Michael S. Booth (mike@turtlerockstudios.com), January 2003 |
|
|
|
// |
|
// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003 |
|
// |
|
// NOTE: The Navigation code uses Doxygen-style comments. If you run Doxygen over this code, it will |
|
// auto-generate documentation. Visit www.doxygen.org to download the system for free. |
|
// |
|
|
|
#ifndef _NAV_MESH_H_ |
|
#define _NAV_MESH_H_ |
|
|
|
#include "utlbuffer.h" |
|
#include "filesystem.h" |
|
#include "GameEventListener.h" |
|
|
|
#include "nav.h" |
|
#include "nav_area.h" |
|
#include "nav_colors.h" |
|
|
|
|
|
class CNavArea; |
|
class CBaseEntity; |
|
class CBreakable; |
|
|
|
extern ConVar nav_edit; |
|
extern ConVar nav_quicksave; |
|
extern ConVar nav_show_approach_points; |
|
extern ConVar nav_show_danger; |
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
class NavAreaCollector |
|
{ |
|
bool m_checkForDuplicates; |
|
public: |
|
NavAreaCollector( bool checkForDuplicates = false ) |
|
{ |
|
m_checkForDuplicates = checkForDuplicates; |
|
} |
|
|
|
bool operator() ( CNavArea *area ) |
|
{ |
|
if ( m_checkForDuplicates && m_area.HasElement( area ) ) |
|
return true; |
|
|
|
m_area.AddToTail( area ); |
|
return true; |
|
} |
|
CUtlVector< CNavArea * > m_area; |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
class EditDestroyNotification |
|
{ |
|
CNavArea *m_deadArea; |
|
|
|
public: |
|
EditDestroyNotification( CNavArea *deadArea ) |
|
{ |
|
m_deadArea = deadArea; |
|
} |
|
|
|
bool operator()( CBaseCombatCharacter *actor ) |
|
{ |
|
actor->OnNavAreaRemoved( m_deadArea ); |
|
return true; |
|
} |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
class NavAttributeClearer |
|
{ |
|
public: |
|
NavAttributeClearer( NavAttributeType attribute ) |
|
{ |
|
m_attribute = attribute; |
|
} |
|
|
|
bool operator() ( CNavArea *area ) |
|
{ |
|
area->SetAttributes( area->GetAttributes() & (~m_attribute) ); |
|
|
|
return true; |
|
} |
|
|
|
NavAttributeType m_attribute; |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
class NavAttributeSetter |
|
{ |
|
public: |
|
NavAttributeSetter( NavAttributeType attribute ) |
|
{ |
|
m_attribute = attribute; |
|
} |
|
|
|
bool operator() ( CNavArea *area ) |
|
{ |
|
area->SetAttributes( area->GetAttributes() | m_attribute ); |
|
|
|
return true; |
|
} |
|
|
|
NavAttributeType m_attribute; |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
class NavAttributeToggler |
|
{ |
|
public: |
|
NavAttributeToggler( NavAttributeType attribute ) |
|
{ |
|
m_attribute = attribute; |
|
} |
|
|
|
bool operator() ( CNavArea *area ); |
|
|
|
NavAttributeType m_attribute; |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
struct NavAttributeLookup |
|
{ |
|
const char *name; |
|
NavAttributeType attribute; |
|
}; |
|
|
|
extern NavAttributeLookup TheNavAttributeTable[]; |
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
class SelectOverlappingAreas |
|
{ |
|
public: |
|
bool operator()( CNavArea *area ); |
|
}; |
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
abstract_class INavAvoidanceObstacle |
|
{ |
|
public: |
|
virtual bool IsPotentiallyAbleToObstructNavAreas( void ) const = 0; // could we at some future time obstruct nav? |
|
virtual float GetNavObstructionHeight( void ) const = 0; // height at which to obstruct nav areas |
|
virtual bool CanObstructNavAreas( void ) const = 0; // can we obstruct nav right this instant? |
|
virtual CBaseEntity *GetObstructingEntity( void ) = 0; |
|
virtual void OnNavMeshLoaded( void ) = 0; |
|
}; |
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
enum GetNavAreaFlags_t |
|
{ |
|
GETNAVAREA_CHECK_LOS = 0x1, |
|
GETNAVAREA_ALLOW_BLOCKED_AREAS = 0x2, |
|
GETNAVAREA_CHECK_GROUND = 0x4, |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
// for nav mesh visibilty computation |
|
struct NavVisPair_t |
|
{ |
|
void SetPair( CNavArea *pArea1, CNavArea *pArea2 ) |
|
{ |
|
int iArea1 = (int)( pArea1 > pArea2 ); |
|
int iArea2 = ( iArea1 + 1 ) % 2; |
|
pAreas[iArea1] = pArea1; |
|
pAreas[iArea2] = pArea2; |
|
} |
|
|
|
CNavArea *pAreas[2]; |
|
}; |
|
|
|
|
|
// for nav mesh visibilty computation |
|
class CVisPairHashFuncs |
|
{ |
|
public: |
|
CVisPairHashFuncs( int ) {} |
|
|
|
bool operator()( const NavVisPair_t &lhs, const NavVisPair_t &rhs ) const |
|
{ |
|
return ( lhs.pAreas[0] == rhs.pAreas[0] && lhs.pAreas[1] == rhs.pAreas[1] ); |
|
} |
|
|
|
unsigned int operator()( const NavVisPair_t &item ) const |
|
{ |
|
int key[2] = { (int)item.pAreas[0] + item.pAreas[1]->GetID(), (int)item.pAreas[1] + item.pAreas[0]->GetID() }; |
|
return Hash8( key ); |
|
} |
|
}; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------- |
|
/** |
|
* The CNavMesh is the global interface to the Navigation Mesh. |
|
* @todo Make this an abstract base class interface, and derive mod-specific implementations. |
|
*/ |
|
class CNavMesh : public CGameEventListener |
|
{ |
|
public: |
|
CNavMesh( void ); |
|
virtual ~CNavMesh(); |
|
|
|
virtual void PreLoadAreas( int nAreas ) {} |
|
virtual CNavArea *CreateArea( void ) const; // CNavArea factory |
|
virtual void DestroyArea( CNavArea * ) const; |
|
virtual HidingSpot *CreateHidingSpot( void ) const; // Hiding Spot factory |
|
|
|
virtual void Reset( void ); // destroy Navigation Mesh data and revert to initial state |
|
virtual void Update( void ); // invoked on each game frame |
|
|
|
virtual void FireGameEvent( IGameEvent *event ); // incoming event processing |
|
|
|
virtual NavErrorType Load( void ); // load navigation data from a file |
|
virtual NavErrorType PostLoad( unsigned int version ); // (EXTEND) invoked after all areas have been loaded - for pointer binding, etc |
|
bool IsLoaded( void ) const { return m_isLoaded; } // return true if a Navigation Mesh has been loaded |
|
bool IsAnalyzed( void ) const { return m_isAnalyzed; } // return true if a Navigation Mesh has been analyzed |
|
|
|
/** |
|
* Return true if nav mesh can be trusted for all climbing/jumping decisions because game environment is fairly simple. |
|
* Authoritative meshes mean path followers can skip CPU intensive realtime scanning of unpredictable geometry. |
|
*/ |
|
virtual bool IsAuthoritative( void ) const { return false; } |
|
|
|
const CUtlVector< Place > *GetPlacesFromNavFile( bool *hasUnnamedPlaces ); // Reads the used place names from the nav file (can be used to selectively precache before the nav is loaded) |
|
|
|
virtual bool Save( void ) const; // store Navigation Mesh to a file |
|
bool IsOutOfDate( void ) const { return m_isOutOfDate; } // return true if the Navigation Mesh is older than the current map version |
|
|
|
virtual unsigned int GetSubVersionNumber( void ) const; // returns sub-version number of data format used by derived classes |
|
virtual void SaveCustomData( CUtlBuffer &fileBuffer ) const { } // store custom mesh data for derived classes |
|
virtual void LoadCustomData( CUtlBuffer &fileBuffer, unsigned int subVersion ) { } // load custom mesh data for derived classes |
|
virtual void SaveCustomDataPreArea( CUtlBuffer &fileBuffer ) const { } // store custom mesh data for derived classes that needs to be loaded before areas are read in |
|
virtual void LoadCustomDataPreArea( CUtlBuffer &fileBuffer, unsigned int subVersion ) { } // load custom mesh data for derived classes that needs to be loaded before areas are read in |
|
|
|
// events |
|
virtual void OnServerActivate( void ); // (EXTEND) invoked when server loads a new map |
|
virtual void OnRoundRestart( void ); // invoked when a game round restarts |
|
virtual void OnRoundRestartPreEntity( void ); // invoked when a game round restarts, but before entities are deleted and recreated |
|
virtual void OnBreakableCreated( CBaseEntity *breakable ) { } // invoked when a breakable is created |
|
virtual void OnBreakableBroken( CBaseEntity *broken ) { } // invoked when a breakable is broken |
|
virtual void OnAreaBlocked( CNavArea *area ); // invoked when the area becomes blocked |
|
virtual void OnAreaUnblocked( CNavArea *area ); // invoked when the area becomes un-blocked |
|
virtual void OnAvoidanceObstacleEnteredArea( CNavArea *area ); // invoked when the area becomes obstructed |
|
virtual void OnAvoidanceObstacleLeftArea( CNavArea *area ); // invoked when the area becomes un-obstructed |
|
|
|
virtual void OnEditCreateNotify( CNavArea *newArea ); // invoked when given area has just been added to the mesh in edit mode |
|
virtual void OnEditDestroyNotify( CNavArea *deadArea ); // invoked when given area has just been deleted from the mesh in edit mode |
|
virtual void OnEditDestroyNotify( CNavLadder *deadLadder ); // invoked when given ladder has just been deleted from the mesh in edit mode |
|
virtual void OnNodeAdded( CNavNode *node ) {}; |
|
|
|
// Obstructions |
|
void RegisterAvoidanceObstacle( INavAvoidanceObstacle *obstruction ); |
|
void UnregisterAvoidanceObstacle( INavAvoidanceObstacle *obstruction ); |
|
const CUtlVector< INavAvoidanceObstacle * > &GetObstructions( void ) const { return m_avoidanceObstacles; } |
|
|
|
unsigned int GetNavAreaCount( void ) const { return m_areaCount; } // return total number of nav areas |
|
|
|
// See GetNavAreaFlags_t for flags |
|
CNavArea *GetNavArea( const Vector &pos, float beneathLimt = 120.0f ) const; // given a position, return the nav area that IsOverlapping and is *immediately* beneath it |
|
CNavArea *GetNavArea( CBaseEntity *pEntity, int nGetNavAreaFlags, float flBeneathLimit = 120.0f ) const; |
|
CNavArea *GetNavAreaByID( unsigned int id ) const; |
|
CNavArea *GetNearestNavArea( const Vector &pos, bool anyZ = false, float maxDist = 10000.0f, bool checkLOS = false, bool checkGround = true ) const; |
|
CNavArea *GetNearestNavArea( CBaseEntity *pEntity, int nGetNavAreaFlags = GETNAVAREA_CHECK_GROUND, float maxDist = 10000.0f ) const; |
|
|
|
Place GetPlace( const Vector &pos ) const; // return Place at given coordinate |
|
const char *PlaceToName( Place place ) const; // given a place, return its name |
|
Place NameToPlace( const char *name ) const; // given a place name, return a place ID or zero if no place is defined |
|
Place PartialNameToPlace( const char *name ) const; // given the first part of a place name, return a place ID or zero if no place is defined, or the partial match is ambiguous |
|
void PrintAllPlaces( void ) const; // output a list of names to the console |
|
int PlaceNameAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ); // Given a partial place name, fill in possible place names for ConCommand autocomplete |
|
|
|
bool GetGroundHeight( const Vector &pos, float *height, Vector *normal = NULL ) const; // get the Z coordinate of the topmost ground level below the given point |
|
bool GetSimpleGroundHeight( const Vector &pos, float *height, Vector *normal = NULL ) const;// get the Z coordinate of the ground level directly below the given point |
|
|
|
|
|
/// increase "danger" weights in the given nav area and nearby ones |
|
void IncreaseDangerNearby( int teamID, float amount, CNavArea *area, const Vector &pos, float maxRadius, float dangerLimit = -1.0f ); |
|
void DrawDanger( void ) const; // draw the current danger levels |
|
|
|
void DrawPlayerCounts( void ) const; // draw the current player counts for each area |
|
|
|
//------------------------------------------------------------------------------------- |
|
// Auto-generation |
|
// |
|
#define INCREMENTAL_GENERATION true |
|
void BeginGeneration( bool incremental = false ); // initiate the generation process |
|
void BeginAnalysis( bool quitWhenFinished = false ); // re-analyze an existing Mesh. Determine Hiding Spots, Encounter Spots, etc. |
|
|
|
bool IsGenerating( void ) const { return m_generationMode != GENERATE_NONE; } // return true while a Navigation Mesh is being generated |
|
const char *GetPlayerSpawnName( void ) const; // return name of player spawn entity |
|
void SetPlayerSpawnName( const char *name ); // define the name of player spawn entities |
|
void AddWalkableSeed( const Vector &pos, const Vector &normal ); // add given walkable position to list of seed positions for map sampling |
|
virtual void AddWalkableSeeds( void ); // adds walkable positions for any/all positions a mod specifies |
|
void ClearWalkableSeeds( void ) { m_walkableSeeds.RemoveAll(); } // erase all walkable seed positions |
|
void MarkStairAreas( void ); |
|
|
|
virtual unsigned int GetGenerationTraceMask( void ) const; // return the mask used by traces when generating the mesh |
|
|
|
|
|
//------------------------------------------------------------------------------------- |
|
// Edit mode |
|
// |
|
unsigned int GetNavPlace( void ) const { return m_navPlace; } |
|
void SetNavPlace( unsigned int place ) { m_navPlace = place; } |
|
|
|
// Edit callbacks from ConCommands |
|
void CommandNavDelete( void ); // delete current area |
|
void CommandNavDeleteMarked( void ); // delete current marked area |
|
|
|
virtual void CommandNavFloodSelect( const CCommand &args ); // select current area and all connected areas, recursively |
|
void CommandNavToggleSelectedSet( void ); // toggles all areas into/out of the selected set |
|
void CommandNavStoreSelectedSet( void ); // stores the current selected set for later |
|
void CommandNavRecallSelectedSet( void ); // restores an older selected set |
|
void CommandNavAddToSelectedSet( void ); // add current area to selected set |
|
void CommandNavAddToSelectedSetByID( const CCommand &args ); // add specified area id to selected set |
|
void CommandNavRemoveFromSelectedSet( void ); // remove current area from selected set |
|
void CommandNavToggleInSelectedSet( void ); // add/remove current area from selected set |
|
void CommandNavClearSelectedSet( void ); // clear the selected set to empty |
|
void CommandNavBeginSelecting( void ); // start continuously selecting areas into the selected set |
|
void CommandNavEndSelecting( void ); // stop continuously selecting areas into the selected set |
|
void CommandNavBeginDragSelecting( void ); // start dragging a selection area |
|
void CommandNavEndDragSelecting( void ); // stop dragging a selection area |
|
void CommandNavBeginDragDeselecting( void ); // start dragging a deselection area |
|
void CommandNavEndDragDeselecting( void ); // stop dragging a deselection area |
|
void CommandNavRaiseDragVolumeMax( void ); // raise the top of the drag volume |
|
void CommandNavLowerDragVolumeMax( void ); // lower the top of the drag volume |
|
void CommandNavRaiseDragVolumeMin( void ); // raise the bottom of the drag volume |
|
void CommandNavLowerDragVolumeMin( void ); // lower the bottom of the drag volume |
|
void CommandNavToggleSelecting( bool playSound = true ); // start/stop continuously selecting areas into the selected set |
|
void CommandNavBeginDeselecting( void ); // start continuously de-selecting areas from the selected set |
|
void CommandNavEndDeselecting( void ); // stop continuously de-selecting areas from the selected set |
|
void CommandNavToggleDeselecting( bool playSound = true ); // start/stop continuously de-selecting areas from the selected set |
|
void CommandNavSelectInvalidAreas( void ); // adds invalid areas to the selected set |
|
void CommandNavSelectBlockedAreas( void ); // adds blocked areas to the selected set |
|
void CommandNavSelectObstructedAreas( void ); // adds obstructed areas to the selected set |
|
void CommandNavSelectDamagingAreas( void ); // adds damaging areas to the selected set |
|
void CommandNavSelectHalfSpace( const CCommand &args ); // selects all areas that intersect the half-space |
|
void CommandNavSelectStairs( void ); // adds stairs areas to the selected set |
|
|
|
void CommandNavSplit( void ); // split current area |
|
void CommandNavMerge( void ); // merge adjacent areas |
|
void CommandNavMark( const CCommand &args ); // mark an area for further operations |
|
void CommandNavUnmark( void ); // removes the mark |
|
|
|
void CommandNavBeginArea( void ); // begin creating a new nav area |
|
void CommandNavEndArea( void ); // end creation of the new nav area |
|
|
|
void CommandNavBeginShiftXY( void ); // begin shifting selected set in the XY plane |
|
void CommandNavEndShiftXY( void ); // end shifting selected set in the XY plane |
|
|
|
void CommandNavConnect( void ); // connect marked area to selected area |
|
void CommandNavDisconnect( void ); // disconnect marked area from selected area |
|
void CommandNavSplice( void ); // create new area in between marked and selected areas |
|
void CommandNavCrouch( void ); // toggle crouch attribute on current area |
|
void CommandNavTogglePlaceMode( void ); // switch between normal and place editing |
|
void CommandNavSetPlaceMode( void ); // switch between normal and place editing |
|
void CommandNavPlaceFloodFill( void ); // floodfill areas out from current area |
|
void CommandNavPlaceSet( void ); // sets the Place for the selected set |
|
void CommandNavPlacePick( void ); // "pick up" the place at the current area |
|
void CommandNavTogglePlacePainting( void ); // switch between "painting" places onto areas |
|
void CommandNavMarkUnnamed( void ); // mark an unnamed area for further operations |
|
void CommandNavCornerSelect( void ); // select a corner on the current area |
|
void CommandNavCornerRaise( const CCommand &args ); // raise a corner on the current area |
|
void CommandNavCornerLower( const CCommand &args ); // lower a corner on the current area |
|
void CommandNavCornerPlaceOnGround( const CCommand &args ); // position a corner on the current area at ground height |
|
void CommandNavWarpToMark( void ); // warp a spectating local player to the selected mark |
|
void CommandNavLadderFlip( void ); // Flips the direction a ladder faces |
|
void CommandNavToggleAttribute( NavAttributeType attribute ); // toggle an attribute on current area |
|
void CommandNavMakeSniperSpots( void ); // cuts up the marked area into individual areas suitable for sniper spots |
|
void CommandNavBuildLadder( void ); // builds a nav ladder on the climbable surface under the cursor |
|
void CommandNavRemoveJumpAreas( void ); // removes jump areas, replacing them with connections |
|
void CommandNavSubdivide( const CCommand &args ); // subdivide each nav area in X and Y to create 4 new areas - limit min size |
|
void CommandNavSaveSelected( const CCommand &args ); // Save selected set to disk |
|
void CommandNavMergeMesh( const CCommand &args ); // Merge a saved selected set into the current mesh |
|
void CommandNavMarkWalkable( void ); |
|
|
|
void AddToDragSelectionSet( CNavArea *pArea ); |
|
void RemoveFromDragSelectionSet( CNavArea *pArea ); |
|
void ClearDragSelectionSet( void ); |
|
|
|
CNavArea *GetMarkedArea( void ) const; // return area marked by user in edit mode |
|
CNavLadder *GetMarkedLadder( void ) const { return m_markedLadder; } // return ladder marked by user in edit mode |
|
|
|
CNavArea *GetSelectedArea( void ) const { return m_selectedArea; } // return area user is pointing at in edit mode |
|
CNavLadder *GetSelectedLadder( void ) const { return m_selectedLadder; } // return ladder user is pointing at in edit mode |
|
void SetMarkedLadder( CNavLadder *ladder ); // mark ladder for further edit operations |
|
void SetMarkedArea( CNavArea *area ); // mark area for further edit operations |
|
|
|
bool IsContinuouslySelecting( void ) const |
|
{ |
|
return m_isContinuouslySelecting; |
|
} |
|
|
|
bool IsContinuouslyDeselecting( void ) const |
|
{ |
|
return m_isContinuouslyDeselecting; |
|
} |
|
|
|
void CreateLadder( const Vector &mins, const Vector &maxs, float maxHeightAboveTopArea ); |
|
void CreateLadder( const Vector &top, const Vector &bottom, float width, const Vector2D &ladderDir, float maxHeightAboveTopArea ); |
|
|
|
float SnapToGrid( float x, bool forceGrid = false ) const; // snap given coordinate to generation grid boundary |
|
Vector SnapToGrid( const Vector& in, bool snapX = true, bool snapY = true, bool forceGrid = false ) const; // snap given vector's X & Y coordinates to generation grid boundary |
|
|
|
const Vector &GetEditCursorPosition( void ) const { return m_editCursorPos; } // return position of edit cursor |
|
void StripNavigationAreas( void ); |
|
const char *GetFilename( void ) const; // return the filename for this map's "nav" file |
|
|
|
/// @todo Remove old select code and make all commands use this selected set |
|
void AddToSelectedSet( CNavArea *area ); // add area to the currently selected set |
|
void RemoveFromSelectedSet( CNavArea *area ); // remove area from the currently selected set |
|
void ClearSelectedSet( void ); // clear the currently selected set to empty |
|
bool IsSelectedSetEmpty( void ) const; // return true if the selected set is empty |
|
bool IsInSelectedSet( const CNavArea *area ) const; // return true if the given area is in the selected set |
|
int GetSelecteSetSize( void ) const; |
|
const NavAreaVector &GetSelectedSet( void ) const; // return the selected set |
|
|
|
/** |
|
* Apply the functor to all navigation areas in the Selected Set, |
|
* or the current selected area. |
|
* If functor returns false, stop processing and return false. |
|
*/ |
|
template < typename Functor > |
|
bool ForAllSelectedAreas( Functor &func ) |
|
{ |
|
if (IsSelectedSetEmpty()) |
|
{ |
|
CNavArea *area = GetSelectedArea(); |
|
|
|
if (area) |
|
{ |
|
if (func( area ) == false) |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
FOR_EACH_VEC( m_selectedSet, it ) |
|
{ |
|
CNavArea *area = m_selectedSet[ it ]; |
|
|
|
if (func( area ) == false) |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------- |
|
/** |
|
* Apply the functor to all navigation areas. |
|
* If functor returns false, stop processing and return false. |
|
*/ |
|
template < typename Functor > |
|
bool ForAllAreas( Functor &func ) |
|
{ |
|
FOR_EACH_VEC( TheNavAreas, it ) |
|
{ |
|
CNavArea *area = TheNavAreas[ it ]; |
|
|
|
if (func( area ) == false) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
// const version of the above |
|
template < typename Functor > |
|
bool ForAllAreas( Functor &func ) const |
|
{ |
|
FOR_EACH_VEC( TheNavAreas, it ) |
|
{ |
|
const CNavArea *area = TheNavAreas[ it ]; |
|
|
|
if (func( area ) == false) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------- |
|
/** |
|
* Apply the functor to all navigation areas that overlap the given extent. |
|
* If functor returns false, stop processing and return false. |
|
*/ |
|
template < typename Functor > |
|
bool ForAllAreasOverlappingExtent( Functor &func, const Extent &extent ) |
|
{ |
|
VPROF_BUDGET( "CNavMesh::ForAllAreasOverlappingExtent", "NextBot" ); |
|
|
|
if ( !m_grid.Count() ) |
|
{ |
|
#if _DEBUG |
|
Warning("Query before nav mesh is loaded! %d\n", TheNavAreas.Count() ); |
|
#endif |
|
return true; |
|
} |
|
static unsigned int searchMarker = RandomInt(0, 1024*1024 ); |
|
if ( ++searchMarker == 0 ) |
|
{ |
|
++searchMarker; |
|
} |
|
|
|
Extent areaExtent; |
|
|
|
// get list in cell that contains position |
|
int startX = WorldToGridX( extent.lo.x ); |
|
int endX = WorldToGridX( extent.hi.x ); |
|
int startY = WorldToGridY( extent.lo.y ); |
|
int endY = WorldToGridY( extent.hi.y ); |
|
|
|
for( int x = startX; x <= endX; ++x ) |
|
{ |
|
for( int y = startY; y <= endY; ++y ) |
|
{ |
|
int iGrid = x + y*m_gridSizeX; |
|
if ( iGrid >= m_grid.Count() ) |
|
{ |
|
ExecuteNTimes( 10, Warning( "** Walked off of the CNavMesh::m_grid in ForAllAreasOverlappingExtent()\n" ) ); |
|
return true; |
|
} |
|
|
|
NavAreaVector *areaVector = &m_grid[ iGrid ]; |
|
|
|
// find closest area in this cell |
|
FOR_EACH_VEC( (*areaVector), it ) |
|
{ |
|
CNavArea *area = (*areaVector)[ it ]; |
|
|
|
// skip if we've already visited this area |
|
if ( area->m_nearNavSearchMarker == searchMarker ) |
|
continue; |
|
|
|
// mark as visited |
|
area->m_nearNavSearchMarker = searchMarker; |
|
area->GetExtent( &areaExtent ); |
|
|
|
if ( extent.IsOverlapping( areaExtent ) ) |
|
{ |
|
if ( func( area ) == false ) |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
template < typename Functor > |
|
bool ForAllAreasInRadius( Functor &func, const Vector &pos, float radius ) |
|
{ |
|
// use a unique marker for this method, so it can be used within a SearchSurroundingArea() call |
|
static unsigned int searchMarker = RandomInt(0, 1024*1024 ); |
|
|
|
++searchMarker; |
|
|
|
if ( searchMarker == 0 ) |
|
{ |
|
++searchMarker; |
|
} |
|
|
|
|
|
// get list in cell that contains position |
|
int originX = WorldToGridX( pos.x ); |
|
int originY = WorldToGridY( pos.y ); |
|
int shiftLimit = ceil( radius / m_gridCellSize ); |
|
float radiusSq = radius * radius; |
|
if ( radius == 0.0f ) |
|
{ |
|
shiftLimit = MAX( m_gridSizeX, m_gridSizeY ); // range 0 means all areas |
|
} |
|
|
|
for( int x = originX - shiftLimit; x <= originX + shiftLimit; ++x ) |
|
{ |
|
if ( x < 0 || x >= m_gridSizeX ) |
|
continue; |
|
|
|
for( int y = originY - shiftLimit; y <= originY + shiftLimit; ++y ) |
|
{ |
|
if ( y < 0 || y >= m_gridSizeY ) |
|
continue; |
|
|
|
NavAreaVector *areaVector = &m_grid[ x + y*m_gridSizeX ]; |
|
|
|
// find closest area in this cell |
|
FOR_EACH_VEC( (*areaVector), it ) |
|
{ |
|
CNavArea *area = (*areaVector)[ it ]; |
|
|
|
// skip if we've already visited this area |
|
if ( area->m_nearNavSearchMarker == searchMarker ) |
|
continue; |
|
|
|
// mark as visited |
|
area->m_nearNavSearchMarker = searchMarker; |
|
|
|
float distSq = ( area->GetCenter() - pos ).LengthSqr(); |
|
|
|
if ( ( distSq <= radiusSq ) || ( radiusSq == 0 ) ) |
|
{ |
|
if ( func( area ) == false ) |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------- |
|
/** |
|
* Apply the functor to all navigation ladders. |
|
* If functor returns false, stop processing and return false. |
|
*/ |
|
template < typename Functor > |
|
bool ForAllLadders( Functor &func ) |
|
{ |
|
for ( int i=0; i<m_ladders.Count(); ++i ) |
|
{ |
|
CNavLadder *ladder = m_ladders[i]; |
|
|
|
if (func( ladder ) == false) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------- |
|
/** |
|
* Apply the functor to all navigation ladders. |
|
* If functor returns false, stop processing and return false. |
|
*/ |
|
template < typename Functor > |
|
bool ForAllLadders( Functor &func ) const |
|
{ |
|
for ( int i=0; i<m_ladders.Count(); ++i ) |
|
{ |
|
const CNavLadder *ladder = m_ladders[i]; |
|
|
|
if (func( ladder ) == false) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
//------------------------------------------------------------------------------------- |
|
/** |
|
* tests a new area for connections to adjacent pre-existing areas |
|
*/ |
|
template < typename Functor > void StitchAreaIntoMesh( CNavArea *area, NavDirType dir, Functor &func ); |
|
|
|
//------------------------------------------------------------------------------------- |
|
/** |
|
* Use the functor to test if an area is needing stitching into the existing nav mesh. |
|
* The functor is different from how we normally use functors - it does no processing, |
|
* and it's return value is true if the area is in the new set to be stiched, and false |
|
* if it's a pre-existing area. |
|
*/ |
|
template < typename Functor > |
|
bool StitchMesh( Functor &func ) |
|
{ |
|
FOR_EACH_VEC( TheNavAreas, it ) |
|
{ |
|
CNavArea *area = TheNavAreas[ it ]; |
|
|
|
if ( func( area ) ) |
|
{ |
|
StitchAreaIntoMesh( area, NORTH, func ); |
|
StitchAreaIntoMesh( area, SOUTH, func ); |
|
StitchAreaIntoMesh( area, EAST, func ); |
|
StitchAreaIntoMesh( area, WEST, func ); |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
NavLadderVector& GetLadders( void ) { return m_ladders; } // Returns the list of ladders |
|
CNavLadder *GetLadderByID( unsigned int id ) const; |
|
|
|
CUtlVector< CNavArea * >& GetTransientAreas( void ) { return m_transientAreas; } |
|
|
|
enum EditModeType |
|
{ |
|
NORMAL, // normal mesh editing |
|
PLACE_PAINTING, // in place painting mode |
|
CREATING_AREA, // creating a new nav area |
|
CREATING_LADDER, // creating a nav ladder |
|
DRAG_SELECTING, // drag selecting a set of areas |
|
SHIFTING_XY, // shifting selected set in XY plane |
|
SHIFTING_Z, // shifting selected set in Z plane |
|
}; |
|
EditModeType GetEditMode( void ) const; // return the current edit mode |
|
void SetEditMode( EditModeType mode ); // change the edit mode |
|
bool IsEditMode( EditModeType mode ) const; // return true if current mode matches given mode |
|
|
|
bool FindNavAreaOrLadderAlongRay( const Vector &start, const Vector &end, CNavArea **area, CNavLadder **ladder, CNavArea *ignore = NULL ); |
|
|
|
void PostProcessCliffAreas(); |
|
void SimplifySelectedAreas( void ); // Simplifies the selected set by reducing to 1x1 areas and re-merging them up with loosened tolerances |
|
|
|
protected: |
|
virtual void PostCustomAnalysis( void ) { } // invoked when custom analysis step is complete |
|
bool FindActiveNavArea( void ); // Finds the area or ladder the local player is currently pointing at. Returns true if a surface was hit by the traceline. |
|
virtual void RemoveNavArea( CNavArea *area ); // remove an area from the grid |
|
bool FindGroundForNode( Vector *pos, Vector *normal ); |
|
void GenerateNodes( const Extent &bounds ); |
|
void RemoveNodes( void ); |
|
|
|
private: |
|
friend class CNavArea; |
|
friend class CNavNode; |
|
friend class CNavUIBasePanel; |
|
|
|
mutable CUtlVector<NavAreaVector> m_grid; |
|
float m_gridCellSize; // the width/height of a grid cell for spatially partitioning nav areas for fast access |
|
int m_gridSizeX; |
|
int m_gridSizeY; |
|
float m_minX; |
|
float m_minY; |
|
unsigned int m_areaCount; // total number of nav areas |
|
|
|
bool m_isLoaded; // true if a Navigation Mesh has been loaded |
|
bool m_isOutOfDate; // true if the Navigation Mesh is older than the actual BSP |
|
bool m_isAnalyzed; // true if the Navigation Mesh needs analysis |
|
|
|
enum { HASH_TABLE_SIZE = 256 }; |
|
CNavArea *m_hashTable[ HASH_TABLE_SIZE ]; // hash table to optimize lookup by ID |
|
int ComputeHashKey( unsigned int id ) const; // returns a hash key for the given nav area ID |
|
|
|
int WorldToGridX( float wx ) const; // given X component, return grid index |
|
int WorldToGridY( float wy ) const; // given Y component, return grid index |
|
void AllocateGrid( float minX, float maxX, float minY, float maxY ); // clear and reset the grid to the given extents |
|
void GridToWorld( int gridX, int gridY, Vector *pos ) const; |
|
|
|
void AddNavArea( CNavArea *area ); // add an area to the grid |
|
|
|
void DestroyNavigationMesh( bool incremental = false ); // free all resources of the mesh and reset it to empty state |
|
void DestroyHidingSpots( void ); |
|
|
|
void ComputeBattlefrontAreas( void ); // determine areas where rushing teams will first meet |
|
|
|
//---------------------------------------------------------------------------------- |
|
// Place directory |
|
// |
|
char **m_placeName; // master directory of place names (ie: "places") |
|
unsigned int m_placeCount; // number of "places" defined in placeName[] |
|
void LoadPlaceDatabase( void ); // load the place names from a file |
|
|
|
//---------------------------------------------------------------------------------- |
|
// Edit mode |
|
// |
|
EditModeType m_editMode; // the current edit mode |
|
bool m_isEditing; // true if in edit mode |
|
|
|
unsigned int m_navPlace; // current navigation place for editing |
|
void OnEditModeStart( void ); // called when edit mode has just been enabled |
|
void DrawEditMode( void ); // draw navigation areas |
|
void OnEditModeEnd( void ); // called when edit mode has just been disabled |
|
void UpdateDragSelectionSet( void ); // update which areas are overlapping the drag selected bounds |
|
Vector m_editCursorPos; // current position of the cursor |
|
CNavArea *m_markedArea; // currently marked area for edit operations |
|
CNavArea *m_selectedArea; // area that is selected this frame |
|
CNavArea *m_lastSelectedArea; // area that was selected last frame |
|
NavCornerType m_markedCorner; // currently marked corner for edit operations |
|
Vector m_anchor; // first corner of an area being created |
|
bool m_isPlacePainting; // if true, we set an area's place by pointing at it |
|
bool m_splitAlongX; // direction the selected nav area would be split |
|
float m_splitEdge; // location of the possible split |
|
|
|
bool m_climbableSurface; // if true, the cursor is pointing at a climable surface |
|
Vector m_surfaceNormal; // Normal of the surface the cursor is pointing at |
|
Vector m_ladderAnchor; // first corner of a ladder being created |
|
Vector m_ladderNormal; // Normal of the surface of the ladder being created |
|
CNavLadder *m_selectedLadder; // ladder that is selected this frame |
|
CNavLadder *m_lastSelectedLadder; // ladder that was selected last frame |
|
CNavLadder *m_markedLadder; // currently marked ladder for edit operations |
|
|
|
bool FindLadderCorners( Vector *c1, Vector *c2, Vector *c3 ); // computes the other corners of a ladder given m_ladderAnchor, m_editCursorPos, and m_ladderNormal |
|
|
|
void GetEditVectors( Vector *pos, Vector *forward ); // Gets the eye position and view direction of the editing player |
|
|
|
CountdownTimer m_showAreaInfoTimer; // Timer that controls how long area info is displayed |
|
|
|
NavAreaVector m_selectedSet; // all currently selected areas |
|
NavAreaVector m_dragSelectionSet; // all areas in the current drag selection |
|
bool m_isContinuouslySelecting; // if true, we are continuously adding to the selected set |
|
bool m_isContinuouslyDeselecting; // if true, we are continuously removing from the selected set |
|
|
|
bool m_bIsDragDeselecting; |
|
int m_nDragSelectionVolumeZMax; |
|
int m_nDragSelectionVolumeZMin; |
|
|
|
void DoToggleAttribute( CNavArea *area, NavAttributeType attribute ); // toggle an attribute on given area |
|
|
|
|
|
//---------------------------------------------------------------------------------- |
|
// Auto-generation |
|
// |
|
bool UpdateGeneration( float maxTime = 0.25f ); // process the auto-generation for 'maxTime' seconds. return false if generation is complete. |
|
|
|
virtual void BeginCustomAnalysis( bool bIncremental ) {} |
|
virtual void EndCustomAnalysis() {} |
|
|
|
CNavNode *m_currentNode; // the current node we are sampling from |
|
NavDirType m_generationDir; |
|
CNavNode *AddNode( const Vector &destPos, const Vector &destNormal, NavDirType dir, CNavNode *source, bool isOnDisplacement, float obstacleHeight, float flObstacleStartDist, float flObstacleEndDist ); // add a nav node and connect it, update current node |
|
|
|
NavLadderVector m_ladders; // list of ladder navigation representations |
|
void BuildLadders( void ); |
|
void DestroyLadders( void ); |
|
|
|
bool SampleStep( void ); // sample the walkable areas of the map |
|
void CreateNavAreasFromNodes( void ); // cover all of the sampled nodes with nav areas |
|
|
|
bool TestArea( CNavNode *node, int width, int height ); // check if an area of size (width, height) can fit, starting from node as upper left corner |
|
int BuildArea( CNavNode *node, int width, int height ); // create a CNavArea of size (width, height) starting fom node at upper left corner |
|
bool CheckObstacles( CNavNode *node, int width, int height, int x, int y ); |
|
|
|
void MarkPlayerClipAreas( void ); |
|
void MarkJumpAreas( void ); |
|
void StichAndRemoveJumpAreas( void ); |
|
void RemoveJumpAreas( void ); |
|
void SquareUpAreas( void ); |
|
void MergeGeneratedAreas( void ); |
|
void ConnectGeneratedAreas( void ); |
|
void FixUpGeneratedAreas( void ); |
|
void FixCornerOnCornerAreas( void ); |
|
void FixConnections( void ); |
|
void SplitAreasUnderOverhangs( void ); |
|
void ValidateNavAreaConnections( void ); |
|
void StitchGeneratedAreas( void ); // Stitches incrementally-generated areas into the existing mesh |
|
void StitchAreaSet( CUtlVector< CNavArea * > *areas ); // Stitches an arbitrary set of areas into the existing mesh |
|
void HandleObstacleTopAreas( void ); // Handles fixing/generating areas on top of slim obstacles such as fences and railings |
|
void RaiseAreasWithInternalObstacles(); |
|
void CreateObstacleTopAreas(); |
|
bool CreateObstacleTopAreaIfNecessary( CNavArea *area, CNavArea *areaOther, NavDirType dir, bool bMultiNode ); |
|
void RemoveOverlappingObstacleTopAreas(); |
|
|
|
|
|
enum GenerationStateType |
|
{ |
|
SAMPLE_WALKABLE_SPACE, |
|
CREATE_AREAS_FROM_SAMPLES, |
|
FIND_HIDING_SPOTS, |
|
FIND_ENCOUNTER_SPOTS, |
|
FIND_SNIPER_SPOTS, |
|
FIND_EARLIEST_OCCUPY_TIMES, |
|
FIND_LIGHT_INTENSITY, |
|
COMPUTE_MESH_VISIBILITY, |
|
CUSTOM, // mod-specific generation step |
|
SAVE_NAV_MESH, |
|
|
|
NUM_GENERATION_STATES |
|
} |
|
m_generationState; // the state of the generation process |
|
enum GenerationModeType |
|
{ |
|
GENERATE_NONE, |
|
GENERATE_FULL, |
|
GENERATE_INCREMENTAL, |
|
GENERATE_SIMPLIFY, |
|
GENERATE_ANALYSIS_ONLY, |
|
} |
|
m_generationMode; // true while a Navigation Mesh is being generated |
|
int m_generationIndex; // used for iterating nav areas during generation process |
|
int m_sampleTick; // counter for displaying pseudo-progress while sampling walkable space |
|
bool m_bQuitWhenFinished; |
|
float m_generationStartTime; |
|
Extent m_simplifyGenerationExtent; |
|
|
|
char *m_spawnName; // name of player spawn entity, used to initiate sampling |
|
|
|
struct WalkableSeedSpot |
|
{ |
|
Vector pos; |
|
Vector normal; |
|
}; |
|
CUtlVector< WalkableSeedSpot > m_walkableSeeds; // list of walkable seed spots for sampling |
|
|
|
CNavNode *GetNextWalkableSeedNode( void ); // return the next walkable seed as a node |
|
int m_seedIdx; |
|
int m_hostThreadModeRestoreValue; // stores the value of host_threadmode before we changed it |
|
|
|
void BuildTransientAreaList( void ); |
|
CUtlVector< CNavArea * > m_transientAreas; |
|
|
|
void UpdateAvoidanceObstacleAreas( void ); |
|
CUtlVector< CNavArea * > m_avoidanceObstacleAreas; |
|
CUtlVector< INavAvoidanceObstacle * > m_avoidanceObstacles; |
|
|
|
void UpdateBlockedAreas( void ); |
|
CUtlVector< CNavArea * > m_blockedAreas; |
|
|
|
CUtlVector< int > m_storedSelectedSet; // "Stored" selected set, so we can do some editing and then restore the old selected set. Done by ID, so we don't have to worry about split/delete/etc. |
|
|
|
void BeginVisibilityComputations( void ); |
|
void EndVisibilityComputations( void ); |
|
}; |
|
|
|
// the global singleton interface |
|
extern CNavMesh *TheNavMesh; |
|
|
|
// factory for creating the Navigation Mesh |
|
extern CNavMesh *NavMeshFactory( void ); |
|
|
|
// for debugging the A* algorithm, if nonzero, show debug display and decrement for each pathfind |
|
extern int g_DebugPathfindCounter; |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline bool CNavMesh::IsEditMode( EditModeType mode ) const |
|
{ |
|
return m_editMode == mode; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline CNavMesh::EditModeType CNavMesh::GetEditMode( void ) const |
|
{ |
|
return m_editMode; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline unsigned int CNavMesh::GetSubVersionNumber( void ) const |
|
{ |
|
return 0; |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline CNavArea *CNavMesh::CreateArea( void ) const |
|
{ |
|
return new CNavArea; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline void CNavMesh::DestroyArea( CNavArea *pArea ) const |
|
{ |
|
delete pArea; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline int CNavMesh::ComputeHashKey( unsigned int id ) const |
|
{ |
|
return id & 0xFF; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline int CNavMesh::WorldToGridX( float wx ) const |
|
{ |
|
int x = (int)( (wx - m_minX) / m_gridCellSize ); |
|
|
|
if (x < 0) |
|
x = 0; |
|
else if (x >= m_gridSizeX) |
|
x = m_gridSizeX-1; |
|
|
|
return x; |
|
} |
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline int CNavMesh::WorldToGridY( float wy ) const |
|
{ |
|
int y = (int)( (wy - m_minY) / m_gridCellSize ); |
|
|
|
if (y < 0) |
|
y = 0; |
|
else if (y >= m_gridSizeY) |
|
y = m_gridSizeY-1; |
|
|
|
return y; |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
inline unsigned int CNavMesh::GetGenerationTraceMask( void ) const |
|
{ |
|
return MASK_NPCSOLID_BRUSHONLY; |
|
} |
|
|
|
|
|
//-------------------------------------------------------------------------------------------------------------- |
|
// |
|
// Function prototypes |
|
// |
|
|
|
extern void ApproachAreaAnalysisPrep( void ); |
|
extern void CleanupApproachAreaAnalysisPrep( void ); |
|
extern bool IsHeightDifferenceValid( float test, float other1, float other2, float other3 ); |
|
|
|
#endif // _NAV_MESH_H_
|
|
|