/* =========================================================================== IceTech GPL Source Code Copyright (C) 2026 Justin Marshall =========================================================================== */ #include "precompiled.h" #pragma hdrstop #include "../Game_local.h" #include "RecastNavMesh.h" idRecastNavMesh::idRecastNavMesh( void ) { handle = RECAST_NAV_HANDLE_INVALID; memset( &syntheticReach, 0, sizeof( syntheticReach ) ); if ( RecastNavigationManager ) { handle = RecastNavigationManager->Alloc(); } } idRecastNavMesh::~idRecastNavMesh( void ) { if ( RecastNavigationManager && handle != RECAST_NAV_HANDLE_INVALID ) { RecastNavigationManager->Free( handle ); handle = RECAST_NAV_HANDLE_INVALID; } } bool idRecastNavMesh::HasManager( void ) const { return RecastNavigationManager && handle != RECAST_NAV_HANDLE_INVALID; } void idRecastNavMesh::Shutdown( void ) { if ( HasManager() ) { RecastNavigationManager->Free( handle ); handle = RecastNavigationManager->Alloc(); } name.Clear(); memset( &syntheticReach, 0, sizeof( syntheticReach ) ); } bool idRecastNavMesh::Load( const idStr &mapName, const idStr &fileName, unsigned int mapFileCRC ) { if ( !HasManager() ) { return false; } if ( !RecastNavigationManager->Load( handle, mapName, fileName, mapFileCRC ) ) { name.Clear(); return false; } name = fileName; return true; } bool idRecastNavMesh::IsLoaded( void ) const { return HasManager() && RecastNavigationManager->IsLoaded( handle ); } int idRecastNavMesh::NumPolys( void ) const { return HasManager() ? RecastNavigationManager->NumPolys( handle ) : 0; } const idAASSettings *idRecastNavMesh::GetSettings( void ) const { return HasManager() ? RecastNavigationManager->GetSettings( handle ) : NULL; } idStr idRecastNavMesh::NavFileNameForMapName( const idStr &mapName ) { idStr navName = mapName; navName += ".navmesh"; return navName; } bool idRecastNavMesh::BuildMapFile( const idStr &mapName, const idStr &aasType ) { return RecastNavigationManager && RecastNavigationManager->BuildMapFile( mapName.c_str(), aasType.c_str() ); } void idRecastNavMesh::Stats( void ) const { if ( HasManager() ) { RecastNavigationManager->Stats( handle ); } } void idRecastNavMesh::DebugDraw( int drawMode ) const { if ( HasManager() ) { RecastNavigationManager->DebugDraw( handle, gameRenderWorld, drawMode, gameLocal.msec ); } } int idRecastNavMesh::PointAreaNum( const idVec3 &origin ) const { return HasManager() ? RecastNavigationManager->PointAreaNum( handle, origin ) : 0; } int idRecastNavMesh::PointReachableAreaNum( const idVec3 &origin, const idBounds &searchBounds, const int areaFlags ) const { return HasManager() ? RecastNavigationManager->PointReachableAreaNum( handle, origin, searchBounds, areaFlags ) : 0; } int idRecastNavMesh::BoundsReachableAreaNum( const idBounds &bounds, const int areaFlags ) const { return HasManager() ? RecastNavigationManager->BoundsReachableAreaNum( handle, bounds, areaFlags ) : 0; } void idRecastNavMesh::PushPointIntoAreaNum( int areaNum, idVec3 &origin ) const { if ( HasManager() ) { RecastNavigationManager->PushPointIntoAreaNum( handle, areaNum, origin ); } } idVec3 idRecastNavMesh::AreaCenter( int areaNum ) const { return HasManager() ? RecastNavigationManager->AreaCenter( handle, areaNum ) : vec3_origin; } int idRecastNavMesh::AreaFlags( int areaNum ) const { return HasManager() ? RecastNavigationManager->AreaFlags( handle, areaNum ) : 0; } int idRecastNavMesh::AreaTravelFlags( int areaNum ) const { return HasManager() ? RecastNavigationManager->AreaTravelFlags( handle, areaNum ) : TFL_INVALID; } bool idRecastNavMesh::Trace( aasTrace_t &trace, const idVec3 &start, const idVec3 &end ) const { return HasManager() && RecastNavigationManager->Trace( handle, trace, start, end ); } bool idRecastNavMesh::SetAreaState( const idBounds &bounds, const int areaContents, bool disabled ) { return HasManager() && RecastNavigationManager->SetAreaState( handle, bounds, areaContents, disabled ); } aasHandle_t idRecastNavMesh::AddObstacle( const idBounds &bounds ) { return HasManager() ? RecastNavigationManager->AddObstacle( handle, bounds ) : -1; } void idRecastNavMesh::RemoveObstacle( const aasHandle_t obstacleHandle ) { if ( HasManager() ) { RecastNavigationManager->RemoveObstacle( handle, obstacleHandle ); } } void idRecastNavMesh::RemoveAllObstacles( void ) { if ( HasManager() ) { RecastNavigationManager->RemoveAllObstacles( handle ); } } int idRecastNavMesh::TravelTimeToGoalArea( int areaNum, const idVec3 &origin, int goalAreaNum, int travelFlags ) const { return HasManager() ? RecastNavigationManager->TravelTimeToGoalArea( handle, areaNum, origin, goalAreaNum, travelFlags ) : 0; } bool idRecastNavMesh::RouteToGoalArea( int areaNum, const idVec3 origin, int goalAreaNum, int travelFlags, int &travelTime, idReachability **reach ) const { recastPath_t route; if ( !HasManager() || !RecastNavigationManager->RouteToGoalArea( handle, areaNum, origin, goalAreaNum, travelFlags, route ) ) { travelTime = 0; *reach = NULL; return false; } travelTime = route.travelTime; memset( &syntheticReach, 0, sizeof( syntheticReach ) ); syntheticReach.travelType = TFL_WALK; syntheticReach.fromAreaNum = areaNum; syntheticReach.toAreaNum = route.moveAreaNum; syntheticReach.start = route.moveGoal; syntheticReach.end = route.moveGoal; syntheticReach.travelTime = travelTime; *reach = &syntheticReach; return true; } bool idRecastNavMesh::PathToGoal( aasPath_t &path, int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags ) const { recastPath_t recastPath; if ( !HasManager() || !RecastNavigationManager->PathToGoal( handle, recastPath, areaNum, origin, goalAreaNum, goalOrigin, travelFlags ) ) { return false; } path.type = PATHTYPE_WALK; path.moveGoal = recastPath.moveGoal; path.moveAreaNum = recastPath.moveAreaNum; path.secondaryGoal = recastPath.secondaryGoal; path.reachability = NULL; return true; } bool idRecastNavMesh::PathValid( int areaNum, const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin, int travelFlags, idVec3 &endPos, int &endAreaNum ) const { return HasManager() && RecastNavigationManager->PathValid( handle, areaNum, origin, goalAreaNum, goalOrigin, travelFlags, endPos, endAreaNum ); } bool idRecastNavMesh::FindNearestGoal( const idNAV *owner, aasGoal_t &goal, int areaNum, const idVec3 origin, const idVec3 &target, int travelFlags, aasObstacle_t *queryObstacles, int numObstacles, idNAVCallback &callback ) const { int areas[MAX_RECAST_PATH_AREAS]; int numAreas = 0; const int targetArea = PointAreaNum( target ); if ( targetArea <= 0 || !HasManager() || !RecastNavigationManager->PathAreaList( handle, areaNum, origin, targetArea, target, travelFlags, areas, numAreas, MAX_RECAST_PATH_AREAS ) ) { return false; } for ( int i = numAreas - 1; i >= 0; i-- ) { const int candidateArea = areas[i]; if ( !callback.AreaIsGoal( owner, candidateArea ) ) { continue; } const idVec3 candidateOrigin = AreaCenter( candidateArea ); bool blocked = false; for ( int j = 0; j < numObstacles; j++ ) { if ( queryObstacles[j].expAbsBounds.ContainsPoint( candidateOrigin ) ) { blocked = true; break; } } if ( !blocked ) { goal.areaNum = candidateArea; goal.origin = candidateOrigin; return true; } } return false; } void idRecastNavMesh::ShowPath( const idVec3 &origin, int goalAreaNum, const idVec3 &goalOrigin ) const { aasPath_t path; const int areaNum = PointAreaNum( origin ); if ( PathToGoal( path, areaNum, origin, goalAreaNum, goalOrigin, TFL_WALK ) ) { gameRenderWorld->DebugLine( colorGreen, origin, path.moveGoal, gameLocal.msec ); gameRenderWorld->DebugArrow( colorYellow, path.moveGoal, goalOrigin, 4.0f, gameLocal.msec ); } } void idRecastNavMesh::ShowArea( const idVec3 &origin ) const { const int areaNum = PointAreaNum( origin ); if ( areaNum <= 0 ) { return; } const idVec3 center = AreaCenter( areaNum ); gameRenderWorld->DebugBounds( colorCyan, idBounds( idVec3( -8.0f, -8.0f, -4.0f ), idVec3( 8.0f, 8.0f, 4.0f ) ), center, gameLocal.msec ); }