5411 lines
186 KiB
C++
5411 lines
186 KiB
C++
#include "PathServer\PathServer.h"
|
|
|
|
// Rage headers
|
|
#include "bank/bkmgr.h"
|
|
#include "profile/cputrace.h"
|
|
#include "system/memory.h"
|
|
#include "system/threadtype.h"
|
|
|
|
// Framework headers
|
|
#include "ai/navmesh/priqueue.h"
|
|
#include "fwgeovis/geovis.h"
|
|
#include "fwsys/fileExts.h"
|
|
#ifdef GTA_ENGINE
|
|
|
|
#include "peds/Ped.h"
|
|
#include "peds/PedIntelligence.h"
|
|
#include "modelinfo/PedModelInfo.h"
|
|
#include "scene/playerswitch/PlayerSwitchInterface.h"
|
|
#include "renderer/Water.h" // Water
|
|
#include "scene/FocusEntity.h"
|
|
#include "script/script_memory_tracking.h"
|
|
#include "streaming/streamingvisualize.h"
|
|
#include "streaming/streaming.h"
|
|
#include "task/Movement/TaskNavBase.h"
|
|
#include "Vfx/Misc/Fire.h"
|
|
#if !__FINAL
|
|
#include "PathServer\ExportCollision.h"
|
|
#endif
|
|
|
|
PARAM(nonav, "turns off navmesh/navnode streaming & streaming-module registration");
|
|
|
|
#else // GTA_ENGINE
|
|
|
|
// This include for when developing the system outside of GTA
|
|
#include "GTATypes.h"
|
|
#include "system/stockallocator.h"
|
|
|
|
#endif // GTA_ENGINE
|
|
|
|
|
|
// All unused, it seems: /FF
|
|
// Whether to load resourced platform-dependent navmeshes, or platform-independent ones from the 'common' folder
|
|
// #define __RESOURCE_NAVMESHES 1
|
|
// Whether to load the low-load hierarchical nodes
|
|
// #define __LOAD_HIERARCHICAL_NODES 1
|
|
// (This is pretty much mandatory now, I don't think it can be un-defined).
|
|
// #define __NO_QUADTREES_FOR_DYNAMIC_NAVMESHES 1
|
|
// Whether to use the navmesh/navnodes index remapping
|
|
#define __REMAP_NAVMESH_INDICES 0
|
|
|
|
NAVMESH_OPTIMISATIONS()
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
void CPedGenBlockedArea::Init(const Vector3 & vOrigin, const float fRadius, const u32 iDuration, const EOwner owner, const bool bShrink)
|
|
{
|
|
m_bActive = true;
|
|
Assert(owner >= 0 && owner < EOwner(1 << 2)); // Only got two bits for it right now.
|
|
m_iOwner = owner;
|
|
m_bShrink = bShrink;
|
|
m_iType = ESphere;
|
|
|
|
m_Sphere.SetOrigin(vOrigin);
|
|
m_Sphere.m_fActiveRadius = fRadius;
|
|
m_Sphere.m_fRadiusSqr = fRadius*fRadius;
|
|
m_Sphere.m_fOriginalRadius = fRadius;
|
|
m_iStartTime = fwTimer::GetTimeInMilliseconds();
|
|
m_iDuration = iDuration;
|
|
}
|
|
|
|
void CPedGenBlockedArea::Init(const Vector3 * pCornerPts, const float fTopZ, const float fBottomZ, const u32 iDuration, const EOwner owner)
|
|
{
|
|
m_bActive = true;
|
|
Assert(owner >= 0 && owner < EOwner(1 << 2)); // Only got two bits for it right now.
|
|
m_iOwner = owner;
|
|
m_iType = EAngledArea;
|
|
|
|
int p;
|
|
for(p=0; p<4; p++)
|
|
{
|
|
m_Area.SetPoint(p, Vector2(pCornerPts[p].x, pCornerPts[p].y));
|
|
}
|
|
|
|
int lastp = 3;
|
|
for(p=0; p<4; p++)
|
|
{
|
|
Vector2 vEdge = m_Area.GetPoint2(p) - m_Area.GetPoint2(lastp);
|
|
vEdge.Normalize();
|
|
|
|
m_Area.SetNormal(p, Vector2(vEdge.y, -vEdge.x));
|
|
|
|
const Vector3 vNormal = m_Area.GetNormal3(p);
|
|
const Vector3 vPt = m_Area.GetPoint3(p);
|
|
m_Area.m_PlaneDists[p] = - DotProduct(vNormal, vPt);
|
|
|
|
lastp = p;
|
|
}
|
|
|
|
m_Area.m_fTopZ = fTopZ;
|
|
m_Area.m_fBottomZ = fBottomZ;
|
|
|
|
m_iStartTime = fwTimer::GetTimeInMilliseconds();
|
|
m_iDuration = iDuration;
|
|
}
|
|
|
|
bool TSphereArea::LiesWithin(const Vector3 & p)
|
|
{
|
|
const Vector3 vOrigin(m_fOrigin[0], m_fOrigin[1], m_fOrigin[2]);
|
|
float fDistSqr = vOrigin.Dist2(p);
|
|
return(fDistSqr < m_fRadiusSqr);
|
|
}
|
|
|
|
bool TAngledArea::LiesWithin(const Vector3 & pt)
|
|
{
|
|
if(pt.z < m_fBottomZ || pt.z > m_fTopZ)
|
|
return false;
|
|
|
|
for(int p=0; p<4; p++)
|
|
{
|
|
// The normals of each edge points OUTWARDS.
|
|
// Therefore if we are behind all 4 planes, we are inside the region.
|
|
const float fDist = DotProduct(GetNormal3(p), pt) + m_PlaneDists[p];
|
|
if(fDist > 0.0f)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
|
|
//*************************************************************
|
|
// CPathServer
|
|
// This implements the interface which game-code uses to
|
|
// find paths via the navigation-mesh system.
|
|
// The actual pathfinding is done via a separate thread within
|
|
// the CPathServerThread class.
|
|
//*************************************************************
|
|
|
|
CPathServer::EProcessingMode CPathServer::m_eProcessingMode = CPathServer::EUndefined;
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
aiNavNodesStore * CPathServer::m_pNavNodesStore = NULL;
|
|
#endif
|
|
int CPathServer::m_iNumDynamicNavMeshesInExistence = 0;
|
|
|
|
#if !__FINAL
|
|
u32 CPathServer::ms_iNumTessellationsThisPath = 0;
|
|
u32 CPathServer::ms_iNumTestDynamicObjectLOS = 0;
|
|
u32 CPathServer::ms_iNumTestNavMeshLOS = 0;
|
|
u32 CPathServer::ms_iNumImmediateTestNavMeshLOS = 0;
|
|
u32 CPathServer::ms_iNumGetObjectsIntersectingRegion = 0;
|
|
u32 CPathServer::ms_iNumCacheHitsOnDynamicObjects = 0;
|
|
float CPathServer::m_fRunningTotalOfTimeOnPathRequests = 0.0f;
|
|
float CPathServer::m_fTimeSpentProcessingThisGameTurn = 0.0f;
|
|
#ifdef GTA_ENGINE
|
|
bool CPathServer::ms_bDrawPedGenBlockingAreas = false;
|
|
bool CPathServer::ms_bVisualiseOpponentPedGeneration = false;
|
|
bool CPathServer::ms_bDrawPedSwitchAreas = false;
|
|
#endif
|
|
#endif
|
|
|
|
u32 CPathServer::m_iThreadHousekeepingFrequencyInMillisecs = 1000;
|
|
u32 CPathServer::m_iLastTimeThreadDidHousekeeping = 0;
|
|
u32 CPathServer::m_iLastTimeCheckedForStaleRequests = 0;
|
|
u32 CPathServer::m_iCheckForStaleRequestsFreq = 2000;
|
|
|
|
bank_u32 CPathServer::m_iTimeToSleepWaitingForRequestsMs = 8;
|
|
bank_u32 CPathServer::m_iTimeToSleepDuringLongRequestsMs = 2;
|
|
bool CPathServer::m_bSleepPathServerThreadOnLongPathRequests = false;
|
|
u32 CPathServer::m_iNumFindPathIterationsBetweenSleepChecks = 64;
|
|
|
|
|
|
bool CPathServer::ms_bInitialised = false;
|
|
#ifdef GTA_ENGINE
|
|
bool CPathServer::ms_bGameInSession = false;
|
|
#else
|
|
bool CPathServer::ms_bGameInSession = true;
|
|
#endif
|
|
bool CPathServer::ms_bStreamingDisabled = false;
|
|
bool CPathServer::m_bDisablePathFinding = false;
|
|
s32 CPathServer::ms_iTimeToNextRequestAndEvict = 0;
|
|
CPathServer::EObjectAvoidanceMode CPathServer::m_eObjectAvoidanceMode = CPathServer::EPolyTessellation;
|
|
bool CPathServer::ms_bNoNeedToCheckObjectsForThisPoly = false;
|
|
bool CPathServer::ms_bHaveAnyDynamicObjectsChanged = false;
|
|
float CPathServer::ms_fDefaultDynamicObjectPlaneEpsilon = 0.0f;
|
|
bank_float CPathServer::ms_fDynamicObjectVelocityThreshold = 1.0f;
|
|
bank_bool CPathServer::ms_bTestConnectionPolyExit = true;
|
|
bank_bool CPathServer::ms_bDisallowClimbObjectLinks = false;
|
|
|
|
bank_s32 CPathServer::ms_iRequestAndEvictFreqMS = 1000;
|
|
bank_bool CPathServer::ms_bRefinePaths = true;
|
|
bank_bool CPathServer::ms_bSmoothRoutesUsingSplines = true;
|
|
bank_bool CPathServer::ms_bCutCornersOffAllPaths = false;
|
|
bank_bool CPathServer::ms_bMinimisePathDistance = true;
|
|
bank_bool CPathServer::ms_bMinimiseBeforeRefine = false;
|
|
bank_bool CPathServer::ms_bPullPathOutFromEdges = true;
|
|
bank_bool CPathServer::ms_bPullPathOutFromEdgesTwice = false;
|
|
bank_bool CPathServer::ms_bPullPathOutFromEdgesStringPull = true;
|
|
bank_bool CPathServer::ms_bDoPolyTessellation = true;
|
|
bank_bool CPathServer::ms_bDoPolyUnTessellation = true;
|
|
bank_bool CPathServer::ms_bDoPreemptiveTessellation = false; // Tessellate any poly we visit which is near a dynamic object
|
|
bank_bool CPathServer::ms_bUseGridCellCache = false;
|
|
bank_bool CPathServer::ms_bUseTessellatedPolyObjectCache = false;
|
|
bank_bool CPathServer::ms_bAlwaysUseMorePointsInPolys = false;
|
|
//bank_bool CPathServer::ms_bSphereRayEarlyOutTestOnObjects = false;
|
|
bank_bool CPathServer::ms_bUseLosToEarlyOutPathRequests = true;
|
|
bank_bool CPathServer::ms_bDontRevisitOpenNodes = false;
|
|
bank_bool CPathServer::ms_bAllowTessellationOfOpenNodes = false;
|
|
bank_bool CPathServer::ms_bOutputDetailsOfPaths = false;
|
|
bank_bool CPathServer::ms_bTestObjectsForEveryPoly = false;
|
|
bank_bool CPathServer::ms_bUseOptimisedPolyCentroids = true;
|
|
bank_bool CPathServer::ms_bUseGridToCullObjectLOS = true;
|
|
bank_bool CPathServer::ms_bUseNewPathPriorityScoring = true;
|
|
|
|
#ifdef GTA_ENGINE
|
|
u32 CPathServer::m_iLastTimeProcessedPedGeneration = 0;
|
|
#if (RSG_PC || RSG_DURANGO || RSG_ORBIS) // higher frequency in NG
|
|
dev_u32 CPathServer::m_iProcessPedGenerationFreq = 30;
|
|
#else
|
|
dev_u32 CPathServer::m_iProcessPedGenerationFreq = 500;
|
|
#endif
|
|
CPathServerAmbientPedGen CPathServer::m_AmbientPedGeneration;
|
|
CPathServerOpponentPedGen CPathServer::m_OpponentPedGeneration;
|
|
#endif
|
|
|
|
#if !__FINAL
|
|
bool CPathServer::ms_bDebugPathFinding = false;
|
|
bool CPathServer::ms_bEnsureLosBeforeEnding = true;
|
|
s32 CPathServer::m_iVisualiseNavMeshes = CPathServer::NavMeshVis_Off;
|
|
bool CPathServer::m_bVisualiseHierarchicalNodes = false;
|
|
bool CPathServer::m_bVisualiseLinkWidths = false;
|
|
bool CPathServer::m_bVisualisePaths = false;
|
|
int CPathServer::m_iVisualiseDataSet = kNavDomainRegular;
|
|
s32 CPathServer::m_iVisualisePathServerInfo = CPathServer::PathServerVis_Off;
|
|
bool CPathServer::m_bVisualiseQuadTrees = false;
|
|
bool CPathServer::m_bVisualiseAllCoverPoints = false;
|
|
bool CPathServer::m_bDebugShowLowClimbOvers = false;
|
|
bool CPathServer::m_bDebugShowHighClimbOvers = false;
|
|
bool CPathServer::m_bDebugShowDropDowns = false;
|
|
bool CPathServer::m_bDebugShowSpecialLinks = true;
|
|
bool CPathServer::m_bDebugShowPolyVolumesAbove = false;
|
|
bool CPathServer::m_bDebugShowPolyVolumesBelow = false;
|
|
bool CPathServer::m_bVerifyAdjacencies = false;
|
|
bool CPathServer::m_bRepeatPathQuery = false;
|
|
bool CPathServer::ms_bUseXTraceOnTheNextPathRequest = false;
|
|
bool CPathServer::m_bDisableObjectAvoidance = false;
|
|
bool CPathServer::m_bDisableAudioRequests = false;
|
|
bool CPathServer::m_bDisableGetClosestPositionForPed = false;
|
|
|
|
bool CPathServer::ms_bAllowObjectClimbing = true;
|
|
bool CPathServer::ms_bAllowNavMeshClimbs = true;
|
|
bool CPathServer::ms_bAllowObjectPushing = false; // Needs fixing if you want to enable this.
|
|
bool CPathServer::m_bStressTestPathFinding = false;
|
|
bool CPathServer::m_bStressTestGetClosestPos = false;
|
|
bool CPathServer::m_bVisualisePolygonRequests = false;
|
|
TPathHandle CPathServer::m_iStressTestPathHandles[NUM_STRESS_TEST_PATHS];
|
|
s32 CPathServer::m_iSelectedPathRequest = 0;
|
|
s32 CPathServer::m_iSelectedLosRequest = 0;
|
|
bool CPathServer::ms_bOnlyDisplaySelectedRoute = false;
|
|
bool CPathServer::ms_bShowSearchExtents = false;
|
|
s32 CPathServer::m_iDebugThreadRunMode = 1;
|
|
Vector3 CPathServer::ms_vPathTestStartPos(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::ms_vPathTestEndPos(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::ms_vPathTestCoverOrigin(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::ms_vPathTestReferenceVector(0.0f,0.0f,0.0f);
|
|
float CPathServer::ms_fPathTestReferenceDist = 0.0f;
|
|
float CPathServer::ms_fPathTestCompletionRadius = 0.0f;
|
|
float CPathServer::ms_fPathTestEntityRadius = PATHSERVER_PED_RADIUS;
|
|
int CPathServer::ms_iPathTestNumInfluenceSpheres = 0;
|
|
TInfluenceSphere CPathServer::ms_PathTestInfluenceSpheres[MAX_NUM_INFLUENCE_SPHERES];
|
|
float CPathServer::ms_fPathTestInfluenceSphereRadius = 8.0f;
|
|
float CPathServer::ms_fPathTestInfluenceMin = 1.0f;
|
|
float CPathServer::ms_fPathTestInfluenceMax = 50.0f;
|
|
|
|
bool CPathServer::ms_bMarkVisitedPolys = false;
|
|
bool CPathServer::ms_bUnMarkVisitedPolys = false;
|
|
bool CPathServer::ms_bDrawDynamicObjectGrids = false;
|
|
bool CPathServer::ms_bDrawDynamicObjectMinMaxs = false;
|
|
bool CPathServer::ms_bDrawDynamicObjectAxes = false;
|
|
bool CPathServer::ms_bRenderMiscThings = false;
|
|
|
|
#if __BANK
|
|
bkCombo * CPathServer::ms_pObjAvoidanceModeCombo = NULL;
|
|
#endif // __BANK
|
|
|
|
s32 CPathServer::m_iObjAvoidanceModeComboIndex = (s32)CPathServer::m_eObjectAvoidanceMode;
|
|
|
|
#if !__FINAL
|
|
|
|
sysPerformanceTimer * CPathServer::m_RequestTimer = NULL;
|
|
sysPerformanceTimer * CPathServer::m_PedGenTimer = NULL;
|
|
sysPerformanceTimer * CPathServer::m_NewPedGenTimer = NULL;
|
|
sysPerformanceTimer * CPathServer::m_MainGameThreadStallTimer = NULL;
|
|
sysPerformanceTimer * CPathServer::m_MiscTimer = NULL;
|
|
sysPerformanceTimer * CPathServer::m_ImmediateModeTimer = NULL;
|
|
float CPathServer::m_fTimeTakenToIssueRequestsInMSecs = 0.0f;
|
|
float CPathServer::m_fTimeTakenToAddRemoveUpdateObjects = 0.0f;
|
|
float CPathServer::m_fTimeTakenToDoPedGenInMSecs = 0.0f;
|
|
#endif // !__PPU
|
|
float CPathServer::ms_fDebugVisPolysMaxDist = 40.0f;
|
|
float CPathServer::ms_fDebugVisNodesMaxDist = 60.0f;
|
|
float CPathServer::m_fVisNavMeshVertexShiftAmount = 0.1f;
|
|
bool CPathServer::m_bDebugPolyUnderfoot = false;
|
|
|
|
#endif // !__FINAL
|
|
|
|
s32 CPathServer::m_iNumPathRegionSwitches = 0;
|
|
TPathRegionSwitch CPathServer::m_PathRegionSwitches[MAX_NUM_PATH_REGION_SWITCHES];
|
|
|
|
s32 CPathServer::m_iNumDeferredAddDynamicObjects = 0;
|
|
s32 CPathServer::m_iNextScriptObjectHandle = 1;
|
|
s32 CPathServer::m_iNumScriptBlockingObjects = 0;
|
|
TScriptDeferredAddDynamicObject CPathServer::m_ScriptDeferredAddDynamicObjects[TScriptDeferredAddDynamicObject::ms_iMaxNum];
|
|
TScriptObjHandlePair CPathServer::m_ScriptedDynamicObjects[MAX_NUM_SCRIPTED_DYNAMIC_OBJECTS];
|
|
|
|
bool CPathServer::ms_bLoadAllHierarchicalData = true;
|
|
Vector3 CPathServer::m_vOrigin = Vector3(0,0,0);
|
|
Vector3 CPathServer::m_vLastOrigin = Vector3(0,0,0);
|
|
float CPathServer::m_fNavMeshLoadProximity = 100.0f;
|
|
float CPathServer::m_fHierarchicalNodesLoadProximity = 600.0f;
|
|
int CPathServer::ms_iRequestAndEvictMeshesFreqMs = 0;
|
|
int CPathServer::m_iTimeOfLastRequestAndEvictMeshes = 0;
|
|
bool CPathServer::ms_bRunningRequestAndEvictMeshes = false;
|
|
|
|
const fwPathServerGameInterface* CPathServer::m_GameInterface = NULL;
|
|
TNavMeshRequiredRegion CPathServer::m_NavMeshRequiredRegions[NAVMESH_MAX_REQUIRED_REGIONS];
|
|
|
|
CPathRequest CPathServer::m_PathRequests[MAX_NUM_PATH_REQUESTS];
|
|
CGridRequest CPathServer::m_GridRequests[MAX_NUM_GRID_REQUESTS];
|
|
CLineOfSightRequest CPathServer::m_LineOfSightRequests[MAX_NUM_LOS_REQUESTS];
|
|
CAudioRequest CPathServer::m_AudioRequests[MAX_NUM_AUDIO_REQUESTS];
|
|
CFloodFillRequest CPathServer::m_FloodFillRequests[MAX_NUM_FLOODFILL_REQUESTS];
|
|
CClearAreaRequest CPathServer::m_ClearAreaRequests[MAX_NUM_CLEARAREA_REQUESTS];
|
|
CClosestPositionRequest CPathServer::m_ClosestPositionRequests[MAX_NUM_CLOSESTPOSITION_REQUESTS];
|
|
|
|
u32 CPathServer::m_iNextHandle = 1;
|
|
u32 CPathServer::m_iNextPathIndexToStartFrom = 0;
|
|
u32 CPathServer::m_iNextGridIndexToStartFrom = 0;
|
|
u32 CPathServer::m_iNextLosIndexToStartFrom = 0;
|
|
u32 CPathServer::m_iNextAudioIndexToStartFrom = 0;
|
|
u32 CPathServer::m_iNextFloodFillIndexToStartFrom = 0;
|
|
u32 CPathServer::m_iNextClearAreaIndexToStartFrom = 0;
|
|
u32 CPathServer::m_iNextClosestPositionIndexToStartFrom = 0;
|
|
|
|
u32 CPathServer::m_iNumDynamicObjects = 0;
|
|
CPathServerThread CPathServer::m_PathServerThread;
|
|
|
|
CPathSearchPriorityQueue * CPathServer::m_pImmediateModePrioriryQueue = NULL;
|
|
TNavMeshPoly * CPathServer::m_pImmediateModeVisitedPolys[IMMEDIATE_MODE_QUERY_MAXNUMVISITEDPOLYS];
|
|
int CPathServer::m_iImmediateModeNumVisitedPolys = 0;
|
|
Vector3 CPathServer::m_vImmediateModeLosIsectPos(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::m_vImmediateModeWanderOrigin(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::m_vImmediateModeWanderDir(0.0f,0.0f,0.0f);
|
|
TTestLosStack CPathServer::m_ImmediateModeTestLosStack[SIZE_TEST_LOS_STACK];
|
|
int CPathServer::m_iImmediateModeTestLosStackNumEntries =0;
|
|
|
|
#ifdef GTA_ENGINE
|
|
#if __BANK
|
|
bool CPathServer::m_bCheckForThreadStalls = false;
|
|
u32 CPathServer::m_iNumThreadStallsForNavMeshes = 0;
|
|
u32 CPathServer::m_iNumThreadStallsForDynamicObjects = 0;
|
|
u32 CPathServer::m_iNumTimesCouldntUpdateObjectsDueToGameThreadAccess = 0;
|
|
float CPathServer::m_fMainGameTimeSpendOnThreadStalls = 0.0f;
|
|
s32 CPathServer::m_iNumImmediateModeLosCallsThisFrame = 0;
|
|
s32 CPathServer::m_iNumImmediateModeWanderPathsThisFrame = 0;
|
|
s32 CPathServer::m_iNumImmediateModeFleePathsThisFrame = 0;
|
|
s32 CPathServer::m_iNumImmediateModeSeekPathsThisFrame = 0;
|
|
float CPathServer::m_fTimeSpentOnImmediateModeCallsThisFrame = 0.0f;
|
|
#endif
|
|
#endif
|
|
|
|
//******************************************************************************
|
|
// Audio parameters
|
|
f32 CPathServer::m_fAudioMinPolySizeForRadiusSearch = 0.0f;
|
|
f32 CPathServer::m_fAudioVelocityCubeSize = 1.0f;
|
|
f32 CPathServer::m_fAudioVelocityDotProduct = 0.86f;
|
|
|
|
#ifdef GTA_ENGINE
|
|
//******************************************************************************
|
|
// Ped-generation member variables. These will replace the old system
|
|
|
|
bool CPathServer::m_bGameRunning = false;
|
|
#else
|
|
bool CPathServer::m_bGameRunning = true;
|
|
#endif
|
|
|
|
bool CPathServer::m_bDisplayTimeTakenToAddDynamicObjects = false;
|
|
u32 CPathServer::m_iNumTimesGaveTime = 0;
|
|
bool CPathServer::ms_bCurrentlyRemovingAndAddingObjects = false;
|
|
bool CPathServer::ms_bUseEventsForRequests = true;
|
|
bool CPathServer::ms_bProcessAllPendingPathsAtOnce = true;
|
|
u32 CPathServer::ms_iBlockRequestsFlags = 0;
|
|
s32 CPathServer::m_iTotalMemoryUsedByPrecalcPaths = 0;
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
bool CPathServer::m_bCoverPointsBufferInUse = false;
|
|
s32 CPathServer::m_iNumCoverPointsInBuffer = 0;
|
|
TCachedCoverPoint CPathServer::m_CoverPointsBuffer[SIZE_OF_COVERPOINTS_BUFFER];
|
|
|
|
bool CPathServer::m_bWaterEdgesBufferInUse = false;
|
|
bool CPathServer::m_bExtractWaterEdgesThisTimeslice = false;
|
|
s32 CPathServer::m_iNumWaterEdgesInBuffer = 0;
|
|
const float CPathServer::ms_fMinDistSqrBetweenWaterEdgePoints = 5.0f*5.0f;
|
|
const float CPathServer::ms_fFindWaterEdgesMaxDist = 20.0f;
|
|
TShortMinMax CPathServer::m_FindWaterEdgesMinMax;
|
|
Vector3 CPathServer::m_vFindWaterEdgesOrigin(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::m_vFindWaterEdgesMin(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::m_vFindWaterEdgesMax(0.0f,0.0f,0.0f);
|
|
Vector3 CPathServer::m_WaterEdgesBuffer[SIZE_OF_WATEREDGES_BUFFER];
|
|
u32 CPathServer::m_WaterEdgeAudioProperties[SIZE_OF_WATEREDGES_BUFFER];
|
|
bool CPathServer::m_bFindCover_CompletedTimeslice = false;
|
|
u32 CPathServer::m_iFindCover_NumNavMeshesIndices = 0;
|
|
TNavMeshIndex CPathServer::m_iFindCover_CurrentlyExtractingNavMeshIndex = 0;
|
|
CNavMesh * CPathServer::m_pFindCoverCurrentNavMesh = NULL;
|
|
TNavMeshIndex CPathServer::m_iFindCover_NavMeshesIndices[MAX_NUM_NAVMESHES_FOR_COVERAREAS];
|
|
u32 CPathServer::m_iCoverNumberAreas = 0;
|
|
CCoverBoundingArea CPathServer::m_CoverBoundingAreas[CCover::MAX_BOUNDING_AREAS];
|
|
u32 CPathServer::m_iFindCover_NavMeshProgress = 0;
|
|
CNavMeshQuadTree * CPathServer::m_pFindCover_ContinueFromThisQuadTreeLeaf = NULL;
|
|
u32 CPathServer::m_iFindCover_NavMeshPolyProgress = 0;
|
|
u32 CPathServer::m_iFindCover_NumIterationsRemainingThisTimeslice = 0;
|
|
u32 CPathServer::m_iFindCover_NumIterationsPerTimeslice = 64;
|
|
u32 CPathServer::m_iFrequencyOfCoverPointTimeslices_Millisecs = 600;
|
|
u32 CPathServer::m_iTimeOfLastCoverPointTimeslice_Millisecs = 0;
|
|
u32 CPathServer::m_iFrequencyOfWaterEdgeExtraction_Millisecs = 4000; // *must* be greater than the coverpoint freq
|
|
u32 CPathServer::m_iTimeOfLastWaterEdgeTimeslice_Millisecs = 0;
|
|
|
|
#if !__FINAL
|
|
sysPerformanceTimer * CPathServer::m_ExtractCoverPointsTimer = NULL;
|
|
sysPerformanceTimer * CPathServer::m_TrackObjectsTimer = NULL;
|
|
float CPathServer::m_fLastTimeTakenToExtractCoverPointsMs = 0.0f;
|
|
float CPathServer::m_fTimeToLockDataForTracking = 0.0f;
|
|
float CPathServer::m_fTimeToTrackAllPeds = 0.0f;
|
|
#endif
|
|
|
|
#endif // GTA_ENGINE
|
|
|
|
float CPathServer::ms_fPedGenAlgorithmNodeFloodFillDist = 30.0f;
|
|
|
|
void InitCountCoverBitsTable()
|
|
{
|
|
for(int i=0; i<256; i++)
|
|
{
|
|
int iNumBits = 0;
|
|
if(i&1) iNumBits++;
|
|
if(i&2) iNumBits++;
|
|
if(i&4) iNumBits++;
|
|
if(i&8) iNumBits++;
|
|
if(i&16) iNumBits++;
|
|
if(i&32) iNumBits++;
|
|
if(i&64) iNumBits++;
|
|
if(i&128) iNumBits++;
|
|
g_iCountCoverBitsTable[i] = (u8)iNumBits;
|
|
}
|
|
}
|
|
|
|
CPathServer::CPathServer()
|
|
{
|
|
|
|
}
|
|
|
|
CPathServer::~CPathServer()
|
|
{
|
|
Shutdown();
|
|
}
|
|
|
|
|
|
|
|
//***********************************************************************************************************
|
|
// Init()
|
|
//
|
|
// This initialises the system, and creates CPathServerThread class.
|
|
// iProcessorIndex' - allows the worker thread to be run on a specific CPU. Leave as zero for now.
|
|
// pRelativePathAndFilenameForNavMeshes - specifies a ".img" file which contains all the navmeshes for
|
|
// the level. The path should be relative to the game's "data/models/cdimages" folder.
|
|
// pPathForDatFile - path to the ".dat" file used to initialise the navigation system. Usually "data".
|
|
// pDebugPathForNavFiles - for development only, specifies a folder where the ".nav" files can be found
|
|
//
|
|
//***********************************************************************************************************
|
|
|
|
bool
|
|
CPathServer::Init(const fwPathServerGameInterface &gameInterface
|
|
, EProcessingMode eProcessingMode
|
|
, u32 iProcessorIndex
|
|
, const char * pRelativePathAndFilenameForNavMeshes, const char *
|
|
#ifndef GTA_ENGINE
|
|
pPathForDatFile
|
|
#endif
|
|
, const char * pDebugPathForNavFiles)
|
|
{
|
|
USE_MEMBUCKET(MEMBUCKET_GAMEPLAY);
|
|
Assert(eProcessingMode == ESingleThreaded || eProcessingMode == EMultiThreaded);
|
|
|
|
Assert(!m_GameInterface); // Not expected to get called more than once without a matching Shutdown().
|
|
m_GameInterface = &gameInterface;
|
|
//@@: location CPATHSERVER_INIT
|
|
m_eProcessingMode = eProcessingMode;
|
|
m_iTotalMemoryUsed = sizeof(CPathServer);
|
|
|
|
InitCountCoverBitsTable();
|
|
|
|
#if !__FINAL
|
|
m_iVisualiseNavMeshes = CPathServer::NavMeshVis_Off;
|
|
m_iVisualisePathServerInfo = CPathServer::PathServerVis_Off;
|
|
m_RequestTimer = rage_new sysPerformanceTimer("PathServer Requests Timer");
|
|
m_PedGenTimer = rage_new sysPerformanceTimer("Ped-Gen Timer");
|
|
m_NavMeshLoadingTimer = rage_new sysPerformanceTimer("NavMesh Loading Timer");
|
|
m_NavMesh2ndLoadingTimer = rage_new sysPerformanceTimer("NavMesh 2nd Loading Timer");
|
|
m_MainGameThreadStallTimer = rage_new sysPerformanceTimer("Main Game Thread-Stall Timer");
|
|
m_MiscTimer = rage_new sysPerformanceTimer("Misc Timer");
|
|
m_ImmediateModeTimer = rage_new sysPerformanceTimer("Immediate-Mode Timer");
|
|
#ifdef GTA_ENGINE
|
|
m_ExtractCoverPointsTimer = rage_new sysPerformanceTimer("Extract CoverPoints Timer");
|
|
m_TrackObjectsTimer = rage_new sysPerformanceTimer("Track Objects Timer");
|
|
#endif
|
|
#endif
|
|
|
|
m_iNumTimesGaveTime = 0;
|
|
ms_bInitialised = true;
|
|
|
|
if(!pRelativePathAndFilenameForNavMeshes)
|
|
{
|
|
pRelativePathAndFilenameForNavMeshes = "navmeshes.img";
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(!m_pNavMeshStores[kNavDomainRegular])
|
|
{
|
|
Assertf(false, "PathServer::Init() - didn't initialise properly.");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
m_DynamicNavMeshStore.Init(m_iMaxDynamicNavmeshTypes);
|
|
|
|
//*********************************************************************************************
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
(void)pDebugPathForNavFiles; // This variable is used offline
|
|
|
|
//RegisterStreamingModule();
|
|
|
|
#else // GTA_ENGINE
|
|
|
|
RegisterStreamingModule(pPathForDatFile);
|
|
|
|
//*********************************************************************************************
|
|
|
|
if(!LoadAllMeshes(pDebugPathForNavFiles))
|
|
{
|
|
return false;
|
|
}
|
|
#endif // GTA_ENGINE
|
|
|
|
// Create the tessellation navmesh, which stores all the polys temporarily tessellated around dynamic objects
|
|
if(ms_bDoPolyTessellation)
|
|
{
|
|
CreateTessellationNavMesh();
|
|
}
|
|
|
|
// TODO: Figure out what to do here. If TAdjPoly::ms_iAdjacentNavmeshOffsets is going to be used for something,
|
|
// we may need to keep one array for each domain. Right now, nothing uses it though, so we just initialize
|
|
// from the regular navigation domain.
|
|
TAdjPoly::InitAdjacentNavmeshOffsets(m_pNavMeshStores[kNavDomainRegular]->GetNumMeshesInX());
|
|
|
|
if(!m_PathServerThread.Init(iProcessorIndex))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_pImmediateModePrioriryQueue = rage_new CPathSearchPriorityQueue(IMMEDIATE_MODE_QUERY_MAXNUMVISITEDPOLYS);
|
|
|
|
Reset();
|
|
|
|
return true;
|
|
}
|
|
|
|
//************************************************************
|
|
// ReadDatFile
|
|
// This function reads in the "nav.dat" file, which details
|
|
// how many sectors there are for each navmesh in the world.
|
|
//************************************************************
|
|
bool
|
|
CPathServer::ReadDatFile(const char * pGameDataPath, CNavDatInfo & navMeshesInfo, CNavDatInfo & GTA_ENGINE_ONLY(navNodesInfo))
|
|
{
|
|
#ifdef GTA_ENGINE
|
|
|
|
(void)pGameDataPath; // This variable is used offline
|
|
|
|
ASSET.PushFolder("common:/data/");
|
|
fiStream * stream = ASSET.Open("nav", "dat", true);
|
|
ASSET.PopFolder();
|
|
|
|
if(stream)
|
|
{
|
|
fiAsciiTokenizer token;
|
|
token.Init(NULL, stream);
|
|
|
|
// Read in each line
|
|
char lineBuff[1024];
|
|
while(token.GetLine(lineBuff, 1024) > 0)
|
|
{
|
|
if(lineBuff[0] == '#')
|
|
{
|
|
continue;
|
|
}
|
|
int iNumMatched = 0;
|
|
int iValue;
|
|
|
|
// SECTORS_PER_NAVMESH defines the resolution which this game's navmesh system works at.
|
|
// A lower value equals more files but smaller sized sections.
|
|
// A higher values means less files, but larger filesizes.
|
|
iNumMatched = sscanf(lineBuff, "SECTORS_PER_NAVMESH = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
#if HEIGHTMAP_GENERATOR_TOOL
|
|
iValue = gv::WORLD_CELLS_PER_TILE;
|
|
#endif
|
|
const int iNumSectorsPerMesh = iValue;
|
|
const float fMeshSize = CPathServerExtents::GetWorldWidthOfSector() * ((float)iNumSectorsPerMesh);
|
|
|
|
navMeshesInfo.iSectorsPerMesh = iNumSectorsPerMesh;
|
|
navMeshesInfo.fMeshSize = fMeshSize;
|
|
#if HEIGHTMAP_GENERATOR_TOOL
|
|
navMeshesInfo.iNumMeshesInX = (gv::WORLD_BOUNDS_MAX_X - gv::WORLD_BOUNDS_MIN_X)/gv::WORLD_TILE_SIZE;
|
|
navMeshesInfo.iNumMeshesInY = (gv::WORLD_BOUNDS_MAX_Y - gv::WORLD_BOUNDS_MIN_Y)/gv::WORLD_TILE_SIZE;
|
|
#else
|
|
navMeshesInfo.iNumMeshesInX = 100; //(WORLD_WIDTHINSECTORS / iNumSectorsPerMesh);
|
|
navMeshesInfo.iNumMeshesInY = 100; //(WORLD_DEPTHINSECTORS / iNumSectorsPerMesh);
|
|
#endif
|
|
navMeshesInfo.iMaxMeshIndex = navMeshesInfo.iNumMeshesInX * navMeshesInfo.iNumMeshesInY;
|
|
|
|
continue;
|
|
}
|
|
// SECTORS_PER_NAVMESH defines the resolution which this game's navnodes system works at.
|
|
// Currently this *must* be a multiple of SECTORS_PER_NAVMESH, above
|
|
iNumMatched = sscanf(lineBuff, "SECTORS_PER_NAVNODES = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
const int iNumSectorsPerMesh = iValue;
|
|
const float fMeshSize = CPathServerExtents::GetWorldWidthOfSector() * ((float)iNumSectorsPerMesh);
|
|
|
|
navNodesInfo.iSectorsPerMesh = iNumSectorsPerMesh;
|
|
navNodesInfo.fMeshSize = fMeshSize;
|
|
navNodesInfo.iNumMeshesInX = 100 / (navNodesInfo.iSectorsPerMesh/navMeshesInfo.iSectorsPerMesh);
|
|
navNodesInfo.iNumMeshesInY = 100 / (navNodesInfo.iSectorsPerMesh/navMeshesInfo.iSectorsPerMesh);
|
|
navNodesInfo.iMaxMeshIndex = navNodesInfo.iNumMeshesInX * navNodesInfo.iNumMeshesInY;
|
|
#endif
|
|
continue;
|
|
}
|
|
// NAVMESH_LOAD_DISTANCE defines the distance at which meshes are loaded/streamed in.
|
|
iNumMatched = sscanf(lineBuff, "NAVMESH_LOAD_DISTANCE = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
m_fNavMeshLoadProximity = (float)iValue;
|
|
continue;
|
|
}
|
|
// MAX_NUM_NAVMESHES_IN_ANY_PACKFILE
|
|
iNumMatched = sscanf(lineBuff, "MAX_NUM_NAVMESHES_IN_ANY_LEVEL = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
navMeshesInfo.iNumMeshesInAnyLevel = iValue;
|
|
continue;
|
|
}
|
|
// MAX_NUM_NAVNODES_IN_ANY_PACKFILE
|
|
iNumMatched = sscanf(lineBuff, "MAX_NUM_NAVNODES_IN_ANY_LEVEL = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
Assert(iValue >= 0 && iValue <= 10000);
|
|
navNodesInfo.iNumMeshesInAnyLevel = iValue;
|
|
continue;
|
|
}
|
|
// MAX_NUM_DYNAMIC_NAVMESH_TYPES
|
|
iNumMatched = sscanf(lineBuff, "MAX_NUM_DYNAMIC_NAVMESH_TYPES = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
Assert(iValue >= 0 && iValue <= 10000);
|
|
m_iMaxDynamicNavmeshTypes = iValue;
|
|
continue;
|
|
}
|
|
}
|
|
stream->Close();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
#else //GTA_ENGINE
|
|
|
|
char fullFileName[512];
|
|
sprintf(fullFileName, "%s\\nav.dat", pGameDataPath);
|
|
|
|
FILE * pFile = fopen(fullFileName, "rt");
|
|
if(!pFile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
char string[256];
|
|
|
|
while(fgets(string, 256, pFile))
|
|
{
|
|
if(string[0] == '#')
|
|
continue;
|
|
|
|
int iNumMatched = 0;
|
|
int iValue;
|
|
|
|
// SECTORS_PER_NAVMESH defines the resolution which this game's navmesh system works at.
|
|
iNumMatched = sscanf(string, "SECTORS_PER_NAVMESH = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
#if HEIGHTMAP_GENERATOR_TOOL
|
|
iValue = CGameWorldHeightMap::WORLD_CELLS_PER_TILE;
|
|
#endif
|
|
const int iNumSectorsPerMesh = iValue;
|
|
const float fMeshSize = CPathServerExtents::GetWorldWidthOfSector() * ((float)iNumSectorsPerMesh);
|
|
|
|
navMeshesInfo.iSectorsPerMesh = iNumSectorsPerMesh;
|
|
navMeshesInfo.fMeshSize = fMeshSize;
|
|
#if HEIGHTMAP_GENERATOR_TOOL
|
|
navMeshesInfo.iNumMeshesInX = (gv::WORLD_BOUNDS_MAX_X - gv::WORLD_BOUNDS_MIN_X)/gv::WORLD_TILE_SIZE;
|
|
navMeshesInfo.iNumMeshesInY = (gv::WORLD_BOUNDS_MAX_Y - gv::WORLD_BOUNDS_MIN_Y)/gv::WORLD_TILE_SIZE;
|
|
#else
|
|
navMeshesInfo.iNumMeshesInX = 100; //(WORLD_WIDTHINSECTORS / iNumSectorsPerMesh);
|
|
navMeshesInfo.iNumMeshesInY = 100; //(WORLD_DEPTHINSECTORS / iNumSectorsPerMesh);
|
|
#endif
|
|
navMeshesInfo.iMaxMeshIndex = navMeshesInfo.iNumMeshesInX * navMeshesInfo.iNumMeshesInY;
|
|
continue;
|
|
}
|
|
// SECTORS_PER_NAVMESH defines the resolution which this game's navnodes system works at.
|
|
// Currently this *must* be a multiple of SECTORS_PER_NAVMESH, above
|
|
iNumMatched = sscanf(string, "SECTORS_PER_NAVNODES = %i", &iValue);
|
|
if(iNumMatched)
|
|
{
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
const int iNumSectorsPerMesh = iValue;
|
|
const float fMeshSize = CPathServerExtents::GetWorldWidthOfSector() * ((float)iNumSectorsPerMesh);
|
|
|
|
navNodesInfo.iSectorsPerMesh = iNumSectorsPerMesh;
|
|
navNodesInfo.fMeshSize = fMeshSize;
|
|
navNodesInfo.iNumMeshesInX = 100 / (navNodesInfo.iSectorsPerMesh/navMeshesInfo.iSectorsPerMesh);
|
|
navNodesInfo.iNumMeshesInY = 100 / (navNodesInfo.iSectorsPerMesh/navMeshesInfo.iSectorsPerMesh);
|
|
navNodesInfo.iMaxMeshIndex = navNodesInfo.iNumMeshesInX * navNodesInfo.iNumMeshesInY;
|
|
#endif
|
|
continue;
|
|
}
|
|
}
|
|
|
|
fclose(pFile);
|
|
return true;
|
|
|
|
#endif
|
|
}
|
|
|
|
void CPathServer::ReadIndexMappingFiles()
|
|
{
|
|
#ifdef GTA_ENGINE
|
|
#if __REMAP_NAVMESH_INDICES
|
|
static const CDataFileMgr::DataFileType s_FileTypesForDataSets[] =
|
|
{
|
|
CDataFileMgr::NAVMESH_INDEXREMAPPING_FILE,
|
|
CDataFileMgr::HEIGHTMESH_INDEXREMAPPING_FILE
|
|
};
|
|
CompileTimeAssert(NELEM(s_FileTypesForDataSets) == kNumNavDomains);
|
|
|
|
for(int i = 0; i < kNumNavDomains; i++)
|
|
{
|
|
if(!fwPathServer::GetIsNavDomainEnabled((aiNavDomain)i))
|
|
continue;
|
|
aiNavMeshStore* pStore = m_pNavMeshStores[i];
|
|
pStore->AllocateMapping(pStore->GetMaxMeshIndex());
|
|
CDataFileMgr::DataFileType type = s_FileTypesForDataSets[i];
|
|
if(!ReadIndexMappingFile(*pStore, type))
|
|
{
|
|
// The backup code below for creating an identity mapping I found to not
|
|
// be very helpful - if we couldn't load the index mapping file for
|
|
// heightmeshes, just leaving the indices at their existing values means
|
|
// that we don't try to load anything, which is what I would expect.
|
|
// So, we only do that for the regular navigation domain. /FF
|
|
|
|
if(i == kNavDomainRegular)
|
|
{
|
|
for(int m=0; m<pStore->GetMaxMeshIndex(); m++)
|
|
{
|
|
pStore->SetMapping(m, (s16)m);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
|
|
#if !__FINAL && SANITY_CHECK_TESSELLATION
|
|
|
|
void CPathServer::SanityCheckPolyConnectionsForAllNavMeshes(void)
|
|
{
|
|
u32 i,p;
|
|
for(i=0; i<m_iTotalNumNavMeshes; i++)
|
|
{
|
|
CNavMesh * pNavMesh = m_pNavMeshStore.GetPtr(i);
|
|
|
|
if(!pNavMesh)
|
|
continue;
|
|
|
|
for(p=0; p<pNavMesh->m_iNumPolys; p++)
|
|
{
|
|
TNavMeshPoly * pPoly = pNavMesh->GetPoly(p);
|
|
if(pPoly->GetReplacedByTessellation())
|
|
continue;
|
|
|
|
m_PathServerThread.SanityCheckPolyConnections(pNavMesh, pPoly);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
//*****************************************************************************
|
|
// Remove any tessellated polys which are derived from polys in this navmesh,
|
|
// by resetting the in-use flag in the 'm_TessellationPolysInUse' bit-array.
|
|
// Visit all of their adjacent polys, and if they link to the poly we are
|
|
// removing - then we reset their adjacency to its initial values
|
|
//*****************************************************************************
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
void CPathServer::PrepareToUnloadNavMeshDataSetNormal(void * UNUSED_PARAM(pNavMesh))
|
|
{
|
|
// NB: We might want to add a 'ForceAbortCurrentPathRequest()' call here?
|
|
// Otherwise we may risk incurring a stall on the main thread, due to the
|
|
// pathserver's activity.
|
|
CPathServer::ForceAbortCurrentPathRequest();
|
|
|
|
// Wait for access from pathfinding thread (and/or main game thread) to finish
|
|
LOCK_NAVMESH_DATA;
|
|
|
|
DetessellateAllPolys();
|
|
}
|
|
|
|
void
|
|
CPathServer::PrepareToUnloadNavMeshDataSetHeightMesh(void * UNUSED_PARAM(pNavMesh))
|
|
{
|
|
// Nothing to do here, I think. /FF
|
|
}
|
|
|
|
void
|
|
CPathServer::PrepareToUnloadHierarchicalNavData(void * UNUSED_PARAM(pNavData))
|
|
{
|
|
// Wait for access from pathfinding thread (and/or main game thread) to finish
|
|
// LOCK_NAVMESH_DATA;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
//*********************************************************************************
|
|
// CPathServer::Process
|
|
// This function must be called once a frame from the main game thread. Here is
|
|
// where we request/evict navmeshes, and also make copies of some data from the
|
|
// CCover class.
|
|
//*********************************************************************************
|
|
|
|
void
|
|
CPathServer::Process(const Vector3 & vOrigin)
|
|
{
|
|
USE_MEMBUCKET(MEMBUCKET_GAMEPLAY);
|
|
m_vOrigin = vOrigin;
|
|
m_iNumTimesGaveTime = 0;
|
|
|
|
//
|
|
#if !__FINAL
|
|
m_fTimeSpentProcessingThisGameTurn = m_fRunningTotalOfTimeOnPathRequests;
|
|
m_fRunningTotalOfTimeOnPathRequests = 0.0f;
|
|
#endif
|
|
|
|
// Update the player's origin (always entry zero in this array)
|
|
m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_bActive = true;
|
|
m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_vOrigin = Vector2(vOrigin.x, vOrigin.y);
|
|
m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_fNavMeshLoadRadius = m_fNavMeshLoadProximity;
|
|
m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_fHierarchicalNodesLoadRadius = m_fHierarchicalNodesLoadProximity;
|
|
m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_iThreadId = static_cast<scrThreadId>(0xFFFFFFFF);
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
static dev_float fForceUpdateMagSqr = 50.0f * 50.0f;
|
|
if( (m_vLastOrigin - m_vOrigin).XYMag2() > fForceUpdateMagSqr)
|
|
{
|
|
CPathServer::RequestAndEvictNextFrame();
|
|
}
|
|
|
|
// Process the requesting & removal of navmeshes
|
|
RequestAndEvictMeshes();
|
|
|
|
#endif
|
|
|
|
m_vLastOrigin = m_vOrigin;
|
|
|
|
|
|
//******************************************************************************
|
|
// We need to make a copy of the bounding areas from the CCover class.
|
|
// We'll only do this when the 'm_bCoverPointsBufferInUse' is set to false.
|
|
// We needn't worry about being locked out of access to this buffer, since
|
|
// this Process() call occurs every frame - whereas the update function runs
|
|
// at only every 200ms (or so).
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
if(!m_bCoverPointsBufferInUse)
|
|
{
|
|
m_bCoverPointsBufferInUse = true;
|
|
m_iCoverNumberAreas = CCover::CopyBoundingAreas(&m_CoverBoundingAreas[0]);
|
|
m_bCoverPointsBufferInUse = false;
|
|
}
|
|
|
|
m_AmbientPedGeneration.ProcessPedGenBlockedAreas();
|
|
|
|
#if !__FINAL
|
|
// If stress-testing the pathfinder, then request (up to) 10 paths every frame between
|
|
// the player and the focus ped.
|
|
if(m_bStressTestPathFinding)
|
|
{
|
|
for(int i=0; i<NUM_STRESS_TEST_PATHS; i++)
|
|
{
|
|
TPathHandle hPath = m_iStressTestPathHandles[i];
|
|
if(hPath != PATH_HANDLE_NULL)
|
|
{
|
|
if(IsRequestResultReady(hPath)==ERequest_Ready)
|
|
{
|
|
CancelRequestInternal(hPath, false);
|
|
m_iStressTestPathHandles[i] = PATH_HANDLE_NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CEntity* pFocusEntity = CDebugScene::FocusEntities_Get(0);
|
|
if(pFocusEntity && pFocusEntity->GetIsTypePed())
|
|
{
|
|
CPed* pFocusPed = static_cast<CPed*>(pFocusEntity);
|
|
|
|
m_iStressTestPathHandles[i] = RequestPath(VEC3V_TO_VECTOR3(FindPlayerPed()->GetTransform().GetPosition()), VEC3V_TO_VECTOR3(pFocusPed->GetTransform().GetPosition()), 0, 1.0f, pFocusPed );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_bStressTestGetClosestPos)
|
|
{
|
|
Vector3 vPlayerPos = VEC3V_TO_VECTOR3(FindPlayerPed()->GetTransform().GetPosition());
|
|
static const int iNumIters = 20;
|
|
for(int t=0; t<iNumIters; t++)
|
|
{
|
|
const Vector3 vRandom(fwRandom::GetRandomNumberInRange(-50.0f,50.0f), fwRandom::GetRandomNumberInRange(-50.0f,50.0f), fwRandom::GetRandomNumberInRange(-50.0f,50.0f));
|
|
Vector3 vOut;
|
|
CPathServer::GetClosestPositionForPed(vPlayerPos + vRandom, vOut, 50.0f);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
// Look through all the path requests and cancel those which haven't been polled in the timeout period.
|
|
// This is to stop those requests from clogging up the free slots in the pathfinder. This function will assert
|
|
// also, as it is up to clients to ensure that all requests they issue are either retrieved or canceled.
|
|
//
|
|
// JB: This has been moved forwards from within the navmesh lock below, to avoid a rare deadlock. Checking
|
|
// for state requests requires a LOCK_NAVMESH_REQUESTS lock, but sometimes a path request from the main thread
|
|
// may do the opposite - locking the requests and then the navmeshes. If unlucky we get could a stalemate.
|
|
|
|
if(fwTimer::GetTimeInMilliseconds() - CPathServer::m_iLastTimeCheckedForStaleRequests >= CPathServer::m_iCheckForStaleRequestsFreq)
|
|
{
|
|
m_PathServerThread.CheckForStaleRequests();
|
|
|
|
CPathServer::m_iLastTimeCheckedForStaleRequests = fwTimer::GetTimeInMilliseconds();
|
|
}
|
|
|
|
|
|
LOCK_REQUESTS
|
|
|
|
//-------------------------------------------------------------------------------------------------------
|
|
// Handle the disabling of timeslicing on peds who are waiting for a request which has completed.
|
|
// For now only performed for path requests
|
|
// NB: We could also disable timeslicing for peds who have a request pending in order to reduce latency
|
|
// by a further frame - but this could adversely affect performance - we can have a lot of peds waiting
|
|
// for paths.
|
|
|
|
for(s32 r=0; r<MAX_NUM_PATH_REQUESTS; r++)
|
|
{
|
|
if(m_PathRequests[r].m_bComplete && !m_PathRequests[r].m_bSlotEmpty)
|
|
{
|
|
CPed * pPed = (CPed*)m_PathRequests[r].m_PedWaitingForThisRequest.Get();
|
|
if(pPed)
|
|
{
|
|
pPed->SetPedResetFlag( CPED_RESET_FLAG_WaitingForCompletedPathRequest, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
for(s32 r=0; r<MAX_NUM_PATH_REQUESTS; r++)
|
|
{
|
|
if(m_PathRequests[r].m_bRequestPending && !m_PathRequests[r].m_bRequestActive)
|
|
{
|
|
if (m_PathRequests[r].m_bKeepUpdatingPedStartPosition && m_PathRequests[r].m_PedWaitingForThisRequest.Get())
|
|
{
|
|
CPed* pPed = static_cast<CPed*>(m_PathRequests[r].m_PedWaitingForThisRequest.Get());
|
|
Vector3 vPathStartPosition = VEC3V_TO_VECTOR3(m_PathRequests[r].m_PedWaitingForThisRequest.Get()->GetTransform().GetPosition());
|
|
|
|
if (m_PathRequests[r].m_fDistAheadOfPed > 0.f)
|
|
{
|
|
CTask* pTask = pPed->GetPedIntelligence()->GetActiveMovementTask();
|
|
if (pTask->GetTaskType() == CTaskTypes::TASK_MOVE_FOLLOW_NAVMESH)
|
|
{
|
|
vPathStartPosition = ((CTaskNavBase*)pTask)->PathRequest_DistanceAheadCalculation(m_PathRequests[r].m_fDistAheadOfPed, pPed);
|
|
}
|
|
}
|
|
|
|
if(pPed->GetNavMeshTracker().IsUpToDate(vPathStartPosition))
|
|
{
|
|
m_PathRequests[r].m_vPathStart = pPed->GetNavMeshTracker().GetLastPosition();
|
|
m_PathRequests[r].m_StartNavmeshAndPoly = pPed->GetNavMeshTracker().GetNavMeshAndPoly();
|
|
}
|
|
else
|
|
{
|
|
m_PathRequests[r].m_vPathStart = vPathStartPosition;
|
|
m_PathRequests[r].m_StartNavmeshAndPoly.Reset();
|
|
}
|
|
|
|
// This chunk is to make it less likely to end up on the other side of a thin wall due to dynamic objects
|
|
if (!pPed->GetNavMeshTracker().GetIsValid() && pPed->GetNavMeshTracker().IsLastNavMeshIntersectionValid())
|
|
{
|
|
Vector3 vNavDiff = m_PathRequests[r].m_vPathStart - pPed->GetNavMeshTracker().GetLastNavMeshIntersection();
|
|
if (vNavDiff.XYMag2() < 0.3f * 0.3f)
|
|
{
|
|
m_PathRequests[r].m_vPathStart = pPed->GetNavMeshTracker().GetLastNavMeshIntersection();
|
|
m_PathRequests[r].m_vPathStart.z += 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_PathRequests[r].m_EntityEndPosition.Get())
|
|
m_PathRequests[r].m_vPathEnd = VEC3V_TO_VECTOR3(m_PathRequests[r].m_EntityEndPosition.Get()->GetTransform().GetPosition());
|
|
|
|
}
|
|
}
|
|
|
|
UNLOCK_REQUESTS
|
|
|
|
// Processed the deferred adding of scripted blocking objects.
|
|
// This is done at a safe part of the frame when we can ensure no navmesh queries are ongoing.
|
|
// The pathserver thread yields whenever ms_iBlockRequestsFlags is non-zero
|
|
|
|
if( m_iNumDeferredAddDynamicObjects > 0 )
|
|
{
|
|
const u32 iFlag = BLOCK_REQUESTS_ON_ADD_DYNAMIC_OBJECTS;
|
|
sysInterlockedOr(&ms_iBlockRequestsFlags, iFlag);
|
|
|
|
ForceAbortCurrentPathRequest();
|
|
ProcessAddDeferredDynamicObjects();
|
|
|
|
sysInterlockedAnd(&ms_iBlockRequestsFlags, ~iFlag);
|
|
}
|
|
|
|
#endif // GTA_ENGINE
|
|
|
|
}
|
|
|
|
|
|
void
|
|
CPathServer::Process(void)
|
|
{
|
|
#ifdef GTA_ENGINE
|
|
|
|
m_bGameRunning = true;
|
|
|
|
static const u32 iMaxNullTime = 10000;
|
|
static u32 iLastTimePedWasNonNull = 0;
|
|
|
|
iLastTimePedWasNonNull = fwTimer::GetTimeInMilliseconds();
|
|
Process(CFocusEntityMgr::GetMgr().GetPos());
|
|
|
|
if((fwTimer::GetTimeInMilliseconds() - iLastTimePedWasNonNull) > iMaxNullTime)
|
|
{
|
|
Assertf(false, "FindPlayerPed() has been returning NULL for over 10 seconds. Something is wrong.");
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
//**********************************************************************************************
|
|
// This function requests new navmeshes, and removes others based upon the m_vOrigin member.
|
|
// Meshes which overlap the 'm_fNavMeshLoadProximity' radius of interest from the m_vOrigin,
|
|
// are requested (if not already loaded).
|
|
// We use the last origin, in order to remove meshes which are no longer with range.
|
|
//
|
|
// NB : It will be necessary to delay the evicting of meshes, so that we don't have situations
|
|
// where the player repeatedly crosses the request/evict boundary and causes the same
|
|
// navmeshes to be repeatedly requested & evicted, thereby screwing up the streaming.
|
|
//
|
|
// NB2 : We should implement a 2nd origin, so that game-designers can force navmeshes to be
|
|
// loaded in around another location - in much the same way that the collision system can
|
|
// specify a 2nd origin as well.
|
|
//
|
|
//**********************************************************************************************
|
|
|
|
void CPathServer::RequestAndEvictNextFrame()
|
|
{
|
|
ms_iTimeToNextRequestAndEvict = 0;
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
atArray<aiMeshLoadRegion> loadRegions(0,64);
|
|
u32 g_bTryLockNumTimesFailed = 0;
|
|
|
|
void CPathServer::RequestAndEvictMeshes()
|
|
{
|
|
if(ms_bStreamingDisabled)
|
|
return;
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
s32 iTimeBefore = ms_iTimeToNextRequestAndEvict;
|
|
ms_iTimeToNextRequestAndEvict -= fwTimer::GetTimeStepInMilliseconds();
|
|
if(ms_iTimeToNextRequestAndEvict >= 0)
|
|
return;
|
|
|
|
//-------------------------------------------
|
|
// Stop pathserver from processing requests
|
|
|
|
if(iTimeBefore >= 0)
|
|
{
|
|
const u32 iFlag = BLOCK_REQUESTS_ON_REQUEST_AND_EVICT;
|
|
sysInterlockedOr(&ms_iBlockRequestsFlags, iFlag);
|
|
}
|
|
|
|
|
|
// Can we take the critical section for the navmeshes?
|
|
// If so we can proceed, otherwise wait until available - the pathserver thread will
|
|
// not attempt to service any more requests while ms_iTimeToNextRequestAndEvict <= 0
|
|
|
|
if( !m_NavMeshDataCriticalSectionToken.TryLock())
|
|
{
|
|
g_bTryLockNumTimesFailed++;
|
|
return;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
ms_bRunningRequestAndEvictMeshes = true;
|
|
|
|
int r;
|
|
|
|
#ifdef GTA_ENGINE
|
|
STRVIS_SET_CONTEXT(strStreamingVisualize::PATHSERVER);
|
|
#endif // GTA_ENGINE
|
|
|
|
//************************************************************************
|
|
// Copy regions for passing into navmesh RequestAndEvict() function
|
|
|
|
loadRegions.clear();
|
|
for(r=0; r<NAVMESH_MAX_REQUIRED_REGIONS; r++)
|
|
{
|
|
aiMeshLoadRegion region;
|
|
|
|
if(m_NavMeshRequiredRegions[r].m_bActive)
|
|
{
|
|
region.m_vOrigin = m_NavMeshRequiredRegions[r].m_vOrigin;
|
|
region.m_fRadius = m_NavMeshRequiredRegions[r].m_fNavMeshLoadRadius;
|
|
loadRegions.Append() = region;
|
|
}
|
|
}
|
|
|
|
// If player switch is active, prevent request/evict of navmeshes until we are close to the destination
|
|
// Without this step we find that the streaming has a deluge of requests to service once it gets to the
|
|
// destination - many of which are no longer required (I suppose they must get buffered during the switch)
|
|
bool bBlockRequestEvict = false;
|
|
|
|
if( g_PlayerSwitch.IsActive() )
|
|
{
|
|
const Vector3 vPos = CFocusEntityMgr::GetMgr().GetPos();
|
|
const Vector3 vDest = VEC3V_TO_VECTOR3(g_PlayerSwitch.GetMgr(g_PlayerSwitch.GetSwitchType())->GetDestPos());
|
|
|
|
if((vPos - vDest).XYMag2() > 100.0f)
|
|
{
|
|
bBlockRequestEvict = true;
|
|
}
|
|
}
|
|
|
|
//*********************************************************************
|
|
|
|
LOCK_STORE_LOADED_MESHES;
|
|
|
|
if(!bBlockRequestEvict)
|
|
{
|
|
m_pNavMeshStores[kNavDomainRegular]->RequestAndEvict(loadRegions, PrepareToUnloadNavMeshDataSetNormal);
|
|
|
|
if(fwPathServer::GetIsNavDomainEnabled(kNavDomainHeightMeshes))
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->RequestAndEvict(loadRegions, PrepareToUnloadNavMeshDataSetHeightMesh);
|
|
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
//*********************************************************************
|
|
// Expand regions for passing into navnode RequestAndEvict() function
|
|
|
|
for(r=0; r<loadRegions.GetCount(); r++)
|
|
{
|
|
loadRegions[r].m_fRadius *= m_fHierarchicalNodesLoadProximity / m_fNavMeshLoadProximity;
|
|
}
|
|
|
|
m_pNavNodesStore->RequestAndEvict(loadRegions, PrepareToUnloadHierarchicalNavData);
|
|
#endif
|
|
|
|
}
|
|
|
|
//**********************************************************************************
|
|
// Process adding/removal of dynamic navmeshes.
|
|
|
|
aiNavMeshStore* pMeshStoreForDynamicMeshes = m_pNavMeshStores[kNavDomainRegular];
|
|
|
|
for(int i=0; i<m_DynamicNavMeshStore.GetNum(); i++)
|
|
{
|
|
const CModelInfoNavMeshRef & ref = m_DynamicNavMeshStore.Get(i);
|
|
|
|
// Request
|
|
if(ref.m_iNumRefs > 0 && !ref.m_pBackUpNavmeshCopy)
|
|
{
|
|
if(!pMeshStoreForDynamicMeshes->HasObjectLoaded(ref.m_iStreamingIndex))
|
|
{
|
|
// These RequestObject() calls are sometimes ignored by the streaming - so I've added these flags.
|
|
pMeshStoreForDynamicMeshes->StreamingRequest(ref.m_iStreamingIndex, STRFLAG_FORCE_LOAD|STRFLAG_PRIORITY_LOAD);
|
|
}
|
|
}
|
|
// Remove
|
|
else if(ref.m_iNumRefs <= 0 && ref.m_pBackUpNavmeshCopy)
|
|
{
|
|
PrepareToUnloadNavMeshDataSetNormal(ref.m_pBackUpNavmeshCopy);
|
|
pMeshStoreForDynamicMeshes->StreamingRemove(ref.m_iStreamingIndex);
|
|
}
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
STRVIS_SET_CONTEXT(strStreamingVisualize::NONE);
|
|
#endif // GTA_ENGINE
|
|
|
|
ms_bRunningRequestAndEvictMeshes = false;
|
|
ms_iTimeToNextRequestAndEvict = ms_iRequestAndEvictFreqMS;
|
|
m_NavMeshDataCriticalSectionToken.Unlock();
|
|
|
|
|
|
//---------------------------------------------
|
|
// Allow pathserver to process requests again
|
|
|
|
const u32 iFlag = BLOCK_REQUESTS_ON_REQUEST_AND_EVICT;
|
|
sysInterlockedAnd(&ms_iBlockRequestsFlags, ~iFlag);
|
|
}
|
|
|
|
|
|
|
|
// Requests navmeshes to be loaded in the specified area
|
|
bool CPathServer::AddNavMeshRegion( const NavMeshRequiredRegion region, const scrThreadId iThreadId, const float fOriginX, const float fOriginY, const float fRadius )
|
|
{
|
|
Assertf( region != NMR_GameplayOrigin, "You cannot manually add a NMR_GameplayOrigin region!" );
|
|
|
|
#if __BANK
|
|
char callingScriptName[64] = { 0 };
|
|
#endif
|
|
|
|
if( region == NMR_Script)
|
|
{
|
|
if(iThreadId == THREAD_INVALID)
|
|
{
|
|
Assertf(iThreadId != THREAD_INVALID, "Invalid scrThreadId");
|
|
return false;
|
|
}
|
|
|
|
#if __BANK
|
|
GtaThread * pScriptThread = GtaThread::GetThreadWithThreadId(iThreadId);
|
|
if(pScriptThread == NULL)
|
|
{
|
|
Assertf(pScriptThread != NULL, "NULL script thread");
|
|
return false;
|
|
}
|
|
|
|
const char * pScriptName = pScriptThread->GetScriptName();
|
|
if(pScriptName == NULL)
|
|
{
|
|
Assertf(pScriptName != NULL && *pScriptName != 0, "Script has no name");
|
|
return false;
|
|
}
|
|
|
|
strcpy(callingScriptName, pScriptName);
|
|
|
|
if(m_NavMeshRequiredRegions[NMR_Script].m_iThreadId != THREAD_INVALID && m_NavMeshRequiredRegions[NMR_Script].m_iThreadId != iThreadId)
|
|
{
|
|
Assertf(m_NavMeshRequiredRegions[NMR_Script].m_iThreadId == THREAD_INVALID, "NMR_Script region already in use by script \"%s\".\nScript \"%s\" is trying to add the NMR_Script navmesh region whilst the original script is still using it (the original script must call REMOVE_NAVMESH_REQUIRED_REGION first)",
|
|
m_NavMeshRequiredRegions[NMR_Script].m_ScriptName, callingScriptName);
|
|
return false;
|
|
}
|
|
|
|
Printf("Adding scripted navmesh region at (%.1f, %.1f) - script \"%s\"\n", fOriginX, fOriginY, pScriptName );
|
|
#endif // __BANK
|
|
|
|
}
|
|
else
|
|
{
|
|
Assertf( iThreadId==0, "Why is there a thread ID specified for a navmesh region in the NMR_NetworkRespawnMgr slot??" );
|
|
|
|
if(!Verifyf( m_NavMeshRequiredRegions[NMR_NetworkRespawnMgr].m_bActive==false, "NMR_NetworkRespawnMgr slot for navmesh required region is already in use. You have to call \"NETWORK_CANCEL_RESPAWN_SEARCH\" to cancel it.") )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_NavMeshRequiredRegions[region].m_vOrigin = Vector2(fOriginX, fOriginY);
|
|
m_NavMeshRequiredRegions[region].m_fNavMeshLoadRadius = fRadius;
|
|
m_NavMeshRequiredRegions[region].m_fHierarchicalNodesLoadRadius = 0.0f;
|
|
m_NavMeshRequiredRegions[region].m_iThreadId = iThreadId;
|
|
m_NavMeshRequiredRegions[region].m_bActive = true;
|
|
|
|
#if __BANK
|
|
if(iThreadId != THREAD_INVALID)
|
|
{
|
|
strcpy(m_NavMeshRequiredRegions[region].m_ScriptName, callingScriptName);
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
scrThreadId CPathServer::GetNavmeshMeshRegionScriptID()
|
|
{
|
|
if(m_NavMeshRequiredRegions[NMR_Script].m_bActive)
|
|
{
|
|
return m_NavMeshRequiredRegions[NMR_Script].m_iThreadId;
|
|
}
|
|
else
|
|
{
|
|
return THREAD_INVALID;
|
|
}
|
|
}
|
|
|
|
bool CPathServer::GetIsNavMeshRegionRequired( const NavMeshRequiredRegion region )
|
|
{
|
|
return m_NavMeshRequiredRegions[region].m_bActive;
|
|
}
|
|
|
|
bool CPathServer::RemoveNavMeshRegion( const NavMeshRequiredRegion region, const scrThreadId iThreadId )
|
|
{
|
|
Assertf( region != NMR_GameplayOrigin, "You cannot remove the NMR_GameplayOrigin region!");
|
|
|
|
if( region == NMR_NetworkRespawnMgr )
|
|
{
|
|
Assertf( m_NavMeshRequiredRegions[NMR_NetworkRespawnMgr].m_bActive, "Attempting to remove a navmesh region NMR_NetworkRespawnMgr which is not active.");
|
|
Assertf( iThreadId==0, "Why is there a thread ID specified for a navmesh region in the NMR_NetworkRespawnMgr slot??" );
|
|
|
|
Printf("Removing network respawn navmesh region at (%.1f, %.1f)\n",
|
|
m_NavMeshRequiredRegions[NMR_NetworkRespawnMgr].m_vOrigin.x, m_NavMeshRequiredRegions[NMR_NetworkRespawnMgr].m_vOrigin.y );
|
|
|
|
m_NavMeshRequiredRegions[NMR_NetworkRespawnMgr].m_bActive = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
else if( region == NMR_Script )
|
|
{
|
|
// Nothing to remove?
|
|
if(m_NavMeshRequiredRegions[NMR_Script].m_iThreadId == THREAD_INVALID)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(iThreadId == THREAD_INVALID)
|
|
{
|
|
Assertf(iThreadId != THREAD_INVALID, "Invalid scrThreadId");
|
|
return false;
|
|
}
|
|
|
|
#if __BANK
|
|
GtaThread * pScriptThread = GtaThread::GetThreadWithThreadId(iThreadId);
|
|
if(pScriptThread == NULL)
|
|
{
|
|
Assertf(pScriptThread != NULL, "NULL script thread");
|
|
return false;
|
|
}
|
|
|
|
const char * pScriptName = pScriptThread->GetScriptName();
|
|
if(pScriptName == NULL)
|
|
{
|
|
Assertf(pScriptName != NULL && *pScriptName != 0, "Script has no name");
|
|
return false;
|
|
}
|
|
/*
|
|
if(m_NavMeshRequiredRegions[NMR_Script].m_iThreadId != iThreadId)
|
|
{
|
|
// This assert removed at Neil & Will's request (url:bugstar:1207781)
|
|
Assertf(m_NavMeshRequiredRegions[NMR_Script].m_iThreadId == iThreadId, "NMR_Script region is being removed by a script which did not set it up. This will cause errors.\nThe script which originally called ADD_NAVMESH_REQUIRED_REGION is \"%s\", and the script which has just called REMOVE_NAVMESH_REQUIRED_REGION is \"%s\"",
|
|
m_NavMeshRequiredRegions[NMR_Script].m_ScriptName, pScriptName);
|
|
return false;
|
|
}
|
|
*/
|
|
Printf("Removing scripted navmesh region at (%.1f, %.1f) - script \"%s\"\n",
|
|
m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.x, m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.y, pScriptName );
|
|
#endif
|
|
|
|
m_NavMeshRequiredRegions[NMR_Script].m_bActive = false;
|
|
m_NavMeshRequiredRegions[NMR_Script].m_iThreadId = THREAD_INVALID;
|
|
|
|
#if __BANK
|
|
strcpy(m_NavMeshRequiredRegions[NMR_Script].m_ScriptName, "NMR_Script");
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Returns whether the navmeshes around the given area are loaded
|
|
bool CPathServer::AreAllNavMeshRegionsLoaded(aiNavDomain domain)
|
|
{
|
|
LOCK_NAVMESH_DATA;
|
|
LOCK_STORE_LOADED_MESHES;
|
|
|
|
const atArray<s32> & loadedMeshes = GetNavMeshStore(domain)->GetLoadedMeshes();
|
|
|
|
s32 i;
|
|
for(i=0; i<loadedMeshes.GetCount(); i++)
|
|
{
|
|
CNavMesh * pNavMesh = GetNavMeshFromIndex(loadedMeshes[i], domain);
|
|
|
|
if(!pNavMesh)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#ifndef GTA_ENGINE
|
|
UNLOCK_STORE_LOADED_MESHES;
|
|
UNLOCK_NAVMESH_DATA;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#endif // GTA_ENGINE
|
|
|
|
void CPathServer::RemoveAllNavMeshRegions()
|
|
{
|
|
for(s32 r=0; r<NAVMESH_MAX_REQUIRED_REGIONS; r++)
|
|
{
|
|
if(r != NMR_GameplayOrigin)
|
|
{
|
|
m_NavMeshRequiredRegions[r].m_bActive = false;
|
|
#ifdef GTA_ENGINE
|
|
m_NavMeshRequiredRegions[r].m_iThreadId = THREAD_INVALID;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if __BANK
|
|
strcpy(m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_ScriptName, "NMR_GameplayOrigin");
|
|
strcpy(m_NavMeshRequiredRegions[NMR_NetworkRespawnMgr].m_ScriptName, "NMR_NetworkRespawnMgr");
|
|
strcpy(m_NavMeshRequiredRegions[NMR_Script].m_ScriptName, "NMR_Script");
|
|
#endif
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
#if __SCRIPT_MEM_CALC
|
|
// TODO: Move this functionality into aiMeshStore
|
|
void CPathServer::CountScriptMemoryUsage(u32& nVirtualSize, u32& nPhysicalSize)
|
|
{
|
|
nVirtualSize = 0;
|
|
nPhysicalSize = 0;
|
|
|
|
if(!m_NavMeshRequiredRegions[NMR_Script].m_bActive)
|
|
return;
|
|
|
|
atArray<strIndex> streamingIndices;
|
|
aiNavMeshStore * pStore = GetNavMeshStore(kNavDomainRegular);
|
|
|
|
const float fMeshHalfSize = pStore->GetMeshSize() * 0.5f;
|
|
const float fMeshRadius = rage::Sqrtf((fMeshHalfSize*fMeshHalfSize)+(fMeshHalfSize*fMeshHalfSize));
|
|
|
|
const float fScriptLoadProximity = m_NavMeshRequiredRegions[NMR_Script].m_fNavMeshLoadRadius + fMeshRadius;
|
|
const Vector3 vScriptRegionMin(m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.x - fScriptLoadProximity, m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.y - fScriptLoadProximity, 0.0f);
|
|
const Vector3 vScriptRegionMax(m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.x + fScriptLoadProximity, m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.y + fScriptLoadProximity, 0.0f);
|
|
|
|
const float fGameplayLoadProximity = m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_fNavMeshLoadRadius + fMeshRadius;
|
|
|
|
float x,y;
|
|
Vector2 vCenter;
|
|
Vector2 vDiff;
|
|
|
|
for(y=vScriptRegionMin.y; y<vScriptRegionMax.y; y+=pStore->GetMeshSize())
|
|
{
|
|
for(x=vScriptRegionMin.x; x<vScriptRegionMax.x; x+=pStore->GetMeshSize())
|
|
{
|
|
const strLocalIndex iMeshIndex = strLocalIndex(pStore->GetMeshIndexFromPosition( Vector3(x,y,0.0f) ));
|
|
if(iMeshIndex != pStore->GetMeshIndexNone())
|
|
{
|
|
const int iY = iMeshIndex.Get() / pStore->GetNumMeshesInX();
|
|
const int iX = iMeshIndex.Get() - (iY * pStore->GetNumMeshesInY());
|
|
|
|
vCenter.x = ((((float)iX) * pStore->GetMeshSize()) + CPathServerExtents::m_vWorldMin.x) + fMeshHalfSize;
|
|
vCenter.y = ((((float)iY) * pStore->GetMeshSize()) + CPathServerExtents::m_vWorldMin.y) + fMeshHalfSize;
|
|
|
|
vDiff.x = vCenter.x - m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.x;
|
|
vDiff.y = vCenter.y - m_NavMeshRequiredRegions[NMR_Script].m_vOrigin.y;
|
|
|
|
const float fDistSqrFromScriptOrigin = vDiff.Mag2();
|
|
|
|
if(fDistSqrFromScriptOrigin < fScriptLoadProximity*fScriptLoadProximity)
|
|
{
|
|
vDiff.x = vCenter.x - m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_vOrigin.x;
|
|
vDiff.y = vCenter.y - m_NavMeshRequiredRegions[NMR_GameplayOrigin].m_vOrigin.y;
|
|
|
|
if(vDiff.Mag2() > fGameplayLoadProximity*fGameplayLoadProximity)
|
|
{
|
|
streamingIndices.Grow() = pStore->GetStreamingIndex(iMeshIndex).Get();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
strStreamingEngine::GetInfo().GetObjectAndDependenciesSizes(streamingIndices, nVirtualSize, nPhysicalSize, NULL, 0, true);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef GTA_ENGINE
|
|
bool
|
|
CPathServer::LoadAllHierarchicalData(const char * UNUSED_PARAM(pPathForHierarchicalData))
|
|
{
|
|
/*
|
|
const s32 iSectorStep = m_iNumSectorsPerNavMesh;
|
|
const s32 iBlockSize = m_iNumSectorsPerNavNodes;
|
|
const s32 iStepSize = iSectorStep * iBlockSize;
|
|
s32 x, y;
|
|
|
|
for(y=0; y<WORLD_DEPTHINSECTORS; y+=iStepSize)
|
|
{
|
|
for(x=0; x<WORLD_WIDTHINSECTORS; x+=iStepSize)
|
|
{
|
|
const u32 index = GetNavMeshIndexFromSector(x, y);
|
|
|
|
if(index != NAVMESH_NAVMESH_INDEX_NONE)
|
|
{
|
|
Assert(!m_StaticNavMeshStore.GetHierarchicalNodes(index));
|
|
|
|
char fileName[256];
|
|
sprintf(fileName, "%s/hier_nav/%i_%i.ihn", pPathForHierarchicalData, x, y);
|
|
CHierarchicalNavData * pHierNav = CHierarchicalNavData::Load(fileName);
|
|
if(pHierNav)
|
|
{
|
|
m_StaticNavMeshStore.SetHierarchicalNodes(index, pHierNav);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
//****************************************************************************
|
|
// LoadMeshes
|
|
// This function loads in ALL the CNavMeshes. This could be a lot of data.
|
|
//****************************************************************************
|
|
#ifndef GTA_ENGINE
|
|
bool
|
|
CPathServer::LoadAllMeshes(const char * pPathForNavMeshes)
|
|
{
|
|
const aiNavDomain domain = kNavDomainRegular;
|
|
|
|
int x,y;
|
|
int iSectorStep = m_pNavMeshStores[domain]->GetNumSectorsPerMesh();
|
|
|
|
u32 iTotalNumVerticesInWorld = 0;
|
|
u32 iTotalNumPolysInWorld = 0;
|
|
u32 iTotalNumCoverPointsInWorld = 0;
|
|
|
|
u32 iGreatestNumCoverPointsInAnyNavMesh = 0;
|
|
|
|
|
|
char sectorName[256];
|
|
char sectorFilename[256];
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
char hierNavName[256];
|
|
#endif
|
|
for(y=0; y<CPathServerExtents::GetWorldDepthInSectors(); y+=iSectorStep)
|
|
{
|
|
for(x=0; x<CPathServerExtents::GetWorldWidthInSectors(); x+=iSectorStep)
|
|
{
|
|
const u32 index = GetNavMeshIndexFromSector(x, y, domain);
|
|
Assert(!m_pNavMeshStores[domain]->GetMeshByIndex(index));
|
|
|
|
sprintf(sectorName, "navmesh[%i][%i].inv", x, y);
|
|
sprintf(sectorFilename, "%s\\navmesh[%i][%i].inv", pPathForNavMeshes, x, y);
|
|
|
|
CNavMesh * pNavMesh = CNavMesh::LoadBinary(sectorFilename);
|
|
if(!pNavMesh)
|
|
continue;
|
|
|
|
if(pNavMesh->GetIndexOfMesh()!=index)
|
|
{
|
|
pNavMesh->SetIndexOfMesh(index);
|
|
for(u32 p=0; p<pNavMesh->GetNumPolys(); p++)
|
|
pNavMesh->GetPoly(p)->SetNavMeshIndex(index);
|
|
|
|
for(u32 a=0; a<pNavMesh->GetSizeOfPools(); a++)
|
|
{
|
|
pNavMesh->GetAdjacentPolysArray().Get(a)->SetNavMeshIndex(index, pNavMesh->GetAdjacentMeshes());
|
|
pNavMesh->GetAdjacentPolysArray().Get(a)->SetOriginalNavMeshIndex(index, pNavMesh->GetAdjacentMeshes());
|
|
}
|
|
}
|
|
|
|
#if __ENSURE_THAT_POLY_QUICK_CENTROIDS_ARE_REALLY_WITHIN_POLYS
|
|
pNavMesh->CheckAllQuickPolyCentroidsAreWithinPolys();
|
|
#endif
|
|
|
|
m_pNavMeshStores[domain]->Set(index, pNavMesh);
|
|
|
|
// total up
|
|
iTotalNumVerticesInWorld += pNavMesh->GetNumVertices();
|
|
iTotalNumPolysInWorld += pNavMesh->GetNumPolys();
|
|
iTotalNumCoverPointsInWorld += pNavMesh->GetNumCoverPoints();
|
|
|
|
if(pNavMesh->GetNumCoverPoints() > iGreatestNumCoverPointsInAnyNavMesh)
|
|
iGreatestNumCoverPointsInAnyNavMesh = pNavMesh->GetNumCoverPoints();
|
|
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
// Load hierarchical navigation data
|
|
if(ms_bLoadAllHierarchicalData)
|
|
{
|
|
sprintf(hierNavName, "%s\\%i_%i.ihn", pPathForNavMeshes, x, y);
|
|
CHierarchicalNavData * pHierNav = CHierarchicalNavData::Load(hierNavName);
|
|
|
|
if(pHierNav)
|
|
{
|
|
s32 iNodexIndes = m_pNavNodesStores[domain]->GetMeshIndexFromSectorCoords(x, y);
|
|
m_pNavNodesStore->Set(iNodexIndes, pHierNav);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if __DEV
|
|
#if __WIN32PC
|
|
|
|
char tmp[256];
|
|
Printf("*****************************************************************\n");
|
|
Printf("*****************************************************************\n");
|
|
|
|
sprintf(tmp, "Total num vertices in all navmeshes : %i\n", iTotalNumVerticesInWorld);
|
|
Printf(tmp);
|
|
OutputDebugString(tmp);
|
|
sprintf(tmp, "Total num polys in all navmeshes : %i\n", iTotalNumPolysInWorld);
|
|
Printf(tmp);
|
|
OutputDebugString(tmp);
|
|
sprintf(tmp, "Total num cover-points in all navmeshes : %i\n", iTotalNumCoverPointsInWorld);
|
|
Printf(tmp);
|
|
OutputDebugString(tmp);
|
|
sprintf(tmp, "Greatest num coverpoints in any navmesh : %i\n(max is 8191 stored in 13 bits in TLinkedCoverPoint)\n", iGreatestNumCoverPointsInAnyNavMesh);
|
|
Printf(tmp);
|
|
OutputDebugString(tmp);
|
|
|
|
Printf("*****************************************************************\n");
|
|
Printf("*****************************************************************\n");
|
|
|
|
#endif
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
#endif // #ifndef GTA_ENGINE
|
|
|
|
//******************************************************************************************
|
|
// UnloadAllMeshes
|
|
// Removes & deletes all the navmeshes
|
|
//******************************************************************************************
|
|
|
|
bool CPathServer::UnloadAllMeshes(bool bForceRemove)
|
|
{
|
|
#if NAVMESH_EXPORT
|
|
if(CNavMeshDataExporter::WillExportCollision())
|
|
return true;
|
|
#endif
|
|
|
|
LOCK_NAVMESH_DATA;
|
|
|
|
s32 i;
|
|
|
|
for(int meshDataSet = 0; meshDataSet < kNumNavDomains; meshDataSet++)
|
|
{
|
|
if(!fwPathServer::GetIsNavDomainEnabled((aiNavDomain)meshDataSet))
|
|
continue;
|
|
|
|
aiNavMeshStore* pStore = m_pNavMeshStores[meshDataSet];
|
|
|
|
// Main-map navmeshes
|
|
for(i=0; i<pStore->GetMaxMeshIndex(); i++)
|
|
{
|
|
if(pStore->GetMeshByIndex(i))
|
|
{
|
|
pStore->SetIsMeshRequired(i, false);
|
|
|
|
// if(bForceRemove)
|
|
// {
|
|
// CStreaming::RemoveObject( pStore->GetStreamingIndex(i), pStore->GetStreamingModuleId() );
|
|
// }
|
|
}
|
|
|
|
pStore->GetStreamingModule()->ClearRequiredFlag(i, STRFLAG_DONTDELETE);
|
|
|
|
if(bForceRemove)
|
|
{
|
|
CStreaming::RemoveObject( pStore->GetStreamingIndex(strLocalIndex(i)), pStore->GetStreamingModuleId() );
|
|
pStore->GetStreamingModule()->ResetAllRefs( pStore->GetStreamingIndex(strLocalIndex(i)) );
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK_NAVMESH_DATA;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//******************************************************************************************
|
|
// Shutdown
|
|
// This function shuts down CPathServer. In turn it shuts down the CPathServerThread -
|
|
// this may not be instant : it sets a flag within the class, which is examined between
|
|
// each path request. Therefore it may have to wait for pending request to complete
|
|
// before terminating.
|
|
//******************************************************************************************
|
|
void
|
|
CPathServer::Shutdown(void)
|
|
{
|
|
if(ms_bInitialised)
|
|
{
|
|
m_PathServerThread.Close();
|
|
|
|
UnloadAllMeshes(true);
|
|
|
|
// The main store of dynamic navmeshes for the streaming
|
|
m_DynamicNavMeshStore.Shutdown();
|
|
|
|
CDynamicObjectsContainer::Shutdown();
|
|
|
|
if(m_pTessellationNavMesh)
|
|
{
|
|
delete m_pTessellationNavMesh;
|
|
m_pTessellationNavMesh = NULL;
|
|
}
|
|
if(m_PolysTessellatedFrom)
|
|
{
|
|
delete[] m_PolysTessellatedFrom;
|
|
m_PolysTessellatedFrom = NULL;
|
|
}
|
|
|
|
for(int meshDataSet = 0; meshDataSet < kNumNavDomains; meshDataSet++)
|
|
{
|
|
if(fwPathServer::GetIsNavDomainEnabled((aiNavDomain)meshDataSet))
|
|
m_pNavMeshStores[meshDataSet]->Shutdown();
|
|
}
|
|
|
|
|
|
#if !__FINAL
|
|
if(m_RequestTimer) {
|
|
delete m_RequestTimer;
|
|
m_RequestTimer = NULL;
|
|
}
|
|
if(m_PedGenTimer) {
|
|
delete m_PedGenTimer;
|
|
m_PedGenTimer = NULL;
|
|
}
|
|
if(m_NavMeshLoadingTimer){
|
|
delete m_NavMeshLoadingTimer;
|
|
m_NavMeshLoadingTimer = NULL;
|
|
}
|
|
if(m_NavMesh2ndLoadingTimer){
|
|
delete m_NavMesh2ndLoadingTimer;
|
|
m_NavMesh2ndLoadingTimer = NULL;
|
|
}
|
|
if(m_MainGameThreadStallTimer){
|
|
delete m_MainGameThreadStallTimer;
|
|
m_MainGameThreadStallTimer = NULL;
|
|
}
|
|
if(m_MiscTimer){
|
|
delete m_MiscTimer;
|
|
m_MiscTimer = NULL;
|
|
}
|
|
if(m_ImmediateModeTimer){
|
|
delete m_ImmediateModeTimer;
|
|
m_ImmediateModeTimer = NULL;
|
|
}
|
|
#ifdef GTA_ENGINE
|
|
if(m_ExtractCoverPointsTimer) {
|
|
delete m_ExtractCoverPointsTimer;
|
|
m_ExtractCoverPointsTimer = NULL;
|
|
}
|
|
if(m_TrackObjectsTimer) {
|
|
delete m_TrackObjectsTimer;
|
|
m_TrackObjectsTimer = NULL;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
if(m_pImmediateModePrioriryQueue)
|
|
{
|
|
delete m_pImmediateModePrioriryQueue;
|
|
m_pImmediateModePrioriryQueue = NULL;
|
|
}
|
|
|
|
m_GameInterface = NULL;
|
|
}
|
|
|
|
|
|
ms_bInitialised = false;
|
|
}
|
|
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
u32 CPathServer::NavMeshIndexToNavNodesIndex(const TNavMeshIndex iNavMesh)
|
|
{
|
|
Assert(iNavMesh != NAVMESH_NAVMESH_INDEX_NONE);
|
|
|
|
s32 iSectorX, iSectorY;
|
|
|
|
m_pNavMeshStore->GetSectorCoordsFromMeshIndex(iNavMesh, iSectorX, iSectorY);
|
|
const u32 iNodesIndex = m_pNavNodesStore->GetMeshIndexFromSectorCoords(iSectorX, iSectorY);
|
|
|
|
return iNodesIndex;
|
|
}
|
|
#endif
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
u32 CPathServer::NavNodesIndexToNavMeshIndex(const TNavMeshIndex iNavNodes)
|
|
{
|
|
Assert(iNavNodes != NAVMESH_NODE_INDEX_NONE);
|
|
|
|
s32 iSectorX, iSectorY;
|
|
|
|
m_pNavNodesStore->GetSectorCoordsFromMeshIndex(iNavNodes, iSectorX, iSectorY);
|
|
const u32 iNavIndex = m_pNavMeshStore->GetMeshIndexFromSectorCoords(iSectorX, iSectorY);
|
|
|
|
return iNavIndex;
|
|
}
|
|
#endif
|
|
bool CPathServer::GetDoNavMeshesAdjoin(const u32 iNavMesh1, const u32 iNavMesh2, aiNavDomain domain)
|
|
{
|
|
const int iNumSectorsPerNavMesh = m_pNavMeshStores[domain]->GetNumSectorsPerMesh();
|
|
|
|
s32 iX1, iY1, iX2, iY2;
|
|
GetSectorFromNavMeshIndex(iNavMesh1, iX1, iY1, domain);
|
|
GetSectorFromNavMeshIndex(iNavMesh2, iX2, iY2, domain);
|
|
|
|
if(iX1==iX2 && (iY1==iY2-iNumSectorsPerNavMesh || iY1==iY2+iNumSectorsPerNavMesh))
|
|
return true;
|
|
if(iY1==iY2 && (iX1==iX2-iNumSectorsPerNavMesh || iX1==iX2+iNumSectorsPerNavMesh))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// GetNavMeshIndexFromPosition
|
|
// Given coordinates in worldspace, this function returns the index of the
|
|
// navmesh at that location. This indexes into the navmeshes array.
|
|
//****************************************************************************
|
|
u32
|
|
CPathServer::GetNavMeshIndexFromPosition(const Vector3 & vPos, aiNavDomain domain)
|
|
{
|
|
// int iSectorX = WORLD_WORLDTOSECTORX(vPos.x);
|
|
// int iSectorY = WORLD_WORLDTOSECTORX(vPos.y);
|
|
const int iSectorX = CPathServerExtents::GetWorldToSectorX(vPos.x);
|
|
const int iSectorY = CPathServerExtents::GetWorldToSectorY(vPos.y);
|
|
|
|
return GetNavMeshIndexFromSector(iSectorX, iSectorY, domain);
|
|
}
|
|
|
|
CHierarchicalNavData * CPathServer::GetHierarchicalNavFromNavMeshIndex(const u32 UNUSED_PARAM(index))
|
|
{
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
if(index <= NAVMESH_MAX_MAP_INDEX)
|
|
{
|
|
const s32 iHierIndex = NavMeshIndexToNavNodesIndex(index);
|
|
return m_pNavNodesStore->GetMeshByIndex(iHierIndex);
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
u32
|
|
CPathServer::GetAdjacentNavMeshIndex(u32 iCurrentNavMeshIndex, eNavMeshEdge iDir, aiNavDomain domain)
|
|
{
|
|
aiNavMeshStore* pStore = m_pNavMeshStores[domain];
|
|
|
|
s32 iY = iCurrentNavMeshIndex / pStore->GetNumMeshesInX();
|
|
s32 iX = iCurrentNavMeshIndex - (iY * pStore->GetNumMeshesInY());
|
|
|
|
s32 iAdjIndex;
|
|
|
|
switch(iDir)
|
|
{
|
|
case eNegX:
|
|
iAdjIndex = (iX > 0) ? iCurrentNavMeshIndex - 1 : NAVMESH_NAVMESH_INDEX_NONE;
|
|
break;
|
|
case ePosX:
|
|
iAdjIndex = (iX < pStore->GetNumMeshesInX()-1) ? iCurrentNavMeshIndex + 1 : NAVMESH_NAVMESH_INDEX_NONE;
|
|
break;
|
|
case eNegY:
|
|
iAdjIndex = (iY > 0) ? iCurrentNavMeshIndex - pStore->GetNumMeshesInY() : NAVMESH_NAVMESH_INDEX_NONE;
|
|
break;
|
|
case ePosY:
|
|
iAdjIndex = (iY < pStore->GetNumMeshesInY()-1) ? iCurrentNavMeshIndex + pStore->GetNumMeshesInY() : NAVMESH_NAVMESH_INDEX_NONE;
|
|
break;
|
|
default:
|
|
Assert(0);
|
|
iAdjIndex = NAVMESH_NAVMESH_INDEX_NONE;
|
|
}
|
|
|
|
return iAdjIndex;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// Finds up to 4 navmeshes intersecting the give position & radius.
|
|
// The radius MUST therefore be less than 1/2 a navmesh width, or we may
|
|
// intersect more than 4 navmeshes.
|
|
// pIndices = an array of 4 TNavMeshIndex's
|
|
//****************************************************************************
|
|
s32
|
|
CPathServer::GetNavMeshesIntersectingPosition(const Vector3 & vPos, const float fRadius, TNavMeshIndex * pIndices, aiNavDomain domain)
|
|
{
|
|
static const Vector2 vOffsets[9] =
|
|
{
|
|
Vector2(0,0),
|
|
Vector2(-1.0f,0),
|
|
Vector2(0,1.0f),
|
|
Vector2(1.0f,0),
|
|
Vector2(0,-1.0f),
|
|
Vector2(0.70710f,0.70710f),
|
|
Vector2(0.70710f,-0.70710f),
|
|
Vector2(-0.70710f,-0.70710f),
|
|
Vector2(-0.70710f,0.70710f)
|
|
};
|
|
s32 iNumIndices=0;
|
|
Vector3 vTestPos;
|
|
vTestPos.z = vPos.z;
|
|
s32 i,j;
|
|
for(i=0; i<9; i++)
|
|
{
|
|
vTestPos.x = vPos.x + (vOffsets[i].x * fRadius);
|
|
vTestPos.y = vPos.y + (vOffsets[i].y * fRadius);
|
|
u32 iNavMeshIndex = CPathServer::GetNavMeshIndexFromPosition(vTestPos, domain);
|
|
for(j=0; j<iNumIndices; j++)
|
|
if(pIndices[j] == iNavMeshIndex) break;
|
|
if(j==iNumIndices)
|
|
pIndices[iNumIndices++] = iNavMeshIndex;
|
|
Assert(iNumIndices<=4);
|
|
}
|
|
Assert(iNumIndices<=4);
|
|
return iNumIndices;
|
|
}
|
|
|
|
CNavMesh * CPathServer::FindClosestLoadedNavMeshToPosition(const Vector3 & vPos, aiNavDomain domain)
|
|
{
|
|
float fClosestDistSqr = FLT_MAX;
|
|
CNavMesh * pClosestNavMesh = NULL;
|
|
|
|
#ifdef GTA_ENGINE
|
|
LOCK_STORE_LOADED_MESHES;
|
|
#endif
|
|
|
|
const atArray<s32> & loadedMeshes = GetNavMeshStore((aiNavDomain)domain)->GetLoadedMeshes();
|
|
|
|
for(int i=0; i<loadedMeshes.GetCount(); i++)
|
|
{
|
|
const TNavMeshIndex iNavMesh = loadedMeshes[i];
|
|
CNavMesh * pNavMesh = GetNavMeshStore((aiNavDomain)domain)->GetMeshByIndex(iNavMesh);
|
|
if(pNavMesh)
|
|
{
|
|
Assert(pNavMesh->GetQuadTree());
|
|
|
|
Vector3 vMid = (pNavMesh->GetQuadTree()->m_Mins + pNavMesh->GetQuadTree()->m_Maxs) * 0.5f;
|
|
vMid.z = vPos.z;
|
|
Vector3 vDiff = vMid - vPos;
|
|
float fDistSqr = vDiff.Mag2();
|
|
|
|
if(fDistSqr < fClosestDistSqr)
|
|
{
|
|
fClosestDistSqr = fDistSqr;
|
|
pClosestNavMesh = pNavMesh;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pClosestNavMesh;
|
|
}
|
|
|
|
#if 0 // Disabled, appears to be unused at the moment. /FF
|
|
|
|
void
|
|
CPathServer::UpdateDynamicNavMeshMatrix(CDynamicNavMeshEntry & entry)
|
|
{
|
|
if(entry.m_pNavMesh && !entry.m_bCurrentlyCopyingMatrix)
|
|
{
|
|
Assert(entry.GetEntity());
|
|
if(!entry.GetEntity())
|
|
return;
|
|
|
|
entry.m_bCurrentlyCopyingMatrix = true;
|
|
|
|
entry.m_pNavMesh->GetMatrixRef() = entry.m_Matrix;
|
|
const Vector3 & vEntityPos = entry.m_Matrix.d;
|
|
|
|
CEntityBoundAI bound(
|
|
*entry.GetEntity(),
|
|
vEntityPos.z,
|
|
PATHSERVER_PED_RADIUS,
|
|
true
|
|
);
|
|
bound.GetPlanes(entry.m_vBoundingPlanes, entry.m_fBoundingPlaneDists);
|
|
|
|
#ifdef GTA_ENGINE
|
|
float fPushThroughGroundAmt = entry.GetEntity()->GetIsTypeVehicle() ? 0.5f : 0.25f;
|
|
float fTopZ = bound.GetTopZ() + 4.0f; // Add an extra amount onto top for dynamic navmeshes
|
|
float fBottomZ = bound.GetBottomZ() - fPushThroughGroundAmt;
|
|
#else
|
|
float fTopZ = vEntityPos.z + 1.0f;
|
|
float fBottomZ = vEntityPos.z - 1.0f;
|
|
#endif
|
|
|
|
// Construct the top & bottom planes for this object, using world UP & DOWN as normal vectors
|
|
entry.m_vBoundingPlanes[4] = Vector3(0.0f, 0.0f, 1.0f);
|
|
entry.m_fBoundingPlaneDists[4] = - DotProduct(entry.m_vBoundingPlanes[4], Vector3(vEntityPos.x, vEntityPos.y, fTopZ));
|
|
entry.m_vBoundingPlanes[5] = Vector3(0.0f, 0.0f, -1.0f);
|
|
entry.m_fBoundingPlaneDists[5] = - DotProduct(entry.m_vBoundingPlanes[5], Vector3(vEntityPos.x, vEntityPos.y, fBottomZ));
|
|
|
|
entry.m_bCurrentlyCopyingMatrix = false;
|
|
}
|
|
}
|
|
|
|
#endif // 0
|
|
|
|
// Simplified interface for basic paths
|
|
TPathHandle
|
|
CPathServer::RequestPath(const Vector3 & vPathStart, const Vector3 & vPathEnd, u64 iPathStyleFlags, float fCompletionRadius, fwEntity * pPed)
|
|
{
|
|
TRequestPathStruct reqStruct;
|
|
reqStruct.m_vPathStart = vPathStart;
|
|
reqStruct.m_vPathEnd = vPathEnd;
|
|
reqStruct.m_iFlags = iPathStyleFlags;
|
|
reqStruct.m_fCompletionRadius = fCompletionRadius;
|
|
reqStruct.m_pPed = pPed;
|
|
|
|
return RequestPath(reqStruct);
|
|
}
|
|
|
|
//*********************************************************************
|
|
// RequestPath
|
|
// A general-purpose path request function, which is called by the
|
|
// more specific request functions.
|
|
// The 'iFlags' field is a bitset which specifies which type of path
|
|
// is being requested, and the "vPathStart,vPathEnd,vReferenceVector &
|
|
// fRederenceDistance" variables are interpreted differently depending
|
|
// upon the path-type.
|
|
// The influence spheres provide a method for steering a path away-
|
|
// from or towards areas of the map, whilst still maintaining the
|
|
// overall path objetives.
|
|
// pEntityPathIsOn is passed in if this path is known to exist upon
|
|
// a dynamic navmesh attached to an entity.
|
|
//*********************************************************************
|
|
TPathHandle
|
|
CPathServer::RequestPath(
|
|
const Vector3 & vPathStart,
|
|
const Vector3 & vPathEnd,
|
|
const Vector3 & vReferenceVector,
|
|
float fReferenceDistance,
|
|
u64 iFlags,
|
|
float fCompletionRadius,
|
|
u32 iNumInfluenceSpheres,
|
|
TInfluenceSphere * pInfluenceSpheres,
|
|
fwEntity * pPed,
|
|
const fwEntity * pEntityPathIsOn)
|
|
{
|
|
TRequestPathStruct reqStruct;
|
|
reqStruct.m_vPathStart = vPathStart;
|
|
reqStruct.m_vPathEnd = vPathEnd;
|
|
reqStruct.m_vReferenceVector = vReferenceVector;
|
|
reqStruct.m_fReferenceDistance = fReferenceDistance;
|
|
reqStruct.m_iFlags = iFlags;
|
|
reqStruct.m_fCompletionRadius = fCompletionRadius;
|
|
reqStruct.m_iNumInfluenceSpheres = iNumInfluenceSpheres;
|
|
sysMemCpy(reqStruct.m_InfluenceSpheres, pInfluenceSpheres, sizeof(TInfluenceSphere)*iNumInfluenceSpheres);
|
|
reqStruct.m_pPed = pPed;
|
|
reqStruct.m_pEntityPathIsOn = pEntityPathIsOn;
|
|
|
|
return RequestPath(reqStruct);
|
|
}
|
|
|
|
TPathHandle CPathServer::RequestPath(const TRequestPathStruct & reqStruct)
|
|
{
|
|
Assert(ms_bGameInSession);
|
|
if(!ms_bGameInSession)
|
|
return PATH_HANDLE_NULL;
|
|
|
|
#ifdef GTA_ENGINE
|
|
#if __DEV
|
|
const bool bStartPosIsValid = rage::FPIsFinite(reqStruct.m_vPathStart.x) && rage::FPIsFinite(reqStruct.m_vPathStart.y) && rage::FPIsFinite(reqStruct.m_vPathStart.z);
|
|
const bool bEndPosIsValid = rage::FPIsFinite(reqStruct.m_vPathEnd.x) && rage::FPIsFinite(reqStruct.m_vPathEnd.y) && rage::FPIsFinite(reqStruct.m_vPathEnd.z);
|
|
// A quick sanity check on the path request endpoints.
|
|
Assert(bStartPosIsValid && bEndPosIsValid);
|
|
Assert(rage::FPIsFinite(reqStruct.m_vReferenceVector.x) && rage::FPIsFinite(reqStruct.m_vReferenceVector.y) && rage::FPIsFinite(reqStruct.m_vReferenceVector.z));
|
|
Assert(rage::FPIsFinite(reqStruct.m_fReferenceDistance));
|
|
#endif
|
|
#endif
|
|
|
|
#if !__FINAL
|
|
if(m_RequestTimer)
|
|
{
|
|
m_RequestTimer->Reset();
|
|
m_RequestTimer->Start();
|
|
}
|
|
#endif
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextPathIndexToStartFrom;
|
|
|
|
m_iNextPathIndexToStartFrom++;
|
|
if(m_iNextPathIndexToStartFrom >= MAX_NUM_PATH_REQUESTS)
|
|
m_iNextPathIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_PATH_REQUESTS;
|
|
|
|
while(c)
|
|
{
|
|
if( m_PathRequests[i].IsReadyForUse() )
|
|
{
|
|
m_PathRequests[i].Clear();
|
|
#ifdef GTA_ENGINE
|
|
m_PathRequests[i].m_PedWaitingForThisRequest = reqStruct.m_pPed;
|
|
#endif
|
|
m_PathRequests[i].m_NavDomain = reqStruct.m_NavDomain;
|
|
m_PathRequests[i].m_PathResultInfo.Clear();
|
|
m_PathRequests[i].m_iType = EPath;
|
|
m_PathRequests[i].m_hHandle = m_iNextHandle++;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_PathRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
m_PathRequests[i].m_bSlotEmpty = false;
|
|
m_PathRequests[i].m_bComplete = false;
|
|
m_PathRequests[i].m_bRequestActive = false;
|
|
m_PathRequests[i].m_iNumPoints = 0;
|
|
|
|
m_PathRequests[i].m_vUnadjustedStartPoint = reqStruct.m_vPathStart;
|
|
m_PathRequests[i].m_vUnadjustedEndPoint = reqStruct.m_vPathEnd;
|
|
|
|
m_PathRequests[i].m_vPathStart = reqStruct.m_vPathStart;
|
|
m_PathRequests[i].m_vPathEnd = reqStruct.m_vPathEnd;
|
|
m_PathRequests[i].m_vPolySearchDir = reqStruct.m_vPolySearchDir;
|
|
|
|
m_PathRequests[i].m_vReferenceVector = reqStruct.m_vReferenceVector;
|
|
m_PathRequests[i].m_fReferenceDistance = reqStruct.m_fReferenceDistance;
|
|
m_PathRequests[i].m_fInitialReferenceDistance = reqStruct.m_fReferenceDistance;
|
|
|
|
Assert(!(reqStruct.m_StartNavmeshAndPoly.m_iNavMeshIndex==NAVMESH_NAVMESH_INDEX_NONE && reqStruct.m_StartNavmeshAndPoly.m_iPolyIndex!=NAVMESH_POLY_INDEX_NONE));
|
|
Assert(!(reqStruct.m_StartNavmeshAndPoly.m_iNavMeshIndex!=NAVMESH_NAVMESH_INDEX_NONE && reqStruct.m_StartNavmeshAndPoly.m_iPolyIndex==NAVMESH_POLY_INDEX_NONE));
|
|
|
|
m_PathRequests[i].m_StartNavmeshAndPoly = reqStruct.m_StartNavmeshAndPoly;
|
|
#ifdef GTA_ENGINE
|
|
m_PathRequests[i].m_EntityEndPosition = reqStruct.m_pPositionEntity;
|
|
#endif
|
|
m_PathRequests[i].m_bScriptedRoute = ((reqStruct.m_iFlags & PATH_FLAG_SCRIPTED_ROUTE)!=0);
|
|
m_PathRequests[i].m_bHighPrioRoute = ((reqStruct.m_iFlags & PATH_FLAG_HIGH_PRIO_ROUTE)!=0);
|
|
m_PathRequests[i].m_bNeverLeaveWater = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_LEAVE_WATER)!=0);
|
|
m_PathRequests[i].m_bNeverLeaveDeepWater = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_LEAVE_DEEP_WATER)!=0);
|
|
m_PathRequests[i].m_bEnsureLosBeforeEnding = ((reqStruct.m_iFlags & PATH_FLAG_ENSURE_LOS_BEFORE_ENDING)!=0);
|
|
m_PathRequests[i].m_bExpandStartEndPolyTessellationRadius = ((reqStruct.m_iFlags & PATH_FLAG_EXPAND_START_END_TESSELLATION_RADIUS)!=0);
|
|
m_PathRequests[i].m_bKeepUpdatingPedStartPosition = ((reqStruct.m_iFlags & PATH_FLAG_KEEP_UPDATING_PED_START_POSITION)!=0);
|
|
m_PathRequests[i].m_bPullFromEdgeExtra = ((reqStruct.m_iFlags & PATH_FLAG_PULL_FROM_EDGE_EXTRA)!=0);
|
|
m_PathRequests[i].m_bSofterFleeHeuristics = ((reqStruct.m_iFlags & PATH_FLAG_SOFTER_FLEE_HEURISTICS)!=0);
|
|
m_PathRequests[i].m_bAvoidTrainTracks = ((reqStruct.m_iFlags & PATH_FLAG_AVOID_TRAIN_TRACKS)!=0);
|
|
|
|
m_PathRequests[i].m_fDistAheadOfPed = reqStruct.m_fDistAheadOfPed;
|
|
|
|
if((reqStruct.m_iFlags & PATH_FLAG_FLEE_TARGET)!=0)
|
|
{
|
|
m_PathRequests[i].m_bFleeTarget = true;
|
|
}
|
|
else
|
|
{
|
|
m_PathRequests[i].m_bFleeTarget = false;
|
|
|
|
Assertf(!m_PathRequests[i].m_bAvoidTrainTracks, "NB: The heuristics for PATH_FLAG_AVOID_TRAIN_TRACKS are currently only tuned for fleeing peds.");
|
|
}
|
|
|
|
if((reqStruct.m_iFlags & PATH_FLAG_WANDER)!=0)
|
|
{
|
|
m_PathRequests[i].m_bWander = true;
|
|
m_PathRequests[i].m_vReferenceVector.z = 0.0f; // Reference vector is a 2d unit vector here
|
|
m_PathRequests[i].m_vReferenceVector.Normalize();
|
|
}
|
|
else
|
|
{
|
|
m_PathRequests[i].m_bWander = false;
|
|
}
|
|
|
|
m_PathRequests[i].m_vCoverOrigin = reqStruct.m_vCoverOrigin;
|
|
|
|
// Allow the limit on path search extents to be overridden (with care!)
|
|
m_PathRequests[i].m_bUseLargerSearchExtents = ((reqStruct.m_iFlags & PATH_FLAG_USE_LARGER_SEARCH_EXTENTS)!=0);
|
|
m_PathRequests[i].m_bDontLimitSearchExtents = ((reqStruct.m_iFlags & PATH_FLAG_DONT_LIMIT_SEARCH_EXTENTS)!=0);
|
|
|
|
#if !__FINAL
|
|
m_PathRequests[i].m_bClimbObjects = ms_bAllowObjectClimbing;
|
|
m_PathRequests[i].m_bPushObjects = ms_bAllowObjectPushing;
|
|
m_PathRequests[i].m_iFrameRequestCompleted = 0;
|
|
#ifdef GTA_ENGINE
|
|
m_PathRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
#endif
|
|
#endif
|
|
|
|
// Can't flee & wander at the same time..
|
|
Assert(!(m_PathRequests[i].m_bFleeTarget && m_PathRequests[i].m_bWander));
|
|
|
|
m_PathRequests[i].m_bUseBestAlternateRouteIfNoneFound = ((reqStruct.m_iFlags & PATH_FLAG_USE_BEST_ALTERNATE_ROUTE_IF_NONE_FOUND)!=0);
|
|
m_PathRequests[i].m_bPreferDownHill = ((reqStruct.m_iFlags & PATH_FLAG_PREFER_DOWNHILL)!=0);
|
|
m_PathRequests[i].m_bPreferPavements = ((reqStruct.m_iFlags & PATH_FLAG_PREFER_PAVEMENTS)!=0);
|
|
m_PathRequests[i].m_bNeverLeavePavements = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_LEAVE_PAVEMENTS)!=0);
|
|
m_PathRequests[i].m_bNeverClimbOverStuff = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_CLIMB_OVER_STUFF)!=0);
|
|
m_PathRequests[i].m_bNeverDropFromHeight = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_DROP_FROM_HEIGHT)!=0);
|
|
m_PathRequests[i].m_bMayUseFatalDrops = ((reqStruct.m_iFlags & PATH_FLAG_MAY_USE_FATAL_DROPS)!=0);
|
|
m_PathRequests[i].m_bNeverUseLadders = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_USE_LADDERS)!=0);
|
|
m_PathRequests[i].m_bDontAvoidDynamicObjects = ((reqStruct.m_iFlags & PATH_FLAG_DONT_AVOID_DYNAMIC_OBJECTS)!=0);
|
|
m_PathRequests[i].m_bGoAsFarAsPossibleIfNavMeshNotLoaded = ((reqStruct.m_iFlags & PATH_FLAG_SELECT_CLOSEST_LOADED_NAVMESH_TO_TARGET)!=0);
|
|
m_PathRequests[i].m_bDoPostProcessToPreserveSlopeInfo = ((reqStruct.m_iFlags & PATH_FLAG_PRESERVE_SLOPE_INFO_IN_PATH)!=0);
|
|
m_PathRequests[i].m_bSmoothSharpCorners = ((reqStruct.m_iFlags & PATH_FLAG_CUT_SHARP_CORNERS)!=0);
|
|
m_PathRequests[i].m_bDynamicNavMeshRoute = ((reqStruct.m_iFlags & PATH_FLAG_DYNAMIC_NAVMESH_ROUTE)!=0);
|
|
m_PathRequests[i].m_bNeverEnterWater = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_ENTER_WATER)!=0);
|
|
m_PathRequests[i].m_bNeverStartInWater = ((reqStruct.m_iFlags & PATH_FLAG_NEVER_START_IN_WATER)!=0);
|
|
m_PathRequests[i].m_bReduceObjectBoundingBoxes = ((reqStruct.m_iFlags & PATH_FLAG_REDUCE_OBJECT_BBOXES)!=0);
|
|
m_PathRequests[i].m_bIgnoreNonSignificantObjects = ((reqStruct.m_iFlags & PATH_FLAG_IGNORE_NON_SIGNIFICANT_OBJECTS)!=0);
|
|
m_PathRequests[i].m_bIfStartNotOnPavementAllowDropsAndClimbs = ((reqStruct.m_iFlags & PATH_FLAG_IF_NOT_ON_PAVEMENT_ALLOW_DROPS_AND_CLIMBS)!=0);
|
|
|
|
m_PathRequests[i].m_bFleeNeverEndInWater = ((reqStruct.m_iFlags & PATH_FLAG_FLEE_NEVER_END_IN_WATER)!=0);
|
|
|
|
m_PathRequests[i].m_bUseMaxSlopeNavigable = (reqStruct.m_fMaxSlopeNavigable != 0.0f);
|
|
m_PathRequests[i].m_bUseDirectionalCover = ((reqStruct.m_iFlags & PATH_FLAG_USE_DIRECTIONAL_COVER)!=0);
|
|
m_PathRequests[i].m_bFavourEnclosedSpaces = ((reqStruct.m_iFlags & PATH_FLAG_FAVOUR_ENCLOSED_SPACES)!=0);
|
|
m_PathRequests[i].m_bAvoidPotentialExplosions = ((reqStruct.m_iFlags & PATH_FLAG_AVOID_POTENTIAL_EXPLOSIONS)!=0);
|
|
m_PathRequests[i].m_bAvoidTearGas = ((reqStruct.m_iFlags & PATH_FLAG_AVOID_TEAR_GAS)!=0);
|
|
m_PathRequests[i].m_bAllowToNavigateUpSteepPolygons = ((reqStruct.m_iFlags & PATH_FLAG_ALLOW_TO_NAVIGATE_UP_STEEP_POLYGONS)!=0);
|
|
m_PathRequests[i].m_bAllowToPushVehicleDoorsClosed = ((reqStruct.m_iFlags & PATH_FLAG_ALLOW_TO_PUSH_VEHICLE_DOORS_CLOSED)!=0);
|
|
m_PathRequests[i].m_bMissionPed = ((reqStruct.m_iFlags & PATH_FLAG_MISSION_PED)!=0);
|
|
m_PathRequests[i].m_bDeactivateObjectsIfCantResolveEndPoints = ((reqStruct.m_iFlags & PATH_FLAG_DEACTIVATE_OBJECTS_IF_CANT_RESOLVE_ENDPOINTS)!=0);
|
|
m_PathRequests[i].m_bDontAvoidFire = ((reqStruct.m_iFlags & PATH_FLAG_DONT_AVOID_FIRE)!=0);
|
|
m_PathRequests[i].m_bCoverFinderPath = ((reqStruct.m_iFlags & PATH_FLAG_COVERFINDER)!=0);
|
|
|
|
m_PathRequests[i].m_fMaxDistanceToAdjustPathStart = reqStruct.m_fMaxDistanceToAdjustPathStart;
|
|
m_PathRequests[i].m_fMaxDistanceToAdjustPathEnd = reqStruct.m_fMaxDistanceToAdjustPathEnd;
|
|
|
|
|
|
// Randomising of points can be triggered either by path flag, or by a ped reset flag
|
|
// (now applied later, through CPathServerGameInterfaceGta).
|
|
m_PathRequests[i].m_bRandomisePoints = ((reqStruct.m_iFlags & PATH_FLAG_RANDOMISE_POINTS)!=0);
|
|
|
|
Assert(reqStruct.m_fMaxSlopeNavigable >= 0.0f && reqStruct.m_fMaxSlopeNavigable <= PI/2.0f);
|
|
m_PathRequests[i].m_fMaxSlopeNavigable = reqStruct.m_fMaxSlopeNavigable;
|
|
|
|
m_PathRequests[i].m_fClampMaxSearchDistance = reqStruct.m_fClampMaxSearchDistance;
|
|
|
|
// Initialise as if there is no underwater navigation. If there is, OverridePathRequestParameters()
|
|
// may change these.
|
|
m_PathRequests[i].m_fDistanceBelowStartToLookForPoly = CPathServerThread::ms_fNormalDistBelowToLookForPoly;
|
|
m_PathRequests[i].m_fDistanceAboveStartToLookForPoly = CPathServerThread::ms_fNormalDistAboveToLookForPoly;
|
|
m_PathRequests[i].m_fDistanceBelowEndToLookForPoly = CPathServerThread::ms_fNormalDistBelowToLookForPoly;
|
|
m_PathRequests[i].m_fDistanceAboveEndToLookForPoly = CPathServerThread::ms_fNormalDistAboveToLookForPoly;
|
|
|
|
#if !__FINAL
|
|
if(ms_bCutCornersOffAllPaths) m_PathRequests[i].m_bSmoothSharpCorners = true;
|
|
m_PathRequests[i].m_hHandleThisWas = m_PathRequests[i].m_hHandle;
|
|
|
|
if(m_bDisableObjectAvoidance)
|
|
m_PathRequests[i].m_bDontAvoidDynamicObjects = true;
|
|
#endif
|
|
|
|
// Tesselation code used for avoidance of dynamic objects hasn't been adapted
|
|
// to work with other data sets than the regular mesh, it's not clear yet
|
|
// if we'll need that or not. Thus, in that case, we force m_bDontAvoidDynamicObjects
|
|
// to true, to make use of existing code paths to not do avoidance.
|
|
if(reqStruct.m_NavDomain != kNavDomainRegular)
|
|
{
|
|
m_PathRequests[i].m_bDontAvoidDynamicObjects = true;
|
|
}
|
|
|
|
m_PathRequests[i].m_bProblemPathStartsAndEndsOnSamePoly = false;
|
|
m_PathRequests[i].m_fPathSearchCompletionRadius = reqStruct.m_fCompletionRadius;
|
|
m_PathRequests[i].m_fEntityRadius = reqStruct.m_fEntityRadius;
|
|
m_PathRequests[i].m_fEntityRadius = Clamp(m_PathRequests[i].m_fEntityRadius, PATHSERVER_PED_RADIUS, PATHSERVER_MAX_PED_RADIUS);
|
|
|
|
m_PathRequests[i].m_bUseVariableEntityRadius = Abs(m_PathRequests[i].m_fEntityRadius - PATHSERVER_PED_RADIUS) > 0.01f;
|
|
m_PathRequests[i].m_bHasAdjustedDynamicObjectsMinMaxForWidth = false;
|
|
|
|
|
|
// Store spheres of influence which the path will favour or avoid, depending upon the fWeighting member
|
|
m_PathRequests[i].m_iNumInfluenceSpheres = Min((int)reqStruct.m_iNumInfluenceSpheres, MAX_NUM_INFLUENCE_SPHERES);
|
|
|
|
if(m_PathRequests[i].m_iNumInfluenceSpheres)
|
|
{
|
|
sysMemCpy(m_PathRequests[i].m_InfluenceSpheres, reqStruct.m_InfluenceSpheres, sizeof(TInfluenceSphere) * m_PathRequests[i].m_iNumInfluenceSpheres);
|
|
}
|
|
#ifdef GTA_ENGINE
|
|
if(m_PathRequests[i].m_bAvoidPotentialExplosions && m_PathRequests[i].m_iNumInfluenceSpheres < MAX_NUM_INFLUENCE_SPHERES)
|
|
{
|
|
m_PathRequests[i].m_iNumInfluenceSpheres += CreateInfluenceSpheresForPotentialExplosions(
|
|
m_PathRequests[i].m_vPathStart,
|
|
&m_PathRequests[i].m_InfluenceSpheres[ m_PathRequests[i].m_iNumInfluenceSpheres ],
|
|
MAX_NUM_INFLUENCE_SPHERES - m_PathRequests[i].m_iNumInfluenceSpheres );
|
|
|
|
Assert(m_PathRequests[i].m_iNumInfluenceSpheres <= MAX_NUM_INFLUENCE_SPHERES);
|
|
}
|
|
if(m_PathRequests[i].m_bAvoidTearGas && m_PathRequests[i].m_iNumInfluenceSpheres < MAX_NUM_INFLUENCE_SPHERES)
|
|
{
|
|
m_PathRequests[i].m_iNumInfluenceSpheres += CreateInfluenceSpheresForTearGas(
|
|
m_PathRequests[i].m_vPathStart,
|
|
&m_PathRequests[i].m_InfluenceSpheres[ m_PathRequests[i].m_iNumInfluenceSpheres ],
|
|
MAX_NUM_INFLUENCE_SPHERES - m_PathRequests[i].m_iNumInfluenceSpheres);
|
|
|
|
Assert(m_PathRequests[i].m_iNumInfluenceSpheres <= MAX_NUM_INFLUENCE_SPHERES);
|
|
}
|
|
m_PathRequests[i].m_iNumInfluenceSpheres = Min((int)m_PathRequests[i].m_iNumInfluenceSpheres, MAX_NUM_INFLUENCE_SPHERES);
|
|
#endif
|
|
m_PathRequests[i].m_bIgnoreTypeVehicles = reqStruct.m_bIgnoreTypeVehicles;
|
|
m_PathRequests[i].m_bIgnoreTypeObjects = reqStruct.m_bIgnoreTypeObjects;
|
|
|
|
m_PathRequests[i].m_iNumIncludeObjects = reqStruct.m_iNumIncludeObjects;
|
|
m_PathRequests[i].m_iNumExcludeObjects = reqStruct.m_iNumExcludeObjects;
|
|
|
|
#ifdef GTA_ENGINE
|
|
int o;
|
|
for(o=0; o<reqStruct.m_iNumIncludeObjects; o++)
|
|
{
|
|
switch(reqStruct.m_IncludeObjects[o]->GetType())
|
|
{
|
|
case ENTITY_TYPE_VEHICLE:
|
|
m_PathRequests[i].m_IncludeObjects[o] = ((CVehicle*)reqStruct.m_IncludeObjects[o])->GetPathServerDynamicObjectIndex();
|
|
break;
|
|
case ENTITY_TYPE_OBJECT:
|
|
m_PathRequests[i].m_IncludeObjects[o] = ((CObject*)reqStruct.m_IncludeObjects[o])->GetPathServerDynamicObjectIndex();
|
|
break;
|
|
default:
|
|
Assertf(false, "Entity type isn't supported in pathserver.");
|
|
m_PathRequests[i].m_iNumIncludeObjects--;
|
|
}
|
|
}
|
|
for(o=0; o<reqStruct.m_iNumExcludeObjects; o++)
|
|
{
|
|
switch(reqStruct.m_ExcludeObjects[o]->GetType())
|
|
{
|
|
case ENTITY_TYPE_VEHICLE:
|
|
m_PathRequests[i].m_ExcludeObjects[o] = ((CVehicle*)reqStruct.m_ExcludeObjects[o])->GetPathServerDynamicObjectIndex();
|
|
break;
|
|
case ENTITY_TYPE_OBJECT:
|
|
m_PathRequests[i].m_ExcludeObjects[o] = ((CObject*)reqStruct.m_ExcludeObjects[o])->GetPathServerDynamicObjectIndex();
|
|
break;
|
|
default:
|
|
Assertf(false, "Entity type isn't supported in pathserver.");
|
|
m_PathRequests[i].m_iNumExcludeObjects--;
|
|
}
|
|
}
|
|
#endif // GTA_ENGINE
|
|
|
|
#if __DEV
|
|
m_PathRequests[i].m_pContext = (void*)reqStruct.m_pPed;
|
|
#endif
|
|
|
|
m_PathRequests[i].m_iPedRandomSeed = 0;
|
|
|
|
// Set the default movement costs
|
|
m_PathRequests[i].m_MovementCosts.SetDefault();
|
|
|
|
//m_PathRequests[i].m_pEntityThisPathIsOn = reqStruct.m_pEntityPathIsOn;
|
|
m_PathRequests[i].m_iIndexOfDynamicNavMesh = NAVMESH_NAVMESH_INDEX_NONE;
|
|
|
|
m_PathRequests[i].m_PathResultInfo.m_iDynObjIndex = DYNAMIC_OBJECT_INDEX_NONE;
|
|
|
|
m_PathRequests[i].m_PathResultInfo.m_vClosestPointFoundToTarget = reqStruct.m_vPathStart;
|
|
|
|
// Now, give the game code a chance to modify the path request, through the
|
|
// fwPathServerGameInterface object. In the first version of this for GTA,
|
|
// this may change iPedRandomSeed, m_MovementCosts, m_bRandomisePoints,
|
|
// and the dynamic mesh info, depending on the user ped and the entity it
|
|
// may be standing on.
|
|
GetGameInterface().OverridePathRequestParameters(reqStruct, m_PathRequests[i]);
|
|
|
|
// Do this after OverridePathRequestParameters
|
|
CPathServer::ModifyMovementCosts(&m_PathRequests[i].m_MovementCosts, m_PathRequests[i].m_bWander, m_PathRequests[i].m_bFleeTarget);
|
|
|
|
// Fix errors/performance issues arising from certain bad usage configurations of influence spheres
|
|
ValidateInfluenceSpheres(&m_PathRequests[i]);
|
|
|
|
// Ready
|
|
m_PathRequests[i].m_bWasAborted = false;
|
|
m_PathRequests[i].m_bRequestPending = true;
|
|
|
|
#if !__FINAL
|
|
if(m_RequestTimer)
|
|
{
|
|
m_RequestTimer->Stop();
|
|
m_fTimeTakenToIssueRequestsInMSecs += (float) m_RequestTimer->GetTimeMS();
|
|
}
|
|
#endif
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
// sysIpcSetEvent(CPathServer::m_PathRequestEvent);
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#endif
|
|
return m_PathRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_PATH_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#if !__FINAL
|
|
if(m_RequestTimer)
|
|
{
|
|
m_RequestTimer->Stop();
|
|
m_fTimeTakenToIssueRequestsInMSecs += (float) m_RequestTimer->GetTimeMS();
|
|
}
|
|
#endif
|
|
|
|
#if AI_OPTIMISATIONS_OFF || AI_VEHICLE_OPTIMISATIONS_OFF || AI_NAVIGATION_OPTIMSATIONS_OFF || NAVMESH_OPTIMISATIONS_OFF
|
|
Errorf("CPathServer : WARNING - no available slots for path request!");
|
|
#endif
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
|
|
// NAME : ValidateInfluenceSpheres
|
|
// PURPOSE : Check for & fix cases where we have influence spheres which may cause problems for navigation:
|
|
// 1) a repelling sphere is placed over a route end point (will cause route to exhaust entire search area before completing)
|
|
void CPathServer::ValidateInfluenceSpheres(CPathRequest * UNUSED_PARAM(pPathRequest))
|
|
{
|
|
|
|
}
|
|
|
|
// FUNCTION: CreateInfluenceSpheresForPotentialExplosions
|
|
// PURPOSE: Scan active fires which may result in an explosion, and create influence sphere for them.
|
|
// Given that we have only a limited number of influence spheres - sort fires by proximity to path start
|
|
// and add as many as we can.
|
|
// TODO: Cull those which are out of range wrt search extents? (will require path extents to be calculated up-front)
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
#define NAV_MAX_AVOID_FIRES 8
|
|
atArray<TPotentialExplosion> g_explosiveFires(0, NAV_MAX_AVOID_FIRES+1);
|
|
|
|
int CPathServer::CreateInfluenceSpheresForPotentialExplosions(const Vector3 & vPathStart, TInfluenceSphere * influenceSpheres, const int iMaxNumInfluenceSpheres)
|
|
{
|
|
if(iMaxNumInfluenceSpheres <= 0)
|
|
return 0;
|
|
|
|
g_explosiveFires.clear();
|
|
|
|
// Hacky cull to ignore fires which are sufficiently far from the path's origin (they will explode before we get there)
|
|
const float fMaxRangeFromStart = 40.0f*40.0f;
|
|
|
|
int i,f;
|
|
for (i=0; i<g_fireMan.GetNumActiveFires(); i++)
|
|
{
|
|
CFire* pFire = g_fireMan.GetActiveFire(i);
|
|
if(pFire)
|
|
{
|
|
// don't add multiple fires from the same entity
|
|
bool bAlreadyAvoided = false;
|
|
if(pFire->GetEntity())
|
|
{
|
|
for(f=0; f<g_explosiveFires.GetCount(); f++)
|
|
{
|
|
if(g_explosiveFires[f].pFire->GetEntity() == pFire->GetEntity())
|
|
{
|
|
bAlreadyAvoided = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(bAlreadyAvoided)
|
|
continue;
|
|
|
|
if (pFire->GetFireType()==FIRETYPE_REGD_VEH_PETROL_TANK || pFire->GetFireType()==FIRETYPE_TIMED_PETROL_POOL || pFire->GetFireType()==FIRETYPE_TIMED_PETROL_TRAIL)
|
|
{
|
|
const Vec3V vVec = pFire->GetPositionWorld() - RCC_VEC3V(vPathStart);
|
|
const float fDistSqr = MagSquared(vVec).Getf();
|
|
|
|
if(fDistSqr < fMaxRangeFromStart)
|
|
{
|
|
for(f=0; f<g_explosiveFires.GetCount(); f++)
|
|
{
|
|
if(fDistSqr < g_explosiveFires[f].fDistSqr)
|
|
{
|
|
TPotentialExplosion newFire;
|
|
newFire.pFire = pFire;
|
|
newFire.fDistSqr = fDistSqr;
|
|
|
|
g_explosiveFires.Insert(f) = newFire;
|
|
if(g_explosiveFires.GetCount() > NAV_MAX_AVOID_FIRES)
|
|
g_explosiveFires.Resize(NAV_MAX_AVOID_FIRES);
|
|
break;
|
|
}
|
|
}
|
|
if(f == g_explosiveFires.GetCount() && g_explosiveFires.GetCount() < NAV_MAX_AVOID_FIRES)
|
|
{
|
|
TPotentialExplosion newFire;
|
|
newFire.pFire = pFire;
|
|
newFire.fDistSqr = fDistSqr;
|
|
g_explosiveFires.Push(newFire);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const int iMinCount = Min(iMaxNumInfluenceSpheres, NAV_MAX_AVOID_FIRES);
|
|
if(g_explosiveFires.GetCount() > iMinCount)
|
|
g_explosiveFires.Resize(iMinCount);
|
|
|
|
for(i=0; i<g_explosiveFires.GetCount(); i++)
|
|
{
|
|
float blastRadius = 0.0f;
|
|
switch(g_explosiveFires[i].pFire->GetFireType())
|
|
{
|
|
case FIRETYPE_REGD_VEH_PETROL_TANK:
|
|
if(g_explosiveFires[i].pFire->GetEntity())
|
|
blastRadius = g_explosiveFires[i].pFire->GetEntity()->GetBoundRadius();
|
|
break;
|
|
case FIRETYPE_TIMED_PETROL_TRAIL:
|
|
case FIRETYPE_TIMED_PETROL_POOL:
|
|
blastRadius = 3.0f; // MAGIC!
|
|
break;
|
|
default:
|
|
Assertf(0, "found unsupported fire type");
|
|
break;
|
|
}
|
|
|
|
static float sfSafeDistance = 5.0f;
|
|
static dev_float fInnerWeighting = 100.0f;
|
|
static dev_float fOuterWeighting = 100.0f;
|
|
|
|
influenceSpheres[i].SetOrigin( VEC3V_TO_VECTOR3( g_explosiveFires[i].pFire->GetPositionWorld() ) );
|
|
influenceSpheres[i].SetRadius( blastRadius + sfSafeDistance );
|
|
influenceSpheres[i].SetInnerWeighting(fInnerWeighting);
|
|
influenceSpheres[i].SetOuterWeighting(fOuterWeighting);
|
|
}
|
|
|
|
return g_explosiveFires.GetCount();
|
|
}
|
|
|
|
// FUNCTION: CreateInfluenceSpheresForTearGas
|
|
// PURPOSE: Scan active tear gas clouds, and create influence sphere for them.
|
|
int CPathServer::CreateInfluenceSpheresForTearGas(const Vector3& vPathStart, TInfluenceSphere* influenceSpheres, const int iMaxNumInfluenceSpheres)
|
|
{
|
|
// First check to see if there is no room for influence spheres to be added
|
|
if( iMaxNumInfluenceSpheres <= 0 )
|
|
{
|
|
// in which case there is no work to do here
|
|
return 0;
|
|
}
|
|
|
|
// Create a list for active tear gas explosions
|
|
static const int NAV_MAX_AVOID_TEAR_GAS = 8;
|
|
atFixedArray<phGtaExplosionInst*, NAV_MAX_AVOID_TEAR_GAS> activeTearGasExplosionList(NAV_MAX_AVOID_TEAR_GAS);
|
|
|
|
// Query the explosion manager
|
|
int numExplosionsFound = CExplosionManager::FindExplosionsByTag(EXP_TAG_SMOKEGRENADE, &activeTearGasExplosionList[0], NAV_MAX_AVOID_TEAR_GAS);
|
|
|
|
// Create a list for the gas clouds
|
|
// Size this list according to the specified number of influences to add
|
|
atArray<TGasCloud> gasPositionsList(0, iMaxNumInfluenceSpheres);
|
|
|
|
// Traverse the list of active explosions
|
|
for(int iExplosion = 0; iExplosion < numExplosionsFound; iExplosion++)
|
|
{
|
|
phGtaExplosionInst* pExplosionInst = activeTearGasExplosionList[iExplosion];
|
|
if( pExplosionInst )
|
|
{
|
|
// Compute distance squared from path start to explosion position
|
|
Vec3V candidateExplosionPos = pExplosionInst->GetPosWld();
|
|
float fCandidateDistSq = DistSquared(VECTOR3_TO_VEC3V(vPathStart), candidateExplosionPos).Getf();
|
|
|
|
// Setup the element to consider adding to the list
|
|
TGasCloud candidateCloud;
|
|
candidateCloud.worldPos = candidateExplosionPos;
|
|
candidateCloud.fDistSqr = fCandidateDistSq;
|
|
|
|
// If the list is empty
|
|
if( gasPositionsList.GetCount() == 0 )
|
|
{
|
|
// append to the list
|
|
gasPositionsList.Push(candidateCloud);
|
|
}
|
|
else // list is not empty
|
|
{
|
|
// scan from back to front to find an insertion point, if any
|
|
for(int scanIndex=gasPositionsList.GetCount()-1; scanIndex >= 0; scanIndex--)
|
|
{
|
|
// if we find an entry more distant than the candidate
|
|
if( fCandidateDistSq < gasPositionsList[scanIndex].fDistSqr )
|
|
{
|
|
// check if the list is full
|
|
if( gasPositionsList.GetCount() >= gasPositionsList.GetCapacity() )
|
|
{
|
|
// Delete the most distant entry (always tail of the list)
|
|
gasPositionsList.Delete(gasPositionsList.GetCount()-1);
|
|
}
|
|
|
|
// replace entry with this closer explosion data
|
|
gasPositionsList.Insert(scanIndex) = candidateCloud;
|
|
|
|
// break out of the scanIndex traversal
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const int iMinCount = Min(iMaxNumInfluenceSpheres, NAV_MAX_AVOID_TEAR_GAS);
|
|
if(gasPositionsList.GetCount() > iMinCount)
|
|
gasPositionsList.Resize(iMinCount);
|
|
|
|
// Now we have the list of gas positions closest to path start
|
|
// Use this list to fill in the influences requested
|
|
for(int i = 0; i < gasPositionsList.GetCount(); i++)
|
|
{
|
|
static dev_float fTearGasRadius = 15.0f; // MAGIC!
|
|
static dev_float fTearGasInnerWeighting = 100.0f; // MAGIC!
|
|
static dev_float fTearGasOuterWeighting = 100.0f; // MAGIC!
|
|
influenceSpheres[i].SetOrigin(VEC3V_TO_VECTOR3(gasPositionsList[i].worldPos));
|
|
influenceSpheres[i].SetRadius(fTearGasRadius);
|
|
influenceSpheres[i].SetInnerWeighting(fTearGasInnerWeighting);
|
|
influenceSpheres[i].SetOuterWeighting(fTearGasOuterWeighting);
|
|
}
|
|
|
|
// Report the number of influence spheres processed
|
|
return gasPositionsList.GetCount();
|
|
}
|
|
|
|
#endif // GTA_ENGINE
|
|
|
|
TPathHandle CPathServer::RequestGrid(const Vector3 & vOrigin, u32 iSize, float fResolution, void * DEV_ONLY(pContext), aiNavDomain domain)
|
|
{
|
|
Assert(ms_bGameInSession);
|
|
if(!ms_bGameInSession)
|
|
return PATH_HANDLE_NULL;
|
|
|
|
if(iSize > WALKRNDOBJGRID_MAXSIZE) iSize = WALKRNDOBJGRID_MAXSIZE;
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextGridIndexToStartFrom;
|
|
|
|
m_iNextGridIndexToStartFrom++;
|
|
if(m_iNextGridIndexToStartFrom >= MAX_NUM_GRID_REQUESTS)
|
|
m_iNextGridIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_GRID_REQUESTS;
|
|
|
|
while(c)
|
|
{
|
|
if( m_GridRequests[i].IsReadyForUse() )
|
|
{
|
|
m_GridRequests[i].Clear();
|
|
|
|
m_GridRequests[i].m_hHandle = m_iNextHandle++;
|
|
m_GridRequests[i].m_NavDomain = domain;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_GridRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
m_GridRequests[i].m_bSlotEmpty = false;
|
|
m_GridRequests[i].m_bComplete = false;
|
|
m_GridRequests[i].m_bRequestActive = false;
|
|
|
|
m_GridRequests[i].m_iType = EGrid;
|
|
|
|
m_GridRequests[i].m_WalkRndObjGrid.m_vOrigin = vOrigin;
|
|
m_GridRequests[i].m_WalkRndObjGrid.m_iSize = (u16)iSize;
|
|
m_GridRequests[i].m_WalkRndObjGrid.m_iCentreCell = (u16)(iSize/2);
|
|
m_GridRequests[i].m_WalkRndObjGrid.m_fResolution = fResolution;
|
|
|
|
#if __DEV
|
|
m_GridRequests[i].m_pContext = pContext;
|
|
#endif
|
|
|
|
m_GridRequests[i].m_bWasAborted = false;
|
|
m_GridRequests[i].m_bRequestPending = true;
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#if !__FINAL
|
|
m_GridRequests[i].m_hHandleThisWas = m_GridRequests[i].m_hHandle;
|
|
m_GridRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
m_GridRequests[i].m_iFrameRequestCompleted = 0;
|
|
#endif
|
|
#endif
|
|
return m_GridRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_GRID_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#if AI_OPTIMISATIONS_OFF || AI_VEHICLE_OPTIMISATIONS_OFF || AI_NAVIGATION_OPTIMSATIONS_OFF || NAVMESH_OPTIMISATIONS_OFF
|
|
Assertf(0, "CPathServer - no available slots for grid request");
|
|
#endif
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
|
|
TPathHandle CPathServer::RequestLineOfSight(const Vector3 & vStart, const Vector3 & vEnd, float fRadius, bool bDynamicObjects, bool bNoLosAcrossWaterBoundary, bool bStartInWater, int iNumExcludeObjects, CEntity ** ppExcludeObjects, const float fMaxSlopeAngle, void * pContext, aiNavDomain domain)
|
|
{
|
|
Vector3 vPts[2] = { vStart, vEnd };
|
|
|
|
return RequestLineOfSight(vPts, 2, fRadius, bDynamicObjects, true, bNoLosAcrossWaterBoundary, bStartInWater, iNumExcludeObjects, ppExcludeObjects, fMaxSlopeAngle, pContext, domain);
|
|
}
|
|
|
|
TPathHandle CPathServer::RequestLineOfSight(const Vector3 * vPts, int iNumPts, float fRadius, bool bDynamicObjects, bool bQuitAtFirstLosFail, bool bNoLosAcrossWaterBoundary, bool bStartInWater, int iNumExcludeObjects, CEntity ** GTA_ENGINE_ONLY(ppExcludeObjects), const float fMaxSlopeAngle, void * DEV_ONLY(pContext), aiNavDomain domain)
|
|
{
|
|
Assert(ms_bGameInSession);
|
|
if(!ms_bGameInSession)
|
|
return PATH_HANDLE_NULL;
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextLosIndexToStartFrom;
|
|
|
|
m_iNextLosIndexToStartFrom++;
|
|
if(m_iNextLosIndexToStartFrom >= MAX_NUM_LOS_REQUESTS)
|
|
m_iNextLosIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_LOS_REQUESTS;
|
|
int p;
|
|
|
|
while(c)
|
|
{
|
|
if(m_LineOfSightRequests[i].IsReadyForUse() )
|
|
{
|
|
m_LineOfSightRequests[i].Clear();
|
|
|
|
m_LineOfSightRequests[i].m_hHandle = m_iNextHandle++;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_LineOfSightRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
// If too many points are passed in, notify the user with an assert, and limit the number
|
|
// to avoid array overruns.
|
|
if(!Verifyf(iNumPts <= MAX_LINEOFSIGHT_POINTS, "Too many points (%d) in pathserver line of sight test, max %d supported.", iNumPts, MAX_LINEOFSIGHT_POINTS))
|
|
{
|
|
iNumPts = MAX_LINEOFSIGHT_POINTS;
|
|
}
|
|
|
|
m_LineOfSightRequests[i].m_bSlotEmpty = false;
|
|
m_LineOfSightRequests[i].m_bComplete = false;
|
|
m_LineOfSightRequests[i].m_bRequestActive = false;
|
|
|
|
m_LineOfSightRequests[i].m_iType = ELineOfSight;
|
|
m_LineOfSightRequests[i].m_NavDomain = domain;
|
|
|
|
m_LineOfSightRequests[i].m_iNumPts = iNumPts;
|
|
|
|
for(p=0; p<iNumPts; p++)
|
|
{
|
|
m_LineOfSightRequests[i].m_vPoints[p] = vPts[p];
|
|
}
|
|
for(p=0; p<MAX_LINEOFSIGHT_POINTS; p++)
|
|
{
|
|
m_LineOfSightRequests[i].m_bLosResults[p] = false;
|
|
}
|
|
|
|
m_LineOfSightRequests[i].m_fRadius = fRadius;
|
|
m_LineOfSightRequests[i].m_bQuitAtFirstLosFail = bQuitAtFirstLosFail;
|
|
m_LineOfSightRequests[i].m_bNoLosAcrossWaterBoundary = bNoLosAcrossWaterBoundary;
|
|
m_LineOfSightRequests[i].m_bStartsInWater = bStartInWater;
|
|
m_LineOfSightRequests[i].m_bDynamicObjects = bDynamicObjects;
|
|
|
|
m_LineOfSightRequests[i].m_iNumExcludeObjects = iNumExcludeObjects;
|
|
m_LineOfSightRequests[i].m_fMaxAngle = fMaxSlopeAngle;
|
|
|
|
#ifdef GTA_ENGINE
|
|
for(s32 o=0; o<iNumExcludeObjects; o++)
|
|
{
|
|
switch(ppExcludeObjects[o]->GetType())
|
|
{
|
|
case ENTITY_TYPE_VEHICLE:
|
|
m_LineOfSightRequests[i].m_ExcludeObjects[o] = ((CVehicle*)ppExcludeObjects[o])->GetPathServerDynamicObjectIndex();
|
|
break;
|
|
case ENTITY_TYPE_OBJECT:
|
|
m_LineOfSightRequests[i].m_ExcludeObjects[o] = ((CObject*)ppExcludeObjects[o])->GetPathServerDynamicObjectIndex();
|
|
break;
|
|
default:
|
|
Assertf(false, "Entity type isn't supported in pathserver.");
|
|
m_LineOfSightRequests[i].m_iNumExcludeObjects--;
|
|
}
|
|
}
|
|
#endif // GTA_ENGINE
|
|
|
|
#if __DEV
|
|
m_LineOfSightRequests[i].m_pContext = pContext;
|
|
#endif
|
|
|
|
m_LineOfSightRequests[i].m_bWasAborted = false;
|
|
m_LineOfSightRequests[i].m_bRequestPending = true;
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#if !__FINAL
|
|
m_LineOfSightRequests[i].m_hHandleThisWas = m_LineOfSightRequests[i].m_hHandle;
|
|
m_LineOfSightRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
m_LineOfSightRequests[i].m_iFrameRequestCompleted = 0;
|
|
#endif
|
|
#endif
|
|
return m_LineOfSightRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_LOS_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
TPathHandle
|
|
CPathServer::RequestAudioProperties(const Vector3 & vPosition, TNavMeshAndPoly * pKnownNavmeshPosition, float fRadius, const Vector3 & vDirection, const bool bPriorityRequest, void * DEV_ONLY(pContext), aiNavDomain domain)
|
|
{
|
|
Assert(ms_bGameInSession);
|
|
if(!ms_bGameInSession)
|
|
return PATH_HANDLE_NULL;
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextAudioIndexToStartFrom;
|
|
|
|
m_iNextAudioIndexToStartFrom++;
|
|
if(m_iNextAudioIndexToStartFrom >= MAX_NUM_AUDIO_REQUESTS)
|
|
m_iNextAudioIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_AUDIO_REQUESTS;
|
|
|
|
while(c)
|
|
{
|
|
if(m_AudioRequests[i].IsReadyForUse() )
|
|
{
|
|
m_AudioRequests[i].Clear();
|
|
|
|
m_AudioRequests[i].m_hHandle = m_iNextHandle++;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_AudioRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
m_AudioRequests[i].m_bSlotEmpty = false;
|
|
m_AudioRequests[i].m_bComplete = false;
|
|
m_AudioRequests[i].m_bRequestActive = false;
|
|
m_AudioRequests[i].m_iType = EAudioProperties;
|
|
m_AudioRequests[i].m_NavDomain = domain;
|
|
|
|
m_AudioRequests[i].m_bPriorityRequest = bPriorityRequest;
|
|
|
|
m_AudioRequests[i].m_vPosition = vPosition;
|
|
m_AudioRequests[i].m_vDirection = vDirection;
|
|
m_AudioRequests[i].m_fRadius = fRadius;
|
|
|
|
#if __DEV
|
|
m_AudioRequests[i].m_pContext = pContext;
|
|
#endif
|
|
if(pKnownNavmeshPosition && pKnownNavmeshPosition->m_iNavMeshIndex!=NAVMESH_NAVMESH_INDEX_NONE && pKnownNavmeshPosition->m_iPolyIndex!=NAVMESH_POLY_INDEX_NONE)
|
|
{
|
|
m_AudioRequests[i].m_KnownNavmeshPosition = *pKnownNavmeshPosition;
|
|
}
|
|
else
|
|
{
|
|
m_AudioRequests[i].m_KnownNavmeshPosition.Reset();
|
|
}
|
|
|
|
m_AudioRequests[i].m_bWasAborted = false;
|
|
m_AudioRequests[i].m_bRequestPending = true;
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#if !__FINAL
|
|
m_AudioRequests[i].m_hHandleThisWas = m_AudioRequests[i].m_hHandle;
|
|
m_AudioRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
m_AudioRequests[i].m_iFrameRequestCompleted = 0;
|
|
#endif
|
|
#endif
|
|
return m_AudioRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_AUDIO_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
|
|
// Requests a flood-fill search, to find the closest accessible sheltered navmesh poly within the given radius
|
|
TPathHandle
|
|
CPathServer::RequestClosestShelteredPolySearch(const Vector3 & vStartPos, const float fMaxRadius, void * pContext)
|
|
{
|
|
return RequestClosestViaFloodFill(vStartPos, fMaxRadius, CFloodFillRequest::EFindClosestShelteredPolyFloodFill, true, false, false, pContext);
|
|
}
|
|
|
|
// Requests a flood-fill search, to find the closest accessible unsheltered navmesh poly within the given radius
|
|
TPathHandle
|
|
CPathServer::RequestClosestUnshelteredPolySearch(const Vector3 & vStartPos, const float fMaxRadius, void * pContext)
|
|
{
|
|
return RequestClosestViaFloodFill(vStartPos, fMaxRadius, CFloodFillRequest::EFindClosestUnshelteredPolyFloodFill, true, false, false, pContext);
|
|
}
|
|
|
|
// Requests a flood-fill search, to find the closest accessible sheltered navmesh poly within the given radius
|
|
TPathHandle
|
|
CPathServer::RequestCalcAreaUnderfootSearch(const Vector3 & vStartPos, const float fMaxRadius, void * pContext)
|
|
{
|
|
return RequestClosestViaFloodFill(vStartPos, fMaxRadius, CFloodFillRequest::ECalcAreaUnderfoot, false, false, false, pContext);
|
|
}
|
|
|
|
// Requests a flood-fill search, to find the closest accessible carnode within the given radius
|
|
TPathHandle
|
|
CPathServer::RequestClosestCarNodeSearch(const Vector3 & vStartPos, const float fMaxRadius, void * pContext)
|
|
{
|
|
return RequestClosestViaFloodFill(vStartPos, fMaxRadius, CFloodFillRequest::EFindClosestCarNodeFloodFill, false, true, true, pContext);
|
|
}
|
|
|
|
TPathHandle
|
|
CPathServer::RequestHasNearbyPavementSearch(const Vector3& vStartPos, const float fMaxRadius, void* pContext)
|
|
{
|
|
return RequestClosestViaFloodFill(vStartPos, fMaxRadius, CFloodFillRequest::EFindNearbyPavementFloodFill, false, false, false, pContext);
|
|
}
|
|
|
|
// Requests a flood-fill search, to find the closest accessible subtype within the given radius
|
|
TPathHandle CPathServer::RequestClosestViaFloodFill(const Vector3 & vStartPos, const float fMaxRadius, CFloodFillRequest::EType eFloodFillType, const bool bCheckDynamicObjects, const bool bUseClimbsAndDrops, const bool bUseLadders, void * DEV_ONLY(pContext), aiNavDomain domain)
|
|
{
|
|
Assert(ms_bGameInSession);
|
|
if(!ms_bGameInSession)
|
|
return PATH_HANDLE_NULL;
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextFloodFillIndexToStartFrom;
|
|
|
|
m_iNextFloodFillIndexToStartFrom++;
|
|
if(m_iNextFloodFillIndexToStartFrom >= MAX_NUM_FLOODFILL_REQUESTS)
|
|
m_iNextFloodFillIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_FLOODFILL_REQUESTS;
|
|
|
|
while(c)
|
|
{
|
|
if(m_FloodFillRequests[i].IsReadyForUse() )
|
|
{
|
|
m_FloodFillRequests[i].Clear();
|
|
|
|
m_FloodFillRequests[i].m_hHandle = m_iNextHandle++;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_FloodFillRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
m_FloodFillRequests[i].m_bSlotEmpty = false;
|
|
m_FloodFillRequests[i].m_bComplete = false;
|
|
m_FloodFillRequests[i].m_bRequestActive = false;
|
|
m_FloodFillRequests[i].m_iType = EFloodFill;
|
|
m_FloodFillRequests[i].m_NavDomain = domain;
|
|
m_FloodFillRequests[i].m_FloodFillType = eFloodFillType;
|
|
|
|
m_FloodFillRequests[i].m_bConsiderDynamicObjects = bCheckDynamicObjects;
|
|
m_FloodFillRequests[i].m_bUseClimbsAndDrops = bUseClimbsAndDrops;
|
|
m_FloodFillRequests[i].m_bUseLadders = bUseLadders;
|
|
|
|
m_FloodFillRequests[i].m_vStartPos = vStartPos;
|
|
m_FloodFillRequests[i].m_fMaxRadius = fMaxRadius;
|
|
|
|
#if __DEV
|
|
m_FloodFillRequests[i].m_pContext = pContext;
|
|
#endif
|
|
|
|
m_FloodFillRequests[i].m_bWasAborted = false;
|
|
m_FloodFillRequests[i].m_bRequestPending = true;
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#if !__FINAL
|
|
m_FloodFillRequests[i].m_hHandleThisWas = m_FloodFillRequests[i].m_hHandle;
|
|
m_FloodFillRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
m_FloodFillRequests[i].m_iFrameRequestCompleted = 0;
|
|
#endif
|
|
#endif
|
|
return m_FloodFillRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_FLOODFILL_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
// Requests a clear-area search, to locate a clear patch of navmesh within search extents
|
|
TPathHandle CPathServer::RequestClearArea(const Vector3 & vSearchOrigin, const float fSearchRadiusXY, const float fSearchDistZ, const float fDesiredClearRadius, const float fOptionalMinimumRadius, const bool bConsiderDynamicObjects, const bool bSearchInteriors, const bool bSearchExterior, const bool bConsiderWater, const bool bConsiderSheltered, void * DEV_ONLY(pContext), aiNavDomain domain)
|
|
{
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextClearAreaIndexToStartFrom;
|
|
|
|
m_iNextClearAreaIndexToStartFrom++;
|
|
if(m_iNextClearAreaIndexToStartFrom >= MAX_NUM_CLEARAREA_REQUESTS)
|
|
m_iNextClearAreaIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_CLEARAREA_REQUESTS;
|
|
|
|
while(c)
|
|
{
|
|
if(m_ClearAreaRequests[i].IsReadyForUse() )
|
|
{
|
|
m_ClearAreaRequests[i].Clear();
|
|
|
|
m_ClearAreaRequests[i].m_hHandle = m_iNextHandle++;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_ClearAreaRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
m_ClearAreaRequests[i].m_bSlotEmpty = false;
|
|
m_ClearAreaRequests[i].m_bComplete = false;
|
|
m_ClearAreaRequests[i].m_bRequestActive = false;
|
|
m_ClearAreaRequests[i].m_iType = EClearArea;
|
|
m_ClearAreaRequests[i].m_NavDomain = domain;
|
|
|
|
m_ClearAreaRequests[i].m_vSearchOrigin = vSearchOrigin;
|
|
m_ClearAreaRequests[i].m_fSearchRadiusXY = fSearchRadiusXY;
|
|
m_ClearAreaRequests[i].m_fSearchDistZ = fSearchDistZ;
|
|
m_ClearAreaRequests[i].m_fDesiredClearAreaRadius = fDesiredClearRadius;
|
|
m_ClearAreaRequests[i].m_fMinimumDistanceFromOrigin = fOptionalMinimumRadius;
|
|
|
|
m_ClearAreaRequests[i].m_bConsiderDynamicObjects = bConsiderDynamicObjects;
|
|
m_ClearAreaRequests[i].m_bConsiderInterior = bSearchInteriors;
|
|
m_ClearAreaRequests[i].m_bConsiderExterior = bSearchExterior;
|
|
m_ClearAreaRequests[i].m_bConsiderWater = bConsiderWater;
|
|
m_ClearAreaRequests[i].m_bConsiderSheltered = bConsiderSheltered;
|
|
|
|
#if __DEV
|
|
m_ClearAreaRequests[i].m_pContext = pContext;
|
|
#endif
|
|
|
|
m_ClearAreaRequests[i].m_bWasAborted = false;
|
|
m_ClearAreaRequests[i].m_bRequestPending = true;
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#if !__FINAL
|
|
m_ClearAreaRequests[i].m_hHandleThisWas = m_ClearAreaRequests[i].m_hHandle;
|
|
m_ClearAreaRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
m_ClearAreaRequests[i].m_iFrameRequestCompleted = 0;
|
|
#endif
|
|
#endif
|
|
return m_ClearAreaRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_CLEARAREA_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
// Requests a clear-area search, to locate a clear patch of navmesh within search extents
|
|
TPathHandle CPathServer::RequestClosestPosition(const Vector3 & vSearchOrigin, const float fSearchRadius, const u32 iFlags, const float fZWeightingAbove, const float fZWeightingAtOrBelow, const float fMinimumSpacing, const s32 iNumAvoidSpheres, const spdSphere * pAvoidSpheres, const s32 iMaxNumResults, void * DEV_ONLY(pContext), aiNavDomain domain)
|
|
{
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
int i = m_iNextClosestPositionIndexToStartFrom;
|
|
|
|
m_iNextClosestPositionIndexToStartFrom++;
|
|
if(m_iNextClosestPositionIndexToStartFrom >= MAX_NUM_CLOSESTPOSITION_REQUESTS)
|
|
m_iNextClosestPositionIndexToStartFrom = 0;
|
|
|
|
int c = MAX_NUM_CLOSESTPOSITION_REQUESTS;
|
|
|
|
while(c)
|
|
{
|
|
if(m_ClosestPositionRequests[i].IsReadyForUse() )
|
|
{
|
|
m_ClosestPositionRequests[i].Clear();
|
|
|
|
m_ClosestPositionRequests[i].m_hHandle = m_iNextHandle++;
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_ClosestPositionRequests[i].m_iTimeRequestIssued = fwTimer::GetTimeInMilliseconds();
|
|
#endif
|
|
|
|
m_ClosestPositionRequests[i].m_bSlotEmpty = false;
|
|
m_ClosestPositionRequests[i].m_bComplete = false;
|
|
m_ClosestPositionRequests[i].m_bRequestActive = false;
|
|
m_ClosestPositionRequests[i].m_iType = EClosestPosition;
|
|
m_ClosestPositionRequests[i].m_NavDomain = domain;
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
Assert(iNumAvoidSpheres < CClosestPositionRequest::MAX_NUM_IGNORE_SPHERES);
|
|
m_ClosestPositionRequests[i].m_iNumAvoidSpheres = Min(iNumAvoidSpheres, CClosestPositionRequest::MAX_NUM_IGNORE_SPHERES);
|
|
|
|
sysMemCpy(m_ClosestPositionRequests[i].m_AvoidSpheres, pAvoidSpheres, sizeof(spdSphere)*iNumAvoidSpheres);
|
|
|
|
m_ClosestPositionRequests[i].m_vSearchOrigin = vSearchOrigin;
|
|
m_ClosestPositionRequests[i].m_fSearchRadius = fSearchRadius;
|
|
|
|
m_ClosestPositionRequests[i].m_fZWeightingAbove = fZWeightingAbove;
|
|
m_ClosestPositionRequests[i].m_fZWeightingAtOrBelow = fZWeightingAtOrBelow;
|
|
|
|
Assert(fMinimumSpacing >= 0.0f);
|
|
m_ClosestPositionRequests[i].m_fMinimumSpacing = Max(fMinimumSpacing, 0.0f);
|
|
|
|
Assert(iMaxNumResults <= CClosestPositionRequest::MAX_NUM_RESULTS);
|
|
m_ClosestPositionRequests[i].m_iMaxResults = Min(iMaxNumResults, CClosestPositionRequest::MAX_NUM_RESULTS);
|
|
|
|
m_ClosestPositionRequests[i].m_bConsiderDynamicObjects = ((iFlags & CClosestPositionRequest::Flag_ConsiderDynamicObjects)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderInterior = ((iFlags & CClosestPositionRequest::Flag_ConsiderInterior)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderExterior = ((iFlags & CClosestPositionRequest::Flag_ConsiderExterior)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderOnlyLand = ((iFlags & CClosestPositionRequest::Flag_ConsiderOnlyLand)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderOnlyWater = ((iFlags & CClosestPositionRequest::Flag_ConsiderOnlyWater)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderOnlyPavement = ((iFlags & CClosestPositionRequest::Flag_ConsiderOnlyPavement)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderOnlyNonIsolated = ((iFlags & CClosestPositionRequest::Flag_ConsiderOnlyNonIsolated)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderOnlySheltered = ((iFlags & CClosestPositionRequest::Flag_ConsiderOnlySheltered)!=0);
|
|
m_ClosestPositionRequests[i].m_bConsiderOnlyNetworkSpawn = ((iFlags & CClosestPositionRequest::Flag_ConsiderOnlyNetworkSpawn)!=0);
|
|
|
|
#if __DEV
|
|
m_ClosestPositionRequests[i].m_pContext = pContext;
|
|
#endif
|
|
|
|
m_ClosestPositionRequests[i].m_bWasAborted = false;
|
|
m_ClosestPositionRequests[i].m_bRequestPending = true;
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
#ifdef GTA_ENGINE
|
|
if(CPathServer::ms_bUseEventsForRequests)
|
|
{
|
|
// Signal the event which the pathserver thread is waiting upon
|
|
sysIpcSignalSema(CPathServer::m_PathRequestSema);
|
|
}
|
|
#if !__FINAL
|
|
m_ClosestPositionRequests[i].m_hHandleThisWas = m_ClosestPositionRequests[i].m_hHandle;
|
|
m_ClosestPositionRequests[i].m_iFrameRequestIssued = fwTimer::GetFrameCount();
|
|
m_ClosestPositionRequests[i].m_iFrameRequestCompleted = 0;
|
|
#endif
|
|
#endif
|
|
return m_ClosestPositionRequests[i].m_hHandle;
|
|
}
|
|
|
|
i++;
|
|
if(i >= MAX_NUM_CLOSESTPOSITION_REQUESTS)
|
|
i = 0;
|
|
c--;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_HANDLE_NULL;
|
|
}
|
|
|
|
// Use this version of the function when the type of the request is known.
|
|
EPathServerRequestResult CPathServer::IsRequestResultReady(TPathHandle hPath, EPathServerRequestType eType)
|
|
{
|
|
// We shouldn't really be calling this with a null handle..
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assertf(false, "Called IsRequestResultReady() with PATH_HANDLE_NULL");
|
|
return ERequest_NotFound;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
switch(eType)
|
|
{
|
|
case EPath:
|
|
{
|
|
for(int i=0; i<MAX_NUM_PATH_REQUESTS; i++)
|
|
{
|
|
if(m_PathRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_PathRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EGrid:
|
|
{
|
|
for(int i=0; i<MAX_NUM_GRID_REQUESTS; i++)
|
|
{
|
|
if(m_GridRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_GridRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ELineOfSight:
|
|
{
|
|
for(int i=0; i<MAX_NUM_LOS_REQUESTS; i++)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EAudioProperties:
|
|
{
|
|
for(int i=0; i<MAX_NUM_AUDIO_REQUESTS; i++)
|
|
{
|
|
if(m_AudioRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_AudioRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EFloodFill:
|
|
{
|
|
for(int i=0; i<MAX_NUM_FLOODFILL_REQUESTS; i++)
|
|
{
|
|
if(m_FloodFillRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_FloodFillRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EClearArea:
|
|
{
|
|
for(int i=0; i<MAX_NUM_CLEARAREA_REQUESTS; i++)
|
|
{
|
|
if(m_ClearAreaRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_ClearAreaRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EClosestPosition:
|
|
{
|
|
for(int i=0; i<MAX_NUM_CLOSESTPOSITION_REQUESTS; i++)
|
|
{
|
|
if(m_ClosestPositionRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_ClosestPositionRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
Assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotFound;
|
|
}
|
|
|
|
EPathServerRequestResult CPathServer::IsRequestResultReady(TPathHandle hPath)
|
|
{
|
|
// We shouldn't really be calling this with a null handle..
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assertf(false, "Called IsRequestResultReady() with PATH_HANDLE_NULL");
|
|
return ERequest_NotFound;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(int i=0; i<MAX_NUM_PATH_REQUESTS; i++)
|
|
{
|
|
if(m_PathRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_PathRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<MAX_NUM_GRID_REQUESTS; i++)
|
|
{
|
|
if(m_GridRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_GridRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<MAX_NUM_LOS_REQUESTS; i++)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<MAX_NUM_AUDIO_REQUESTS; i++)
|
|
{
|
|
if(m_AudioRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_AudioRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<MAX_NUM_FLOODFILL_REQUESTS; i++)
|
|
{
|
|
if(m_FloodFillRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_FloodFillRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<MAX_NUM_CLEARAREA_REQUESTS; i++)
|
|
{
|
|
if(m_ClearAreaRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_ClearAreaRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
for(int i=0; i<MAX_NUM_CLOSESTPOSITION_REQUESTS; i++)
|
|
{
|
|
if(m_ClosestPositionRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_ClosestPositionRequests[i].m_bComplete)
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_Ready;
|
|
}
|
|
else
|
|
{
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotReady;
|
|
}
|
|
}
|
|
}
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return ERequest_NotFound;
|
|
}
|
|
|
|
|
|
|
|
|
|
EPathServerErrorCode
|
|
CPathServer::LockPathResult(TPathHandle hPath, int * iNumPoints, Vector3 *& pPoints, TNavMeshWaypointFlag *& pWaypointFlags, TPathResultInfo * pPathInfo)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(hPath != PATH_HANDLE_NULL);
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(int i=0; i<MAX_NUM_PATH_REQUESTS; i++)
|
|
{
|
|
if(m_PathRequests[i].m_hHandle == hPath)
|
|
{
|
|
if(m_PathRequests[i].m_bComplete)
|
|
{
|
|
*iNumPoints = m_PathRequests[i].m_iNumPoints;
|
|
pPoints = &m_PathRequests[i].m_PathPoints[0];
|
|
pWaypointFlags = &m_PathRequests[i].m_WaypointFlags[0];
|
|
|
|
if(pPathInfo)
|
|
{
|
|
sysMemCpy(pPathInfo, &m_PathRequests[i].m_PathResultInfo, sizeof(TPathResultInfo));
|
|
}
|
|
|
|
#if NAVMESH_OPTIMISATIONS_OFF && __ASSERT
|
|
if(m_PathRequests[i].m_iNumPoints >= 2)
|
|
{
|
|
const Vector3 vDiff = m_PathRequests[i].m_PathPoints[m_PathRequests[i].m_iNumPoints-1] - m_PathRequests[i].m_PathPoints[m_PathRequests[i].m_iNumPoints-2];
|
|
Assert(vDiff.Mag2() > SMALL_FLOAT);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if !__FINAL
|
|
#ifdef GTA_ENGINE
|
|
#if DEBUG_DRAW
|
|
if(m_iVisualiseNavMeshes && m_PathRequests[i].m_iNumPoints)
|
|
{
|
|
for(s32 p=0; p<m_PathRequests[i].m_iNumPoints-1; p++)
|
|
{
|
|
grcDebugDraw::Line(
|
|
m_PathRequests[i].m_PathPoints[p] + Vector3(0, 0, 0.5f),
|
|
m_PathRequests[i].m_PathPoints[p+1] + Vector3(0, 0, 0.5f),
|
|
Color32(255,0,0,255) //0xff0000ff
|
|
);
|
|
}
|
|
}
|
|
#endif // DEBUG_DRAW
|
|
#endif
|
|
if(ms_bOutputDetailsOfPaths)
|
|
{
|
|
#if __BANK
|
|
#if __DEV
|
|
DebugOutPathRequestResult(&m_PathRequests[i]);
|
|
#endif // __DEV
|
|
#endif // BANK
|
|
}
|
|
#endif
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return m_PathRequests[i].m_iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
bool CPathServer::UnlockPathResultAndClear(TPathHandle hPath)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(hPath != PATH_HANDLE_NULL);
|
|
return false;
|
|
}
|
|
|
|
return CancelRequestInternal(hPath, false);
|
|
}
|
|
|
|
|
|
|
|
// Copies the data into the 'destGrid', and thereafter hPath is invalid.
|
|
EPathServerErrorCode CPathServer::GetGridResultAndClear(TPathHandle hPath, CWalkRndObjGrid & destGrid)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(hPath != PATH_HANDLE_NULL);
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(int i=0; i<MAX_NUM_GRID_REQUESTS; i++)
|
|
{
|
|
if(m_GridRequests[i].m_hHandle == hPath)
|
|
{
|
|
Assert(m_GridRequests[i].m_iType == EGrid);
|
|
|
|
if(m_GridRequests[i].m_bComplete)
|
|
{
|
|
sysMemCpy(&destGrid, &m_GridRequests[i].m_WalkRndObjGrid, sizeof(CWalkRndObjGrid));
|
|
|
|
EPathServerErrorCode iCompletionCode = m_GridRequests[i].m_iCompletionCode;
|
|
m_GridRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
// Copies the result into bLineOfSightIsClear, and clears the request
|
|
EPathServerErrorCode CPathServer::GetLineOfSightResult(TPathHandle handle, bool & bLineOfSightIsClear)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(handle == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(handle != PATH_HANDLE_NULL);
|
|
bLineOfSightIsClear = false;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(u32 i=0; i<MAX_NUM_LOS_REQUESTS; i++)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_hHandle == handle)
|
|
{
|
|
Assert(m_LineOfSightRequests[i].m_iType == ELineOfSight);
|
|
|
|
if(m_LineOfSightRequests[i].m_bComplete)
|
|
{
|
|
bLineOfSightIsClear = m_LineOfSightRequests[i].m_bLineOfSightExists;
|
|
EPathServerErrorCode iCompletionCode = m_LineOfSightRequests[i].m_iCompletionCode;
|
|
|
|
m_LineOfSightRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
bLineOfSightIsClear = false;
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
bLineOfSightIsClear = false;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
// Copies the overall result into bLineOfSightIsClear. Individual lineseg results are then copied into the array of
|
|
// bools pointed to by bLineOfSightResults. This array MUST be enough to accomodate the results (it's recommended
|
|
// that this array be dimensioned to MAX_LINEOFSIGHT_POINTS. The last parameter passed in should equal the number
|
|
// of points in the original LOS request - it is here just for error checking.
|
|
|
|
EPathServerErrorCode CPathServer::GetLineOfSightResult(TPathHandle handle, bool & bLineOfSightIsClear, bool * bLineOfSightResults, int ASSERT_ONLY(iNumPts))
|
|
{
|
|
Assert(iNumPts > 0 && iNumPts < MAX_LINEOFSIGHT_POINTS);
|
|
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(handle == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(handle != PATH_HANDLE_NULL);
|
|
bLineOfSightIsClear = false;
|
|
bLineOfSightResults = NULL;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(u32 i=0; i<MAX_NUM_LOS_REQUESTS; i++)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_hHandle == handle)
|
|
{
|
|
Assert(m_LineOfSightRequests[i].m_iType == ELineOfSight);
|
|
|
|
if(m_LineOfSightRequests[i].m_bComplete)
|
|
{
|
|
bLineOfSightIsClear = m_LineOfSightRequests[i].m_bLineOfSightExists;
|
|
|
|
// Just a safety measure
|
|
Assert(iNumPts == m_LineOfSightRequests[i].m_iNumPts);
|
|
|
|
// Copy in the results for the individual line segments
|
|
for(int p=0; p<m_LineOfSightRequests[i].m_iNumPts; p++)
|
|
{
|
|
bLineOfSightResults[p] = m_LineOfSightRequests[i].m_bLosResults[p];
|
|
}
|
|
|
|
EPathServerErrorCode iCompletionCode = m_LineOfSightRequests[i].m_iCompletionCode;
|
|
|
|
m_LineOfSightRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
bLineOfSightIsClear = false;
|
|
bLineOfSightResults = NULL;
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
bLineOfSightIsClear = false;
|
|
bLineOfSightResults = NULL;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
// Copies the result of the audio properties query into iAudioProperties, and clears the request
|
|
EPathServerErrorCode CPathServer::GetAudioPropertiesResult(TPathHandle handle, u32 & iAudioProperty)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(handle == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(handle != PATH_HANDLE_NULL);
|
|
iAudioProperty = 0;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(u32 i=0; i<MAX_NUM_AUDIO_REQUESTS; i++)
|
|
{
|
|
if(m_AudioRequests[i].m_hHandle == handle)
|
|
{
|
|
// For now, to be on the safe side, if any audio requests are encountered with the
|
|
// wrong m_iType, I'll cancel the request and return PATH_NOT_FOUND. This is better
|
|
// than returning potentially bad data to the audio system, and since this only seems
|
|
// to happen extremely rarely it should be ok (until I track down the real cause!)
|
|
if(m_AudioRequests[i].m_iType != EAudioProperties)
|
|
{
|
|
Assert(false);
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
CancelRequestInternal(handle, false);
|
|
return PATH_NOT_FOUND;
|
|
}
|
|
|
|
if(m_AudioRequests[i].m_bComplete)
|
|
{
|
|
Assert(m_AudioRequests[i].m_iNumAudioProperties > 0);
|
|
iAudioProperty = m_AudioRequests[i].m_iAudioProperties[0];
|
|
|
|
EPathServerErrorCode iCompletionCode = m_AudioRequests[i].m_iCompletionCode;
|
|
m_AudioRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
iAudioProperty = 0;
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
iAudioProperty = 0;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
// Copies the result of the audio properties query into iAudioProperties, and clears the request
|
|
// NOTE : pAudioProperties & pPolyAreas *must* point to arrays of MAX_SURROUNDING_POLYS_FOR_AUDIO_PROPERTIES in size.
|
|
EPathServerErrorCode CPathServer::GetAudioPropertiesResult(TPathHandle handle, u32 & iNumProperties, u32 * pAudioProperties, float * pPolyAreas, Vector3 * pPolyCentroids, u8 * pAdditionalFlags)
|
|
{
|
|
Assert(handle != PATH_HANDLE_NULL);
|
|
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(handle == PATH_HANDLE_NULL)
|
|
{
|
|
iNumProperties = 0;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(u32 i=0; i<MAX_NUM_AUDIO_REQUESTS; i++)
|
|
{
|
|
if(m_AudioRequests[i].m_hHandle == handle)
|
|
{
|
|
Assertf(m_AudioRequests[i].m_iType == EAudioProperties, "Navmesh floodfill warning : was expecting EAudioProperties but found %i", m_AudioRequests[i].m_iType);
|
|
|
|
if(m_AudioRequests[i].m_bComplete)
|
|
{
|
|
iNumProperties = m_AudioRequests[i].m_iNumAudioProperties;
|
|
for(int p=0; p<m_AudioRequests[i].m_iNumAudioProperties; p++)
|
|
{
|
|
pAudioProperties[p] = m_AudioRequests[i].m_iAudioProperties[p];
|
|
pPolyAreas[p] = m_AudioRequests[i].m_fPolyAreas[p];
|
|
pPolyCentroids[p] = m_AudioRequests[i].m_vPolyCentroids[p];
|
|
pAdditionalFlags[p] = m_AudioRequests[i].m_iAdditionalFlags[p];
|
|
}
|
|
|
|
EPathServerErrorCode iCompletionCode = m_AudioRequests[i].m_iCompletionCode;
|
|
m_AudioRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
iNumProperties = 0;
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
iNumProperties = 0;
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
EPathServerErrorCode
|
|
CPathServer::GetClosestCarNodeSearchResultAndClear(const TPathHandle hPath, Vector3 & vClosestCarNode)
|
|
{
|
|
float fTmp;
|
|
return GetClosestFloodFillResultAndClear(hPath, vClosestCarNode, fTmp, CFloodFillRequest::EFindClosestCarNodeFloodFill);
|
|
}
|
|
EPathServerErrorCode
|
|
CPathServer::GetClosestShelteredPolySearchResultAndClear(const TPathHandle hPath, Vector3 & vClosestShelteredPoly)
|
|
{
|
|
float fTmp;
|
|
return GetClosestFloodFillResultAndClear(hPath, vClosestShelteredPoly, fTmp, CFloodFillRequest::EFindClosestShelteredPolyFloodFill);
|
|
}
|
|
EPathServerErrorCode
|
|
CPathServer::GetClosestUnshelteredPolySearchResultAndClear(const TPathHandle hPath, float & closestDistSquared)
|
|
{
|
|
Vector3 vTmp;
|
|
return GetClosestFloodFillResultAndClear(hPath, vTmp, closestDistSquared, CFloodFillRequest::EFindClosestUnshelteredPolyFloodFill);
|
|
}
|
|
EPathServerErrorCode
|
|
CPathServer::GetAreaUnderfootSearchResultAndClear(const TPathHandle hPath, float & fArea)
|
|
{
|
|
Vector3 vTmp;
|
|
return GetClosestFloodFillResultAndClear(hPath, vTmp, fArea, CFloodFillRequest::ECalcAreaUnderfoot);
|
|
}
|
|
EPathServerErrorCode
|
|
CPathServer::GetHasNearbyPavementSearchResultAndClear(const TPathHandle hPath)
|
|
{
|
|
Vector3 vTmp;
|
|
float fTmp;
|
|
return GetClosestFloodFillResultAndClear(hPath, vTmp, fTmp, CFloodFillRequest::EFindNearbyPavementFloodFill);
|
|
}
|
|
|
|
EPathServerErrorCode CPathServer::GetClosestFloodFillResultAndClear(const TPathHandle hPath, Vector3 & vClosest, float & fValue, const CFloodFillRequest::EType ASSERT_ONLY(eType))
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(hPath != PATH_HANDLE_NULL);
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(int i=0; i<MAX_NUM_FLOODFILL_REQUESTS; i++)
|
|
{
|
|
if(m_FloodFillRequests[i].m_hHandle == hPath)
|
|
{
|
|
Assert(m_FloodFillRequests[i].m_iType == EFloodFill);
|
|
Assert(m_FloodFillRequests[i].m_FloodFillType == eType);
|
|
|
|
if(m_FloodFillRequests[i].m_bComplete)
|
|
{
|
|
bool bFoundPosition=false;
|
|
|
|
switch(m_FloodFillRequests[i].m_FloodFillType)
|
|
{
|
|
case CFloodFillRequest::EAudioPropertiesFloodFill:
|
|
{
|
|
Assertf(0, "EAudioPropertiesFloodFill - not implemented yet!");
|
|
break;
|
|
}
|
|
case CFloodFillRequest::EFindClosestCarNodeFloodFill:
|
|
{
|
|
vClosest = m_FloodFillRequests[i].GetFindClosestCarNodeData()->GetCarNodePos();
|
|
bFoundPosition = m_FloodFillRequests[i].GetFindClosestCarNodeData()->m_bFoundCarNode;
|
|
break;
|
|
}
|
|
case CFloodFillRequest::EFindClosestShelteredPolyFloodFill:
|
|
{
|
|
vClosest = m_FloodFillRequests[i].GetFindClosestShelteredPolyData()->GetPos();
|
|
bFoundPosition = m_FloodFillRequests[i].GetFindClosestShelteredPolyData()->m_bFound;
|
|
break;
|
|
}
|
|
case CFloodFillRequest::EFindClosestUnshelteredPolyFloodFill:
|
|
{
|
|
fValue = m_FloodFillRequests[i].GetFindClosestUnshelteredPolyData()->m_fClosestDistSqr;
|
|
break;
|
|
}
|
|
case CFloodFillRequest::ECalcAreaUnderfoot:
|
|
{
|
|
fValue = m_FloodFillRequests[i].GetCalcAreaUnderfootData()->m_fArea;
|
|
bFoundPosition = true;
|
|
break;
|
|
}
|
|
case CFloodFillRequest::EFindClosestPositionOnLand:
|
|
{
|
|
vClosest = m_FloodFillRequests[i].GetFindClosestPositionOnLandData()->GetPos();
|
|
bFoundPosition = m_FloodFillRequests[i].GetFindClosestPositionOnLandData()->m_bFound;
|
|
break;
|
|
}
|
|
case CFloodFillRequest::EFindNearbyPavementFloodFill:
|
|
{
|
|
bFoundPosition = m_FloodFillRequests[i].GetFindNearbyPavementPolyData()->m_bFound;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
Assertf(0, "Unhandled floodfill type!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
EPathServerErrorCode iCompletionCode = m_FloodFillRequests[i].m_iCompletionCode;
|
|
|
|
if(iCompletionCode == PATH_NO_ERROR && !bFoundPosition)
|
|
{
|
|
iCompletionCode = PATH_NOT_FOUND;
|
|
}
|
|
m_FloodFillRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
|
|
// Copies the data into the 'destGrid', and thereafter hPath is invalid.
|
|
EPathServerErrorCode CPathServer::GetClearAreaResultAndClear(const TPathHandle hPath, Vector3 & vResultOrigin)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(hPath != PATH_HANDLE_NULL);
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(int i=0; i<MAX_NUM_CLEARAREA_REQUESTS; i++)
|
|
{
|
|
if(m_ClearAreaRequests[i].m_hHandle == hPath)
|
|
{
|
|
Assert(m_ClearAreaRequests[i].m_iType == EClearArea);
|
|
|
|
if(m_ClearAreaRequests[i].m_bComplete)
|
|
{
|
|
EPathServerErrorCode iCompletionCode = m_ClearAreaRequests[i].m_iCompletionCode;
|
|
vResultOrigin = m_ClearAreaRequests[i].m_vResultOrigin;
|
|
|
|
m_ClearAreaRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
// Copies the data into the 'destGrid', and thereafter hPath is invalid.
|
|
EPathServerErrorCode CPathServer::GetClosestPositionResultAndClear(const TPathHandle hPath, CClosestPositionRequest::TResults & results)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(hPath == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(hPath != PATH_HANDLE_NULL);
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
for(int i=0; i<MAX_NUM_CLOSESTPOSITION_REQUESTS; i++)
|
|
{
|
|
if(m_ClosestPositionRequests[i].m_hHandle == hPath)
|
|
{
|
|
Assert(m_ClosestPositionRequests[i].m_iType == EClosestPosition);
|
|
|
|
if(m_ClosestPositionRequests[i].m_bComplete)
|
|
{
|
|
EPathServerErrorCode iCompletionCode = m_ClosestPositionRequests[i].m_iCompletionCode;
|
|
|
|
sysMemCpy(&results, &m_ClosestPositionRequests[i].m_Results, sizeof(CClosestPositionRequest::TResults));
|
|
|
|
m_ClosestPositionRequests[i].Reset();
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return iCompletionCode;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return PATH_STILL_PENDING;
|
|
}
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return PATH_ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
void CPathServer::InitBeforeMapLoaded(unsigned UNUSED_PARAM(iInitMode))
|
|
{
|
|
Reset(true);
|
|
}
|
|
void CPathServer::InitSession(unsigned UNUSED_PARAM(iInitMode))
|
|
{
|
|
ms_bGameInSession = true;
|
|
Reset();
|
|
}
|
|
void CPathServer::ShutdownSession(unsigned UNUSED_PARAM(iShutdownMode))
|
|
{
|
|
ms_bGameInSession = false;
|
|
Reset();
|
|
}
|
|
|
|
void CPathServer::Reset(const bool bLoadingNewMap)
|
|
{
|
|
#ifdef GTA_ENGINE
|
|
|
|
bool bOldVal = CPathServer::ms_bGameInSession;
|
|
CPathServer::ms_bGameInSession = false;
|
|
|
|
ForceAbortCurrentPathRequest();
|
|
|
|
while(fwPathServer::m_bPathServerThreadIsActive)
|
|
{
|
|
sysIpcYield(0);
|
|
}
|
|
|
|
m_AmbientPedGeneration.ClearAllPedGenBlockingAreas(false, false);
|
|
m_AmbientPedGeneration.Reset();
|
|
#endif
|
|
|
|
// Remove all requested regions
|
|
RemoveAllNavMeshRegions();
|
|
|
|
//LOCK_REQUESTS
|
|
|
|
{
|
|
int r;
|
|
for(r=0; r<MAX_NUM_PATH_REQUESTS; r++)
|
|
{
|
|
m_PathRequests[r].Clear();
|
|
m_PathRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
for(r=0; r<MAX_NUM_GRID_REQUESTS; r++)
|
|
{
|
|
m_GridRequests[r].Clear();
|
|
m_GridRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
for(r=0; r<MAX_NUM_LOS_REQUESTS; r++)
|
|
{
|
|
m_LineOfSightRequests[r].Clear();
|
|
m_LineOfSightRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
for(r=0; r<MAX_NUM_AUDIO_REQUESTS; r++)
|
|
{
|
|
m_AudioRequests[r].Clear();
|
|
m_AudioRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
for(r=0; r<MAX_NUM_FLOODFILL_REQUESTS; r++)
|
|
{
|
|
m_FloodFillRequests[r].Clear();
|
|
m_FloodFillRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
for(r=0; r<MAX_NUM_CLEARAREA_REQUESTS; r++)
|
|
{
|
|
m_ClearAreaRequests[r].Clear();
|
|
m_ClearAreaRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
for(r=0; r<MAX_NUM_CLOSESTPOSITION_REQUESTS; r++)
|
|
{
|
|
m_ClosestPositionRequests[r].Clear();
|
|
m_ClosestPositionRequests[r].m_bSlotEmpty = true;
|
|
}
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
LOCK_NAVMESH_DATA;
|
|
LOCK_IMMEDIATE_DATA;
|
|
LOCK_DYNAMIC_OBJECTS_DATA;
|
|
|
|
if(bLoadingNewMap)
|
|
{
|
|
//---------------------------------
|
|
// Unload all main-map navmeshes
|
|
|
|
for(int domain = 0; domain < kNumNavDomains; domain++)
|
|
{
|
|
if(!fwPathServer::GetIsNavDomainEnabled((aiNavDomain)domain))
|
|
continue;
|
|
|
|
aiNavMeshStore* pStore = m_pNavMeshStores[domain];
|
|
|
|
pStore->ResetBuildID();
|
|
|
|
for(u32 s=0; s<(u32)pStore->GetSize(); s++)
|
|
{
|
|
const strLocalIndex iStreamingIndex = strLocalIndex(s);
|
|
CNavMesh * pNavMesh = pStore->GetMeshByStreamingIndex(iStreamingIndex.Get());
|
|
|
|
// If a mesh is not dynamic, then its a main-map navmesh
|
|
if(pNavMesh && !pNavMesh->GetIsDynamic())
|
|
{
|
|
if(domain == kNavDomainRegular)
|
|
{
|
|
PrepareToUnloadNavMeshDataSetNormal(pNavMesh);
|
|
}
|
|
else
|
|
{
|
|
Assert(domain == kNavDomainHeightMeshes);
|
|
PrepareToUnloadNavMeshDataSetHeightMesh(pNavMesh);
|
|
}
|
|
pStore->ClearRequiredFlag(iStreamingIndex.Get(), STRFLAG_DONTDELETE);
|
|
pStore->StreamingRemove(iStreamingIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------
|
|
// Unload all dynamic navmeshes
|
|
|
|
for(int d=0; d<m_DynamicNavMeshStore.GetNum(); d++)
|
|
{
|
|
CModelInfoNavMeshRef & dynNavInf = m_DynamicNavMeshStore.Get(d);
|
|
dynNavInf.m_iNumRefs = 0;
|
|
|
|
PrepareToUnloadNavMeshDataSetNormal(dynNavInf.m_pBackUpNavmeshCopy);
|
|
|
|
m_pNavMeshStores[kNavDomainRegular]->StreamingRemove(dynNavInf.m_iStreamingIndex);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
m_PathServerThread.Reset();
|
|
ms_iBlockRequestsFlags = 0;
|
|
|
|
m_vOrigin = Vector3(g_fVeryLargeValue, g_fVeryLargeValue, g_fVeryLargeValue);
|
|
|
|
// Reset timers, etc
|
|
m_iLastTimeThreadDidHousekeeping = 0;
|
|
m_iTimeOfLastRequestAndEvictMeshes = 0;
|
|
m_iLastTimeCheckedForStaleRequests = 0;
|
|
m_iLastTimeProcessedPedGeneration = 0;
|
|
|
|
m_iNextHandle = 1;
|
|
m_iNextPathIndexToStartFrom = 0;
|
|
m_iNextGridIndexToStartFrom = 0;
|
|
m_iNextLosIndexToStartFrom = 0;
|
|
m_iNextFloodFillIndexToStartFrom = 0;
|
|
m_iNextClearAreaIndexToStartFrom = 0;
|
|
m_iNextClosestPositionIndexToStartFrom = 0;
|
|
|
|
m_iNumDynamicObjects = 0;
|
|
m_iNumPathRegionSwitches = 0;
|
|
ms_bCurrentlyRemovingAndAddingObjects = false;
|
|
m_iNumDeferredAddDynamicObjects = 0;
|
|
m_iNumScriptBlockingObjects = 0;
|
|
|
|
m_iTimeOfLastCoverPointTimeslice_Millisecs = 0;
|
|
|
|
for(s32 i=0; i<MAX_NUM_SCRIPTED_DYNAMIC_OBJECTS; i++)
|
|
{
|
|
m_ScriptedDynamicObjects[i].m_iScriptObjectHandle = -1;
|
|
m_ScriptedDynamicObjects[i].m_iObjectIndex = DYNAMIC_OBJECT_INDEX_NONE;
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_bGameRunning = false;
|
|
CPathServer::ms_bGameInSession = bOldVal;
|
|
#endif
|
|
}
|
|
|
|
bool CPathServer::CancelRequest(TPathHandle handle)
|
|
{
|
|
return CancelRequestInternal(handle, true);
|
|
}
|
|
|
|
bool CPathServer::CancelRequestInternal(TPathHandle handle, bool bCancelledByClient)
|
|
{
|
|
// We really shouldn't be calling this function with a null handle
|
|
if(handle == PATH_HANDLE_NULL)
|
|
{
|
|
Assert(handle != PATH_HANDLE_NULL);
|
|
return false;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(LOCK_REQUESTS)
|
|
|
|
u32 i;
|
|
for(i=0; i<MAX_NUM_PATH_REQUESTS; i++)
|
|
{
|
|
if(m_PathRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_PathRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_PathRequests[i].m_bRequestPending || m_PathRequests[i].m_bRequestActive || m_PathRequests[i].m_bComplete)
|
|
{
|
|
m_PathRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_PathRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_PathRequests[i].m_iType = EPath;
|
|
|
|
if(m_PathRequests[i].m_bRequestActive)
|
|
{
|
|
m_PathRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<MAX_NUM_GRID_REQUESTS; i++)
|
|
{
|
|
if(m_GridRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_GridRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_GridRequests[i].m_bRequestPending || m_GridRequests[i].m_bRequestActive || m_GridRequests[i].m_bComplete)
|
|
{
|
|
m_GridRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_GridRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_GridRequests[i].m_iType = EGrid;
|
|
|
|
if(m_GridRequests[i].m_bRequestActive)
|
|
{
|
|
m_GridRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<MAX_NUM_LOS_REQUESTS; i++)
|
|
{
|
|
if(m_LineOfSightRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_LineOfSightRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_LineOfSightRequests[i].m_bRequestPending || m_LineOfSightRequests[i].m_bRequestActive || m_LineOfSightRequests[i].m_bComplete)
|
|
{
|
|
m_LineOfSightRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_LineOfSightRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_LineOfSightRequests[i].m_iType = ELineOfSight;
|
|
|
|
if(m_LineOfSightRequests[i].m_bRequestActive)
|
|
{
|
|
m_LineOfSightRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<MAX_NUM_AUDIO_REQUESTS; i++)
|
|
{
|
|
if(m_AudioRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_AudioRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_AudioRequests[i].m_bRequestPending || m_AudioRequests[i].m_bRequestActive || m_AudioRequests[i].m_bComplete)
|
|
{
|
|
m_AudioRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_AudioRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_AudioRequests[i].m_iType = EAudioProperties;
|
|
|
|
if(m_AudioRequests[i].m_bRequestActive)
|
|
{
|
|
m_AudioRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<MAX_NUM_FLOODFILL_REQUESTS; i++)
|
|
{
|
|
if(m_FloodFillRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_FloodFillRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_FloodFillRequests[i].m_bRequestPending || m_FloodFillRequests[i].m_bRequestActive || m_FloodFillRequests[i].m_bComplete)
|
|
{
|
|
m_FloodFillRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_FloodFillRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_FloodFillRequests[i].m_iType = EFloodFill;
|
|
|
|
if(m_FloodFillRequests[i].m_bRequestActive)
|
|
{
|
|
m_FloodFillRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<MAX_NUM_CLEARAREA_REQUESTS; i++)
|
|
{
|
|
if(m_ClearAreaRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_ClearAreaRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_ClearAreaRequests[i].m_bRequestPending || m_ClearAreaRequests[i].m_bRequestActive || m_ClearAreaRequests[i].m_bComplete)
|
|
{
|
|
m_ClearAreaRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_ClearAreaRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_ClearAreaRequests[i].m_iType = EClearArea;
|
|
|
|
if(m_ClearAreaRequests[i].m_bRequestActive)
|
|
{
|
|
m_ClearAreaRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<MAX_NUM_CLOSESTPOSITION_REQUESTS; i++)
|
|
{
|
|
if(m_ClosestPositionRequests[i].m_hHandle == handle)
|
|
{
|
|
sysCriticalSection critSec(m_ClosestPositionRequests[i].m_CriticalSectionToken);
|
|
|
|
if(m_ClosestPositionRequests[i].m_bRequestPending || m_ClosestPositionRequests[i].m_bRequestActive || m_ClosestPositionRequests[i].m_bComplete)
|
|
{
|
|
m_ClosestPositionRequests[i].Reset();
|
|
if(bCancelledByClient)
|
|
m_ClosestPositionRequests[i].m_iCompletionCode = PATH_CANCELLED;
|
|
m_ClosestPositionRequests[i].m_iType = EClosestPosition;
|
|
|
|
if(m_ClosestPositionRequests[i].m_bRequestActive)
|
|
{
|
|
m_ClosestPositionRequests[i].m_bWaitingToAbort = true;
|
|
}
|
|
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
GTA_ENGINE_ONLY(UNLOCK_REQUESTS)
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//***************************************************************************
|
|
// PathServerThreadFunc
|
|
// This is the entry-point for the CPathServerThread's worker thread.
|
|
// The thread spins until a request is made, and then works to process it
|
|
// and fill in the results. This function calls members of the
|
|
// CPathServerThread class which created it.
|
|
//***************************************************************************
|
|
|
|
#ifdef GTA_ENGINE
|
|
DECLARE_THREAD_FUNC(PathServerThreadFunc) {
|
|
#else
|
|
DWORD WINAPI PathServerThreadFunc(LPVOID ptr) {
|
|
#endif
|
|
|
|
TPathServerThreadFuncParams * pParams = (TPathServerThreadFuncParams*) ptr;
|
|
|
|
CPathServerThread * pPathServerThread = pParams->m_pPathServerThread;
|
|
|
|
#ifdef GTA_ENGINE
|
|
RAGETRACE_INITTHREAD("PathServer", 64, 1);
|
|
sysThreadType::AddCurrentThreadType(SYS_THREAD_PATHSERVER);
|
|
#else
|
|
// because memory allocation on pathserver thread has stopped working in navmesh test app
|
|
static char theAllocatorBuffer[sizeof(stockAllocator)];
|
|
sysMemAllocator * theAllocator = (::new (theAllocatorBuffer) stockAllocator);
|
|
sysMemAllocator::SetCurrent(*theAllocator);
|
|
sysMemAllocator::SetMaster(*theAllocator);
|
|
sysMemAllocator::SetContainer(*theAllocator);
|
|
#endif
|
|
|
|
pPathServerThread->Run();
|
|
|
|
#ifndef GTA_ENGINE
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
void
|
|
CPathServer::SuspendThread(void)
|
|
{
|
|
Assert(m_eProcessingMode == EMultiThreaded);
|
|
|
|
// Wait until all the critical sections are free - so the CPathServerThread is not stopped mid-request
|
|
// Try to enter the critical section
|
|
LOCK_NAVMESH_DATA;
|
|
LOCK_IMMEDIATE_DATA;
|
|
LOCK_DYNAMIC_OBJECTS_DATA;
|
|
|
|
Assertf(false, "sysIpcSuspendThread not supported");
|
|
|
|
}
|
|
|
|
void CPathServer::ResumeThread()
|
|
{
|
|
Assert(m_eProcessingMode == EMultiThreaded);
|
|
|
|
// Wait until all the critical sections are free - so the CPathServerThread is not stopped mid-request
|
|
// Try to enter the critical section
|
|
LOCK_NAVMESH_DATA;
|
|
LOCK_IMMEDIATE_DATA;
|
|
LOCK_DYNAMIC_OBJECTS_DATA;
|
|
|
|
Assertf(false, "sysIpcResumeThread not supported");
|
|
|
|
dynamicObjectsCriticalSection.Exit();
|
|
navMeshDataCriticalSection.Exit();
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
bool CPathServer::IsNavMeshLoadedAtPosition(const Vector3 & vPos, aiNavDomain domain)
|
|
{
|
|
TNavMeshIndex iNavMeshIndex = GetNavMeshIndexFromPosition(vPos, domain);
|
|
CNavMesh * pNavMesh = GetNavMeshFromIndex(iNavMeshIndex, domain);
|
|
|
|
return (pNavMesh != NULL);
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
void CPathServer::DeferredDisableClimbAtPosition(const Vector3 & vPos, const bool bNavmeshClimb, const bool bClimbableObject)
|
|
{
|
|
m_PathServerThread.AddDeferredDisableClimbAtPosition(vPos, bNavmeshClimb, bClimbableObject);
|
|
}
|
|
|
|
bool CPathServer::DisableClimbAtPosition(const Vector3 & vPos, const bool bClimbableObject)
|
|
{
|
|
LOCK_NAVMESH_DATA
|
|
|
|
// This function always operates on the regular data set at this point.
|
|
aiNavDomain domain = kNavDomainRegular;
|
|
|
|
TNavMeshIndex iNavMesh = GetNavMeshIndexFromPosition(vPos, domain);
|
|
if(iNavMesh==NAVMESH_NAVMESH_INDEX_NONE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
CNavMesh * pNavMesh = GetNavMeshFromIndex(iNavMesh, domain);
|
|
if(!pNavMesh)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bDisabled = false;
|
|
if(bClimbableObject)
|
|
{
|
|
bDisabled = pNavMesh->DisableSpecialLinksSpanningPosition(vPos, 2.0f);
|
|
}
|
|
else
|
|
{
|
|
bDisabled = pNavMesh->DisableNonStandardAdjacancyAtPosition(vPos, 20.0f);
|
|
}
|
|
|
|
return bDisabled;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef GTA_ENGINE
|
|
static aiNavMeshStoreInterfaceGta s_NavMeshStoreInterface;
|
|
#endif
|
|
|
|
void CPathServer::RegisterStreamingModule(const char* pPathForDatFile)
|
|
{
|
|
m_NavDomainsEnabled[kNavDomainHeightMeshes] = false;
|
|
|
|
if(!pPathForDatFile)
|
|
{
|
|
pPathForDatFile = "common:/data/";
|
|
}
|
|
|
|
// Read the "nav.dat" file
|
|
CNavDatInfo navMeshesInfo, navNodesInfo;
|
|
ReadDatFile(pPathForDatFile, navMeshesInfo, navNodesInfo);
|
|
|
|
if(navMeshesInfo.iMaxMeshIndex == 0)
|
|
{
|
|
Assertf(false, "PathServer::Init() - didn't initialise properly.");
|
|
return;
|
|
}
|
|
|
|
for(int domain = 0; domain < kNumNavDomains; domain++)
|
|
{
|
|
m_pNavMeshStores[domain] = NULL;
|
|
}
|
|
|
|
|
|
#ifdef GTA_ENGINE
|
|
#if __REMAP_NAVMESH_INDICES
|
|
m_pNavMeshStores[kNavDomainRegular] = rage_new aiNavMeshStore(kNavDomainRegular, s_NavMeshStoreInterface, "NavMeshes", NAVMESH_FILE_ID, navMeshesInfo.iNumMeshesInAnyLevel+NAVMESH_INDEX_GAP_BETWEEN_STATIC_AND_DYNAMIC+m_iMaxDynamicNavmeshTypes, 2048, false, __DEFRAG_NAVMESHES);
|
|
#else
|
|
m_pNavMeshStores[kNavDomainRegular] = rage_new aiNavMeshStore(kNavDomainRegular, s_NavMeshStoreInterface, "NavMeshes", NAVMESH_FILE_ID, NAVMESH_INDEX_FINAL_DYNAMIC+1, 2048, false, __DEFRAG_NAVMESHES);
|
|
#endif // __REMAP_NAVMESH_INDICES
|
|
#else
|
|
m_pNavMeshStores[kNavDomainRegular] = rage_new aiNavMeshStore("NavMeshes", NAVMESH_FILE_ID, navMeshesInfo.iNumMeshesInAnyLevel+NAVMESH_INDEX_GAP_BETWEEN_STATIC_AND_DYNAMIC+m_iMaxDynamicNavmeshTypes, false);
|
|
#endif
|
|
|
|
// At this point, fwConfigManager should have been initialized already, so
|
|
// we can just force the pools to be allocated right here, unlike what we do for
|
|
// the statically constructed fwAssetStore objects (g_IplStore, etc). It needs
|
|
// to be done before the aiNavMeshStore::Init() call.
|
|
#ifdef GTA_ENGINE
|
|
m_pNavMeshStores[kNavDomainRegular]->FinalizeSize();
|
|
#endif
|
|
|
|
m_pNavMeshStores[kNavDomainRegular]->SetMaxMeshIndex(navMeshesInfo.iMaxMeshIndex);
|
|
m_pNavMeshStores[kNavDomainRegular]->SetMeshSize(navMeshesInfo.fMeshSize);
|
|
m_pNavMeshStores[kNavDomainRegular]->SetNumMeshesInX(navMeshesInfo.iNumMeshesInX);
|
|
m_pNavMeshStores[kNavDomainRegular]->SetNumMeshesInY(navMeshesInfo.iNumMeshesInY);
|
|
m_pNavMeshStores[kNavDomainRegular]->SetNumMeshesInAnyLevel(navMeshesInfo.iNumMeshesInAnyLevel);
|
|
m_pNavMeshStores[kNavDomainRegular]->SetNumSectorsPerMesh(navMeshesInfo.iSectorsPerMesh);
|
|
m_pNavMeshStores[kNavDomainRegular]->Init();
|
|
|
|
m_pNavMeshStores[kNavDomainRegular]->CreateNameHashTable();
|
|
|
|
// To conserve memory, we don't reserve space for a bunch of heightmeshes here. It should be possible
|
|
// to use the pool name "HeightMeshes" to specify the size of the aiNavMeshStore from the configuration file.
|
|
if(fwPathServer::GetIsNavDomainEnabled(kNavDomainHeightMeshes))
|
|
{
|
|
const int kDefaultMaxHeightMeshes = 1;
|
|
#ifdef GTA_ENGINE
|
|
m_pNavMeshStores[kNavDomainHeightMeshes] = rage_new aiNavMeshStore(kNavDomainHeightMeshes, s_NavMeshStoreInterface, "HeightMeshes", HEIGHTMESH_FILE_ID, kDefaultMaxHeightMeshes, 2048, false, __DEFRAG_NAVMESHES);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->FinalizeSize();
|
|
#else
|
|
m_pNavMeshStores[kNavDomainHeightMeshes] = rage_new aiNavMeshStore("HeightMeshes", HEIGHTMESH_FILE_ID, kDefaultMaxHeightMeshes, false);
|
|
#endif
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->SetMaxMeshIndex(navMeshesInfo.iMaxMeshIndex);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->SetMeshSize(navMeshesInfo.fMeshSize);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->SetNumMeshesInX(navMeshesInfo.iNumMeshesInX);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->SetNumMeshesInY(navMeshesInfo.iNumMeshesInY);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->SetNumMeshesInAnyLevel(navMeshesInfo.iNumMeshesInAnyLevel);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->SetNumSectorsPerMesh(navMeshesInfo.iSectorsPerMesh);
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->Init();
|
|
|
|
m_pNavMeshStores[kNavDomainHeightMeshes]->CreateNameHashTable();
|
|
}
|
|
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
m_pNavNodesStore = rage_new aiNavNodesStore(s_NavMeshStoreInterface, "NavNodes", NAVNODES_FILE_ID, navNodesInfo.iNumMeshesInAnyLevel, false);
|
|
|
|
#ifdef GTA_ENGINE
|
|
m_pNavNodesStore->FinalizeSize();
|
|
#endif
|
|
|
|
m_pNavNodesStore->SetMaxMeshIndex(navNodesInfo.iMaxMeshIndex);
|
|
m_pNavNodesStore->SetMeshSize(navNodesInfo.fMeshSize);
|
|
m_pNavNodesStore->SetNumMeshesInX(navNodesInfo.iNumMeshesInX);
|
|
m_pNavNodesStore->SetNumMeshesInY(navNodesInfo.iNumMeshesInY);
|
|
m_pNavNodesStore->SetNumMeshesInAnyLevel(navNodesInfo.iNumMeshesInAnyLevel);
|
|
m_pNavNodesStore->SetNumSectorsPerMesh(navNodesInfo.iSectorsPerMesh);
|
|
m_pNavNodesStore->Init();
|
|
#endif
|
|
|
|
#ifdef GTA_ENGINE
|
|
#if __BANK
|
|
if(PARAM_nonav.Get())
|
|
ms_bStreamingDisabled = true;
|
|
#endif
|
|
#if NAVMESH_EXPORT
|
|
if(CNavMeshDataExporter::WillExportCollision())
|
|
ms_bStreamingDisabled = true;
|
|
#endif
|
|
|
|
if(!ms_bStreamingDisabled)
|
|
{
|
|
for(int domain = 0; domain < kNumNavDomains; domain++)
|
|
{
|
|
if(!fwPathServer::GetIsNavDomainEnabled((aiNavDomain)domain))
|
|
continue;
|
|
|
|
if(m_pNavMeshStores[domain])
|
|
{
|
|
strStreamingEngine::GetInfo().GetModuleMgr().AddModule(m_pNavMeshStores[domain]);
|
|
#if USE_PAGED_POOLS_FOR_STREAMING
|
|
m_pNavMeshStores[domain]->AllocateSlot(NAVMESH_INDEX_FINAL_DYNAMIC);
|
|
#endif // USE_PAGED_POOLS_FOR_STREAMING
|
|
}
|
|
}
|
|
|
|
#if __HIERARCHICAL_NODES_ENABLED
|
|
strStreamingEngine::GetInfo().GetModuleMgr().AddModule(m_pNavNodesStore);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
#endif // GTA_ENGINE
|
|
}
|
|
|
|
|
|
#if !__FINAL && __BANK
|
|
|
|
void DbgWriteFile(fiStream * pStream, const char * pTxt, ...)
|
|
{
|
|
char tmp[256];
|
|
va_list argptr;
|
|
va_start(argptr, pTxt);
|
|
vformatf(tmp, 256, pTxt, argptr);
|
|
pStream->Write(tmp, istrlen(tmp));
|
|
}
|
|
|
|
bool
|
|
CPathServer::OutputPathRequestProblem(u32 iPathRequestIndex, const char * pFilename)
|
|
{
|
|
CPathRequest * pRequest = &m_PathRequests[iPathRequestIndex];
|
|
return OutputPathRequestProblem(pRequest, pFilename);
|
|
}
|
|
|
|
bool
|
|
CPathServer::OutputPathRequestProblem(CPathRequest * pRequest, const char * pFilename)
|
|
{
|
|
// Wait until dynamic objects are available
|
|
#ifdef GTA_ENGINE
|
|
sysCriticalSection dynamicObjectsCriticalSection(m_DynamicObjectsCriticalSectionToken);
|
|
#else
|
|
EnterCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
#endif
|
|
|
|
char filename[512] = { 0 };
|
|
|
|
if(!pFilename)
|
|
{
|
|
bool bOk = BANKMGR.OpenFile(filename, 512, "*.prob", true, "PathFinding Problem Files");
|
|
if(!bOk)
|
|
{
|
|
#ifndef GTA_ENGINE
|
|
LeaveCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
#endif
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strcpy(filename, pFilename);
|
|
}
|
|
|
|
fiStream * pStream = fiStream::Create(filename);
|
|
|
|
if(!pStream)
|
|
{
|
|
Printf("Couldn't open file \"%s\" to write.\n", filename);
|
|
|
|
#ifndef GTA_ENGINE
|
|
LeaveCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
int o;
|
|
|
|
//******************************************************************************
|
|
// Dump out info about the path
|
|
//******************************************************************************
|
|
|
|
s32 iWander = (s32)pRequest->m_bWander;
|
|
s32 iFlee = (s32)pRequest->m_bFleeTarget;
|
|
|
|
int iStartNavX = CPathServerExtents::GetWorldToSectorX(pRequest->m_vUnadjustedStartPoint.x);
|
|
int iStartNavY = CPathServerExtents::GetWorldToSectorY(pRequest->m_vUnadjustedStartPoint.y);
|
|
int iEndNavX = CPathServerExtents::GetWorldToSectorX(pRequest->m_vUnadjustedEndPoint.x);
|
|
int iEndNavY = CPathServerExtents::GetWorldToSectorY(pRequest->m_vUnadjustedEndPoint.y);
|
|
|
|
static int iFileVersion = 2;
|
|
DbgWriteFile(pStream, "PATHFINDING PROBLEM FILE VERSION %i\n\n\n", iFileVersion);
|
|
|
|
DbgWriteFile(pStream, "PATH DETAILS:\n\n");
|
|
DbgWriteFile(pStream, "*STARTPOS %.3f %.3f %.3f\n", pRequest->m_vUnadjustedStartPoint.x, pRequest->m_vUnadjustedStartPoint.y, pRequest->m_vUnadjustedStartPoint.z);
|
|
DbgWriteFile(pStream, "START NAVMESH:[%i,%i]\n", iStartNavX, iStartNavY);
|
|
DbgWriteFile(pStream, "*ENDPOS %.3f %.3f %.3f\n", pRequest->m_vUnadjustedEndPoint.x, pRequest->m_vUnadjustedEndPoint.y, pRequest->m_vUnadjustedEndPoint.z);
|
|
if(!iWander && !iFlee)
|
|
DbgWriteFile(pStream, "END NAVMESH:[%i,%i]\n", iEndNavX, iEndNavY);
|
|
DbgWriteFile(pStream, "*REFVEC %.3f %.3f %.3f\n", pRequest->m_vReferenceVector.x, pRequest->m_vReferenceVector.y, pRequest->m_vReferenceVector.z);
|
|
DbgWriteFile(pStream, "*REFDIST %.3f\n", pRequest->m_fReferenceDistance);
|
|
DbgWriteFile(pStream, "*COMPLETION_RADIUS %.3f\n", pRequest->m_fPathSearchCompletionRadius);
|
|
|
|
|
|
|
|
DbgWriteFile(pStream, "*m_bPreferPavements = %s\n", pRequest->m_bPreferPavements ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bNeverLeavePavements = %s\n", pRequest->m_bNeverLeavePavements ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bNeverClimbOverStuff = %s\n", pRequest->m_bNeverClimbOverStuff ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bNeverDropFromHeight = %s\n", pRequest->m_bNeverDropFromHeight ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bMayUseFatalDrops = %s\n", pRequest->m_bMayUseFatalDrops ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bNeverUseLadders = %s\n", pRequest->m_bNeverUseLadders ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bAllowFirstPointToBeInsideDynamicObject = %s\n", pRequest->m_bAllowFirstPointToBeInsideDynamicObject ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bFleeTarget = %s\n", pRequest->m_bFleeTarget ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bWander = %s\n", pRequest->m_bWander ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bNeverEnterWater = %s\n", pRequest->m_bNeverEnterWater ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bClimbObjects = %s\n", pRequest->m_bClimbObjects ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bPushObjects = %s\n", pRequest->m_bPushObjects ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bDontAvoidDynamicObjects = %s\n", pRequest->m_bDontAvoidDynamicObjects ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bEndPointWasAdjusted = %s\n", pRequest->m_bEndPointWasAdjusted ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bProblemPathStartsAndEndsOnSamePoly = %s\n", pRequest->m_bProblemPathStartsAndEndsOnSamePoly ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bPreferDownHill = %s\n", pRequest->m_bPreferDownHill ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bGoAsFarAsPossibleIfNavMeshNotLoaded = %s\n", pRequest->m_bGoAsFarAsPossibleIfNavMeshNotLoaded ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bSmoothSharpCorners = %s\n", pRequest->m_bSmoothSharpCorners ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bDoPostProcessToPreserveSlopeInfo = %s\n", pRequest->m_bDoPostProcessToPreserveSlopeInfo ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bScriptedRoute = %s\n", pRequest->m_bScriptedRoute ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bReduceObjectBoundingBoxes = %s\n", pRequest->m_bReduceObjectBoundingBoxes ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bUseLargerSearchExtents = %s\n", pRequest->m_bUseLargerSearchExtents ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bDontLimitSearchExtents = %s\n", pRequest->m_bDontLimitSearchExtents ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bDynamicNavMeshRoute = %s\n", pRequest->m_bDynamicNavMeshRoute ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bIfStartNotOnPavementAllowDropsAndClimbs = %s\n", pRequest->m_bIfStartNotOnPavementAllowDropsAndClimbs ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bNeverStartInWater = %s\n", pRequest->m_bNeverStartInWater ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bIgnoreNonSignificantObjects = %s\n", pRequest->m_bIgnoreNonSignificantObjects ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bIgnoreTypeVehicles = %s\n", pRequest->m_bIgnoreTypeVehicles ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bIgnoreTypeObjects = %s\n", pRequest->m_bIgnoreTypeObjects ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bFleeNeverEndInWater = %s\n", pRequest->m_bFleeNeverEndInWater ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bConsiderFreeSpaceAroundPoly = %s\n", pRequest->m_bConsiderFreeSpaceAroundPoly ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bUseMaxSlopeNavigable = %s\n", pRequest->m_bUseMaxSlopeNavigable ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bRandomisePoints = %s\n", pRequest->m_bRandomisePoints ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bUseDirectionalCover = %s\n", pRequest->m_bUseDirectionalCover ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bFavourEnclosedSpaces = %s\n", pRequest->m_bFavourEnclosedSpaces ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bUseVariableEntityRadius = %s\n", pRequest->m_bUseVariableEntityRadius ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bAvoidPotentialExplosions = %s\n", pRequest->m_bAvoidPotentialExplosions ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bAvoidTearGas = %s\n", pRequest->m_bAvoidTearGas ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bAllowToNavigateUpSteepPolygons = %s\n", pRequest->m_bAllowToNavigateUpSteepPolygons ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bAllowToPushVehicleDoorsClosed = %s\n", pRequest->m_bAllowToPushVehicleDoorsClosed ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bMissionPed = %s\n", pRequest->m_bMissionPed ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bEnsureLosBeforeEnding = %s\n", pRequest->m_bEnsureLosBeforeEnding ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bExpandStartEndPolyTessellationRadius = %s\n", pRequest->m_bExpandStartEndPolyTessellationRadius ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bPullFromEdgeExtra = %s\n", pRequest->m_bPullFromEdgeExtra ? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bSofterFleeHeuristics = %s\n", pRequest->m_bSofterFleeHeuristics? "true" : "false");
|
|
DbgWriteFile(pStream, "*m_bAvoidTrainTracks = %s\n", pRequest->m_bAvoidTrainTracks? "true" : "false");
|
|
|
|
// Write movement costs
|
|
DbgWriteFile(pStream, "*m_fClimbHighPenalty = %.3f\n", pRequest->m_MovementCosts.m_fClimbHighPenalty);
|
|
DbgWriteFile(pStream, "*m_fClimbLowPenalty = %.3f\n", pRequest->m_MovementCosts.m_fClimbLowPenalty);
|
|
DbgWriteFile(pStream, "*m_fDropDownPenalty = %.3f\n", pRequest->m_MovementCosts.m_fDropDownPenalty);
|
|
DbgWriteFile(pStream, "*m_fDropDownPenaltyPerMetre = %.3f\n", pRequest->m_MovementCosts.m_fDropDownPenaltyPerMetre);
|
|
DbgWriteFile(pStream, "*m_fClimbLadderPenalty = %.3f\n", pRequest->m_MovementCosts.m_fClimbLadderPenalty);
|
|
DbgWriteFile(pStream, "*m_fClimbLadderPenaltyPerMetre = %.3f\n", pRequest->m_MovementCosts.m_fClimbLadderPenaltyPerMetre);
|
|
DbgWriteFile(pStream, "*m_fEnterWaterPenalty = %.3f\n", pRequest->m_MovementCosts.m_fEnterWaterPenalty);
|
|
DbgWriteFile(pStream, "*m_fLeaveWaterPenalty = %.3f\n", pRequest->m_MovementCosts.m_fLeaveWaterPenalty);
|
|
DbgWriteFile(pStream, "*m_fBeInWaterPenalty = %.3f\n", pRequest->m_MovementCosts.m_fBeInWaterPenalty);
|
|
DbgWriteFile(pStream, "*m_fClimbObjectPenaltyPerMetre = %.3f\n", pRequest->m_MovementCosts.m_fClimbObjectPenaltyPerMetre);
|
|
DbgWriteFile(pStream, "*m_fWanderPenaltyForZeroPedDensity = %.3f\n", pRequest->m_MovementCosts.m_fWanderPenaltyForZeroPedDensity);
|
|
DbgWriteFile(pStream, "*m_fMoveOntoSteepSurfacePenalty = %.3f\n", pRequest->m_MovementCosts.m_fMoveOntoSteepSurfacePenalty);
|
|
DbgWriteFile(pStream, "*m_fPenaltyForNoDirectionalCover = %.3f\n", pRequest->m_MovementCosts.m_fPenaltyForNoDirectionalCover);
|
|
DbgWriteFile(pStream, "*m_fPenaltyForFavourCoverPerUnsetBit = %.3f\n", pRequest->m_MovementCosts.m_fPenaltyForFavourCoverPerUnsetBit);
|
|
DbgWriteFile(pStream, "*m_iPenaltyForMovingToLowerPedDensity = %i\n", pRequest->m_MovementCosts.m_iPenaltyForMovingToLowerPedDensity);
|
|
DbgWriteFile(pStream, "*m_iPenaltyPerPolyFreeSpaceDifference = %i\n", pRequest->m_MovementCosts.m_iPenaltyPerPolyFreeSpaceDifference);
|
|
DbgWriteFile(pStream, "*m_fAvoidTrainTracksPenalty = %.3f\n", pRequest->m_MovementCosts.m_fAvoidTrainTracksPenalty);
|
|
|
|
|
|
DbgWriteFile(pStream, "*RANDSEED %i\n", pRequest->m_iPedRandomSeed);
|
|
DbgWriteFile(pStream, "*ENTITY_RADIUS %.3f\n", pRequest->m_fEntityRadius);
|
|
|
|
DbgWriteFile(pStream, "*NUM_INCLUDE %i\n", pRequest->m_iNumIncludeObjects);
|
|
for(o=0; o<pRequest->m_iNumIncludeObjects; o++)
|
|
DbgWriteFile(pStream, "*OBJ %i\n", pRequest->m_IncludeObjects[o]);
|
|
DbgWriteFile(pStream, "*NUM_EXCLUDE %i\n", pRequest->m_iNumExcludeObjects);
|
|
for(o=0; o<pRequest->m_iNumExcludeObjects; o++)
|
|
DbgWriteFile(pStream, "*OBJ %i\n", pRequest->m_ExcludeObjects[o]);
|
|
|
|
DbgWriteFile(pStream, "*NUM_INFLUENCE_SPHERES %i\n", pRequest->m_iNumInfluenceSpheres);
|
|
for(o=0; o<pRequest->m_iNumInfluenceSpheres; o++)
|
|
{
|
|
DbgWriteFile(pStream, "*SPHERE %.3f %.3f %.3f %.3f %.3f %.3f\n",
|
|
pRequest->m_InfluenceSpheres[o].GetOrigin().x, pRequest->m_InfluenceSpheres[o].GetOrigin().y, pRequest->m_InfluenceSpheres[o].GetOrigin().z,
|
|
pRequest->m_InfluenceSpheres[o].GetRadius(), pRequest->m_InfluenceSpheres[o].GetInnerWeighting(), pRequest->m_InfluenceSpheres[o].GetOuterWeighting() );
|
|
}
|
|
|
|
DbgWriteFile(pStream, "\n\n\n");
|
|
DbgWriteFile(pStream, "DYNAMIC OBJECTS:\n\n");
|
|
|
|
//******************************************************************************
|
|
// Dump out the details of all the dynamic objects
|
|
//******************************************************************************
|
|
|
|
TDynamicObject * pObj = m_PathServerThread.m_pFirstDynamicObject;
|
|
int iNumObjs = 0;
|
|
while(pObj)
|
|
{
|
|
iNumObjs++;
|
|
pObj = pObj->m_pNext;
|
|
}
|
|
|
|
DbgWriteFile(pStream, "*NUMOBJECTS %i\n\n", iNumObjs);
|
|
|
|
pObj = m_PathServerThread.m_pFirstDynamicObject;
|
|
|
|
while(pObj)
|
|
{
|
|
DbgWriteFile(pStream, "*OBJECTPOS %f %f %f\n", pObj->m_Bounds.GetOrigin().x, pObj->m_Bounds.GetOrigin().y, pObj->m_Bounds.GetOrigin().z);
|
|
DbgWriteFile(pStream, "*CORNERS %f %f %f %f %f %f %f %f %f %f %f %f\n",
|
|
pObj->m_vVertices[0].x, pObj->m_vVertices[0].y, pObj->m_Bounds.GetOrigin().z,
|
|
pObj->m_vVertices[1].x, pObj->m_vVertices[1].y, pObj->m_Bounds.GetOrigin().z,
|
|
pObj->m_vVertices[2].x, pObj->m_vVertices[2].y, pObj->m_Bounds.GetOrigin().z,
|
|
pObj->m_vVertices[3].x, pObj->m_vVertices[3].y, pObj->m_Bounds.GetOrigin().z
|
|
);
|
|
DbgWriteFile(pStream, "*EDGEPLANES %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f\n",
|
|
pObj->m_Bounds.m_vEdgePlaneNormals[0].x, pObj->m_Bounds.m_vEdgePlaneNormals[0].y, pObj->m_Bounds.m_vEdgePlaneNormals[0].z, pObj->m_Bounds.m_vEdgePlaneNormals[0].w,
|
|
pObj->m_Bounds.m_vEdgePlaneNormals[1].x, pObj->m_Bounds.m_vEdgePlaneNormals[1].y, pObj->m_Bounds.m_vEdgePlaneNormals[1].z, pObj->m_Bounds.m_vEdgePlaneNormals[1].w,
|
|
pObj->m_Bounds.m_vEdgePlaneNormals[2].x, pObj->m_Bounds.m_vEdgePlaneNormals[2].y, pObj->m_Bounds.m_vEdgePlaneNormals[2].z, pObj->m_Bounds.m_vEdgePlaneNormals[2].w,
|
|
pObj->m_Bounds.m_vEdgePlaneNormals[3].x, pObj->m_Bounds.m_vEdgePlaneNormals[3].y, pObj->m_Bounds.m_vEdgePlaneNormals[3].z, pObj->m_Bounds.m_vEdgePlaneNormals[3].w,
|
|
pObj->m_Bounds.m_vEdgePlaneNormals[4].x, pObj->m_Bounds.m_vEdgePlaneNormals[4].y, pObj->m_Bounds.m_vEdgePlaneNormals[4].z, pObj->m_Bounds.m_vEdgePlaneNormals[4].w,
|
|
pObj->m_Bounds.m_vEdgePlaneNormals[5].x, pObj->m_Bounds.m_vEdgePlaneNormals[5].y, pObj->m_Bounds.m_vEdgePlaneNormals[5].z, pObj->m_Bounds.m_vEdgePlaneNormals[5].w
|
|
);
|
|
|
|
Vector3 vSegmentPlanes[4];
|
|
pObj->m_Bounds.CalculateSegmentPlanes(vSegmentPlanes, pObj->m_vVertices);
|
|
|
|
DbgWriteFile(pStream, "*SEGPLANES %f %f %f %f %f %f %f %f %f %f %f %f\n",
|
|
vSegmentPlanes[0].x, vSegmentPlanes[0].y, vSegmentPlanes[0].z,
|
|
vSegmentPlanes[1].x, vSegmentPlanes[1].y, vSegmentPlanes[1].z,
|
|
vSegmentPlanes[2].x, vSegmentPlanes[2].y, vSegmentPlanes[2].z,
|
|
vSegmentPlanes[3].x, vSegmentPlanes[3].y, vSegmentPlanes[3].z
|
|
);
|
|
DbgWriteFile(pStream, "*TOPZ %f\n", pObj->m_Bounds.GetTopZ());
|
|
DbgWriteFile(pStream, "*BOTTOMZ %f\n", pObj->m_Bounds.GetBottomZ());
|
|
//DbgWriteFile(pStream, "*BOUNDINGRADIUS %f\n", pObj->m_Bounds.m_fBoundingRadius);
|
|
DbgWriteFile(pStream, "*MINMAX %i %i %i %i %i %i \n", pObj->m_MinMax.m_iMinX, pObj->m_MinMax.m_iMinY, pObj->m_MinMax.m_iMinZ, pObj->m_MinMax.m_iMaxX, pObj->m_MinMax.m_iMaxY, pObj->m_MinMax.m_iMaxZ);
|
|
DbgWriteFile(pStream, "*OPENABLE %i\n", pObj->m_bIsOpenable);
|
|
DbgWriteFile(pStream, "*CLIMBABLE %i\n", pObj->m_bIsClimbable);
|
|
DbgWriteFile(pStream, "*PUSHABLE %i\n", pObj->m_bIsPushable);
|
|
|
|
DbgWriteFile(pStream, "\n");
|
|
|
|
#if __ASSERT
|
|
// Error check the bounds of this entity
|
|
static const float fPlaneEps = 0.05f;
|
|
for(int pl=0; pl<4; pl++)
|
|
{
|
|
Vector3 vCorner(pObj->m_vVertices[pl].x, pObj->m_vVertices[pl].y, pObj->m_Bounds.GetOrigin().z);
|
|
float fPlaneDist = pObj->m_Bounds.m_vEdgePlaneNormals[pl].Dot3(vCorner) + pObj->m_Bounds.m_vEdgePlaneNormals[pl].w;
|
|
Assert(Abs(fPlaneDist) < fPlaneEps);
|
|
}
|
|
#endif
|
|
|
|
pObj = pObj->m_pNext;
|
|
}
|
|
|
|
DbgWriteFile(pStream, "\n\n\n\n");
|
|
|
|
pStream->Close();
|
|
|
|
|
|
#ifndef GTA_ENGINE
|
|
LeaveCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifndef GTA_ENGINE
|
|
|
|
bool CPathServer::LoadPathRequestProblem()
|
|
{
|
|
char filename[512] = { 0 };
|
|
|
|
bool bOk = BANKMGR.OpenFile(filename, 512, "*.prob", false, "PathFinding Problem Files");
|
|
if(!bOk)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return LoadPathRequestProblem(filename);
|
|
}
|
|
|
|
bool ScanTF(char *& pBuffer, const char * pToken)
|
|
{
|
|
char tokenBuffer[256];
|
|
sprintf(tokenBuffer, "%s = %%s", pToken);
|
|
|
|
char tmp[256];
|
|
|
|
while(*pBuffer != '*') pBuffer++;
|
|
int n = sscanf(pBuffer, tokenBuffer, tmp);
|
|
pBuffer++;
|
|
if(n != 1)
|
|
{
|
|
Assert(false);
|
|
return false;
|
|
}
|
|
return (stricmp(tmp, "true")==0) ? true : false;
|
|
}
|
|
|
|
bool CPathServer::LoadPathRequestProblem(char * filename)
|
|
{
|
|
// Wait until dynamic objects are available
|
|
EnterCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
|
|
FILE * pFile = fopen(filename, "rt");
|
|
if(!pFile)
|
|
{
|
|
LeaveCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
return false;
|
|
}
|
|
|
|
fseek(pFile, 0, SEEK_END);
|
|
int iSize = ftell(pFile);
|
|
fseek(pFile, 0, SEEK_SET);
|
|
char * buffer = rage_new char[iSize];
|
|
int iNumBytes = fread(buffer, 1, iSize, pFile);
|
|
buffer[iNumBytes] = 0;
|
|
fclose(pFile);
|
|
|
|
char * ptr = buffer;
|
|
|
|
int o;
|
|
u32 iRandSeed = 0;
|
|
//float fCompletionRadius = 0.0f;
|
|
int iNumObjs = 0;
|
|
|
|
// Just choose slot 0 for now..
|
|
CPathRequest * pRequest = &m_PathRequests[0];
|
|
|
|
// Skip to path details section
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "STARTPOS %f %f %f", &pRequest->m_vUnadjustedStartPoint.x, &pRequest->m_vUnadjustedStartPoint.y, &pRequest->m_vUnadjustedStartPoint.z);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "ENDPOS %f %f %f", &pRequest->m_vUnadjustedEndPoint.x, &pRequest->m_vUnadjustedEndPoint.y, &pRequest->m_vUnadjustedEndPoint.z);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "REFVEC %f %f %f", &pRequest->m_vReferenceVector.x, &pRequest->m_vReferenceVector.y, &pRequest->m_vReferenceVector.z);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "REFDIST %f", &pRequest->m_fReferenceDistance);
|
|
pRequest->m_fInitialReferenceDistance = pRequest->m_fReferenceDistance;
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "COMPLETION_RADIUS %f", &pRequest->m_fPathSearchCompletionRadius);
|
|
|
|
|
|
|
|
|
|
|
|
pRequest->m_bPreferPavements = ScanTF(ptr, "*m_bPreferPavements");
|
|
pRequest->m_bNeverLeavePavements = ScanTF(ptr, "*m_bNeverLeavePavements");
|
|
pRequest->m_bNeverClimbOverStuff = ScanTF(ptr, "*m_bNeverClimbOverStuff");
|
|
pRequest->m_bNeverDropFromHeight = ScanTF(ptr, "*m_bNeverDropFromHeight");
|
|
pRequest->m_bMayUseFatalDrops = ScanTF(ptr, "*m_bMayUseFatalDrops");
|
|
pRequest->m_bNeverUseLadders = ScanTF(ptr, "*m_bNeverUseLadders");
|
|
pRequest->m_bAllowFirstPointToBeInsideDynamicObject = ScanTF(ptr, "*m_bAllowFirstPointToBeInsideDynamicObject");
|
|
pRequest->m_bFleeTarget = ScanTF(ptr, "*m_bFleeTarget");
|
|
pRequest->m_bWander = ScanTF(ptr, "*m_bWander");
|
|
pRequest->m_bNeverEnterWater = ScanTF(ptr, "*m_bNeverEnterWater");
|
|
pRequest->m_bClimbObjects = ScanTF(ptr, "*m_bClimbObjects");
|
|
pRequest->m_bPushObjects = ScanTF(ptr, "*m_bPushObjects");
|
|
pRequest->m_bDontAvoidDynamicObjects = ScanTF(ptr, "*m_bDontAvoidDynamicObjects");
|
|
pRequest->m_bEndPointWasAdjusted = ScanTF(ptr, "*m_bEndPointWasAdjusted");
|
|
pRequest->m_bProblemPathStartsAndEndsOnSamePoly = ScanTF(ptr, "*m_bProblemPathStartsAndEndsOnSamePoly");
|
|
pRequest->m_bPreferDownHill = ScanTF(ptr, "*m_bPreferDownHill");
|
|
pRequest->m_bGoAsFarAsPossibleIfNavMeshNotLoaded = ScanTF(ptr, "*m_bGoAsFarAsPossibleIfNavMeshNotLoaded");
|
|
pRequest->m_bSmoothSharpCorners = ScanTF(ptr, "*m_bSmoothSharpCorners");
|
|
pRequest->m_bDoPostProcessToPreserveSlopeInfo = ScanTF(ptr, "*m_bDoPostProcessToPreserveSlopeInfo");
|
|
pRequest->m_bScriptedRoute = ScanTF(ptr, "*m_bScriptedRoute");
|
|
pRequest->m_bReduceObjectBoundingBoxes = ScanTF(ptr, "*m_bReduceObjectBoundingBoxes");
|
|
pRequest->m_bUseLargerSearchExtents = ScanTF(ptr, "*m_bUseLargerSearchExtents");
|
|
pRequest->m_bDontLimitSearchExtents = ScanTF(ptr, "*m_bDontLimitSearchExtents");
|
|
pRequest->m_bDynamicNavMeshRoute = ScanTF(ptr, "*m_bDynamicNavMeshRoute");
|
|
pRequest->m_bIfStartNotOnPavementAllowDropsAndClimbs = ScanTF(ptr, "*m_bIfStartNotOnPavementAllowDropsAndClimbs");
|
|
pRequest->m_bNeverStartInWater = ScanTF(ptr, "*m_bNeverStartInWater");
|
|
pRequest->m_bIgnoreNonSignificantObjects = ScanTF(ptr, "*m_bIgnoreNonSignificantObjects");
|
|
pRequest->m_bIgnoreTypeVehicles = ScanTF(ptr, "*m_bIgnoreTypeVehicles");
|
|
pRequest->m_bIgnoreTypeObjects = ScanTF(ptr, "*m_bIgnoreTypeObjects");
|
|
pRequest->m_bFleeNeverEndInWater = ScanTF(ptr, "*m_bFleeNeverEndInWater");
|
|
pRequest->m_bConsiderFreeSpaceAroundPoly = ScanTF(ptr, "*m_bConsiderFreeSpaceAroundPoly");
|
|
pRequest->m_bUseMaxSlopeNavigable = ScanTF(ptr, "*m_bUseMaxSlopeNavigable");
|
|
pRequest->m_bRandomisePoints = ScanTF(ptr, "*m_bRandomisePoints");
|
|
pRequest->m_bUseDirectionalCover = ScanTF(ptr, "*m_bUseDirectionalCover");
|
|
pRequest->m_bFavourEnclosedSpaces = ScanTF(ptr, "*m_bFavourEnclosedSpaces");
|
|
pRequest->m_bUseVariableEntityRadius = ScanTF(ptr, "*m_bUseVariableEntityRadius");
|
|
pRequest->m_bAvoidPotentialExplosions = ScanTF(ptr, "*m_bAvoidPotentialExplosions");
|
|
pRequest->m_bAvoidTearGas = ScanTF(ptr, "*m_bAvoidTearGas");
|
|
pRequest->m_bAllowToNavigateUpSteepPolygons = ScanTF(ptr, "*m_bAllowToNavigateUpSteepPolygons");
|
|
pRequest->m_bAllowToPushVehicleDoorsClosed = ScanTF(ptr, "*m_bAllowToPushVehicleDoorsClosed");
|
|
pRequest->m_bMissionPed = ScanTF(ptr, "*m_bMissionPed");
|
|
pRequest->m_bEnsureLosBeforeEnding = ScanTF(ptr, "*m_bEnsureLosBeforeEnding");
|
|
pRequest->m_bExpandStartEndPolyTessellationRadius = ScanTF(ptr, "*m_bExpandStartEndPolyTessellationRadius");
|
|
pRequest->m_bPullFromEdgeExtra = ScanTF(ptr, "*m_bPullFromEdgeExtra");
|
|
pRequest->m_bSofterFleeHeuristics = ScanTF(ptr, "*m_bSofterFleeHeuristics");
|
|
pRequest->m_bAvoidTrainTracks = ScanTF(ptr, "*m_bAvoidTrainTracks");
|
|
|
|
// Load movement costs
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fClimbHighPenalty = %f", &pRequest->m_MovementCosts.m_fClimbHighPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fClimbLowPenalty = %f", &pRequest->m_MovementCosts.m_fClimbLowPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fDropDownPenalty = %f", &pRequest->m_MovementCosts.m_fDropDownPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fDropDownPenaltyPerMetre = %f", &pRequest->m_MovementCosts.m_fDropDownPenaltyPerMetre);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fClimbLadderPenalty = %f", &pRequest->m_MovementCosts.m_fClimbLadderPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fClimbLadderPenaltyPerMetre = %f", &pRequest->m_MovementCosts.m_fClimbLadderPenaltyPerMetre);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fEnterWaterPenalty = %f", &pRequest->m_MovementCosts.m_fEnterWaterPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fLeaveWaterPenalty = %f", &pRequest->m_MovementCosts.m_fLeaveWaterPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fBeInWaterPenalty = %f", &pRequest->m_MovementCosts.m_fBeInWaterPenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fClimbObjectPenaltyPerMetre = %f", &pRequest->m_MovementCosts.m_fClimbObjectPenaltyPerMetre);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fWanderPenaltyForZeroPedDensity = %f", &pRequest->m_MovementCosts.m_fWanderPenaltyForZeroPedDensity);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fMoveOntoSteepSurfacePenalty = %f", &pRequest->m_MovementCosts.m_fMoveOntoSteepSurfacePenalty);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fPenaltyForNoDirectionalCover = %f", &pRequest->m_MovementCosts.m_fPenaltyForNoDirectionalCover);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fPenaltyForFavourCoverPerUnsetBit = %f", &pRequest->m_MovementCosts.m_fPenaltyForFavourCoverPerUnsetBit);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_iPenaltyForMovingToLowerPedDensity = %i", &pRequest->m_MovementCosts.m_iPenaltyForMovingToLowerPedDensity);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_iPenaltyPerPolyFreeSpaceDifference = %i", &pRequest->m_MovementCosts.m_iPenaltyPerPolyFreeSpaceDifference);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "m_fAvoidTrainTracksPenalty = %f", &pRequest->m_MovementCosts.m_fAvoidTrainTracksPenalty);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "RANDSEED %u", &iRandSeed);
|
|
pRequest->m_iPedRandomSeed = iRandSeed;
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "ENTITY_RADIUS %f", &pRequest->m_fEntityRadius);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "NUMINCLUDE %i", &pRequest->m_iNumIncludeObjects);
|
|
for(o=0; o<pRequest->m_iNumIncludeObjects; o++)
|
|
{
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "OBJ %i", &pRequest->m_IncludeObjects[o]);
|
|
}
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "NUMEXCLUDE %i", &pRequest->m_iNumExcludeObjects);
|
|
for(o=0; o<pRequest->m_iNumExcludeObjects; o++)
|
|
{
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "OBJ %i", &pRequest->m_ExcludeObjects[o]);
|
|
}
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "NUM_INFLUENCE_SPHERES %i", &pRequest->m_iNumInfluenceSpheres);
|
|
for(o=0; o<pRequest->m_iNumInfluenceSpheres; o++)
|
|
{
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
Vector3 vOrigin;
|
|
float fRadius, fInner, fOuter;
|
|
sscanf(ptr, "SPHERE %f %f %f %f %f %f",
|
|
&vOrigin.x, &vOrigin.y, &vOrigin.z, &fRadius, &fInner, &fOuter);
|
|
pRequest->m_InfluenceSpheres[o].Init(vOrigin, fRadius, fInner, fOuter);
|
|
}
|
|
|
|
// Skip to dynamic objects section
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "NUMOBJECTS %i", &iNumObjs);
|
|
|
|
for(o=0; o<iNumObjs; o++)
|
|
{
|
|
TDynamicObject * pObj = rage_new TDynamicObject();
|
|
memset(pObj, 0, sizeof(TDynamicObject));
|
|
|
|
pObj->m_pEntity = (fwEntity*)(new CEntity());
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
Vector3 vOrigin;
|
|
sscanf(ptr, "OBJECTPOS %f %f %f", &vOrigin.x, &vOrigin.y, &vOrigin.z);
|
|
pObj->m_Bounds.SetOrigin(vOrigin);
|
|
|
|
((CEntity*)pObj->m_pEntity)->m_vPosition = pObj->m_Bounds.GetOrigin();
|
|
|
|
float fTmp;
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "CORNERS %f %f %f %f %f %f %f %f %f %f %f %f",
|
|
&pObj->m_vVertices[0].x, &pObj->m_vVertices[0].y, &fTmp,
|
|
&pObj->m_vVertices[1].x, &pObj->m_vVertices[1].y, &fTmp,
|
|
&pObj->m_vVertices[2].x, &pObj->m_vVertices[2].y, &fTmp,
|
|
&pObj->m_vVertices[3].x, &pObj->m_vVertices[3].y, &fTmp
|
|
);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "EDGEPLANES %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
|
|
&pObj->m_Bounds.m_vEdgePlaneNormals[0].x, &pObj->m_Bounds.m_vEdgePlaneNormals[0].y, &pObj->m_Bounds.m_vEdgePlaneNormals[0].z, &pObj->m_Bounds.m_vEdgePlaneNormals[0].w,
|
|
&pObj->m_Bounds.m_vEdgePlaneNormals[1].x, &pObj->m_Bounds.m_vEdgePlaneNormals[1].y, &pObj->m_Bounds.m_vEdgePlaneNormals[1].z, &pObj->m_Bounds.m_vEdgePlaneNormals[1].w,
|
|
&pObj->m_Bounds.m_vEdgePlaneNormals[2].x, &pObj->m_Bounds.m_vEdgePlaneNormals[2].y, &pObj->m_Bounds.m_vEdgePlaneNormals[2].z, &pObj->m_Bounds.m_vEdgePlaneNormals[2].w,
|
|
&pObj->m_Bounds.m_vEdgePlaneNormals[3].x, &pObj->m_Bounds.m_vEdgePlaneNormals[3].y, &pObj->m_Bounds.m_vEdgePlaneNormals[3].z, &pObj->m_Bounds.m_vEdgePlaneNormals[3].w,
|
|
&pObj->m_Bounds.m_vEdgePlaneNormals[4].x, &pObj->m_Bounds.m_vEdgePlaneNormals[4].y, &pObj->m_Bounds.m_vEdgePlaneNormals[4].z, &pObj->m_Bounds.m_vEdgePlaneNormals[4].w,
|
|
&pObj->m_Bounds.m_vEdgePlaneNormals[5].x, &pObj->m_Bounds.m_vEdgePlaneNormals[5].y, &pObj->m_Bounds.m_vEdgePlaneNormals[5].z, &pObj->m_Bounds.m_vEdgePlaneNormals[5].w
|
|
);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
|
|
Vector3 tempPlanes[4];
|
|
sscanf(ptr, "SEGPLANES %f %f %f %f %f %f %f %f %f %f %f %f",
|
|
&tempPlanes[0].x, &tempPlanes[0].y, &tempPlanes[0].z,
|
|
&tempPlanes[1].x, &tempPlanes[1].y, &tempPlanes[1].z,
|
|
&tempPlanes[2].x, &tempPlanes[2].y, &tempPlanes[2].z,
|
|
&tempPlanes[3].x, &tempPlanes[3].y, &tempPlanes[3].z
|
|
);
|
|
|
|
float fTopZ, fBottomZ;
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "TOPZ %f", &fTopZ);
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "BOTTOMZ %f", &fBottomZ);
|
|
|
|
pObj->m_Bounds.SetTopZ(fTopZ);
|
|
pObj->m_Bounds.SetBottomZ(fBottomZ);
|
|
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
int iMinX,iMinY,iMinZ,iMaxX,iMaxY,iMaxZ;
|
|
sscanf(ptr, "MINMAX %i %i %i %i %i %i", &iMinX,&iMinY,&iMinZ,&iMaxX,&iMaxY,&iMaxZ);
|
|
pObj->m_MinMax.m_iMinX = (s16)iMinX;
|
|
pObj->m_MinMax.m_iMinY = (s16)iMinY;
|
|
pObj->m_MinMax.m_iMinZ = (s16)iMinZ;
|
|
pObj->m_MinMax.m_iMaxX = (s16)iMaxX;
|
|
pObj->m_MinMax.m_iMaxY = (s16)iMaxY;
|
|
pObj->m_MinMax.m_iMaxZ = (s16)iMaxZ;
|
|
|
|
int bIsOpenable;
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "OPENABLE %i", &bIsOpenable);
|
|
pObj->m_bIsOpenable = bIsOpenable;
|
|
|
|
int bIsClimbable;
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "CLIMBABLE %i", &bIsClimbable);
|
|
pObj->m_bIsClimbable = bIsClimbable;
|
|
|
|
int bIsPushable;
|
|
while(*ptr != '*') ptr++; ptr++;
|
|
sscanf(ptr, "PUSHABLE %i", &bIsPushable);
|
|
pObj->m_bIsPushable = bIsPushable;
|
|
|
|
// Error check the bounds of this entity
|
|
static const float fPlaneEps = 0.05f;
|
|
for(int pl=0; pl<4; pl++)
|
|
{
|
|
#if __ASSERT
|
|
Vector3 vCorner(pObj->m_vVertices[pl].x, pObj->m_vVertices[pl].y, pObj->m_Bounds.GetOrigin().z);
|
|
float fPlaneDist = pObj->m_Bounds.m_vEdgePlaneNormals[pl].Dot3(vCorner) + pObj->m_Bounds.m_vEdgePlaneNormals[pl].w;
|
|
Assert(Abs(fPlaneDist) < fPlaneEps);
|
|
#endif
|
|
}
|
|
|
|
#ifdef GTA_ENGINE
|
|
pObj->m_bIsActive = false;
|
|
#else
|
|
pObj->m_bIsActive = true;
|
|
#endif
|
|
pObj->m_bInactiveDueToVelocity = false;
|
|
|
|
pObj->m_bFlaggedForDeletion = false;
|
|
pObj->m_pEntity = NULL;
|
|
pObj->m_bCurrentlyCopyingBounds = false;
|
|
pObj->m_bCurrentlyUpdatingNewBounds = false;
|
|
pObj->m_bNewBounds = false;
|
|
pObj->m_bPossibleIntersection = false;
|
|
|
|
pObj->m_bNeedsReInsertingIntoGridCells = true;
|
|
|
|
pObj->m_pOwningGridCell = NULL;
|
|
pObj->m_pPrevObjInGridCell = NULL;
|
|
pObj->m_pNextObjInGridCell = NULL;
|
|
|
|
|
|
pObj->m_pNext = m_PathServerThread.m_pFirstDynamicObject;
|
|
m_PathServerThread.m_pFirstDynamicObject = pObj;
|
|
}
|
|
|
|
CPathServer::ms_bHaveAnyDynamicObjectsChanged = true;
|
|
|
|
LeaveCriticalSection(&m_PathServerThread.m_DynamicObjectsCriticalSection);
|
|
return true;
|
|
}
|
|
|
|
#endif // GTA_ENGINE
|
|
|
|
#endif
|
|
|
|
|
|
//*************************************************************************************************
|
|
|
|
#ifdef GTA_ENGINE
|
|
|
|
void CPathServerGameInterfaceGta::OverridePathRequestParameters(const TRequestPathStruct &reqStruct, CPathRequest &pathRequest) const
|
|
{
|
|
if(reqStruct.m_pPed)
|
|
{
|
|
// We don't expect anything not derived from CEntity, at this point.
|
|
Assert(reqStruct.m_pPed->GetIsClassId(CEntity::GetStaticClassId()));
|
|
|
|
const CEntity* pEntity = static_cast<const CEntity*>(reqStruct.m_pPed);
|
|
if(pEntity->GetIsTypePed())
|
|
{
|
|
const CPed* pPed = static_cast<const CPed*>(pEntity);
|
|
if(pPed->GetPedResetFlag( CPED_RESET_FLAG_RandomisePointsDuringNavigation ))
|
|
{
|
|
pathRequest.m_bRandomisePoints = true;
|
|
}
|
|
pathRequest.m_iPedRandomSeed = (pPed->GetRandomSeed() ^ fwTimer::GetTimeInMilliseconds());
|
|
|
|
if (pPed->GetPedIntelligence()->GetNavCapabilities().IsFlagSet(CPedNavCapabilityInfo::FLAG_SEARCH_FOR_PATHS_ABOVE_PED))
|
|
{
|
|
pathRequest.m_fDistanceBelowStartToLookForPoly = 1.0f;
|
|
pathRequest.m_fDistanceAboveStartToLookForPoly = LARGE_FLOAT;
|
|
pathRequest.m_fMaxDistanceToAdjustPathStart = LARGE_FLOAT;
|
|
}
|
|
|
|
// Override some of the movement costs based on the ped's stats
|
|
SetPathFindMovementCostsFromPed(pathRequest.m_MovementCosts, pPed);
|
|
}
|
|
}
|
|
|
|
//*******************************************************************************
|
|
// If this path is to be found on an entity which owns a dynamic navmesh, then
|
|
// we should note the index of the mesh. We cannot use CEntity pointers within
|
|
// the pathfinding as it is not threadsafe (even with the RegdPtr mechanism)
|
|
|
|
if(reqStruct.m_pEntityPathIsOn)
|
|
{
|
|
LOCK_NAVMESH_DATA;
|
|
|
|
// We don't expect anything not derived from CEntity, at this point.
|
|
Assert(reqStruct.m_pEntityPathIsOn->GetIsClassId(CEntity::GetStaticClassId()));
|
|
const CEntity* pEntityPathIsOn = static_cast<const CEntity*>(reqStruct.m_pEntityPathIsOn);
|
|
|
|
#if __DEV
|
|
Assert(pEntityPathIsOn->GetIsTypeVehicle());
|
|
const CVehicle * pVehicle = (CVehicle*)pEntityPathIsOn;
|
|
Assert(pVehicle->m_nVehicleFlags.bHasDynamicNavMesh);
|
|
#endif
|
|
|
|
const u32 iModelIndex = pEntityPathIsOn->GetModelIndex();
|
|
Assert(pEntityPathIsOn->GetIsTypeVehicle());
|
|
const TDynamicObjectIndex iDynObjIndex = ((CVehicle*)pEntityPathIsOn)->GetPathServerDynamicObjectIndex();
|
|
|
|
int n;
|
|
for(n=0; n<fwPathServer::GetDynamicNavMeshStore().GetNum(); n++)
|
|
{
|
|
CModelInfoNavMeshRef & ref = fwPathServer::GetDynamicNavMeshStore().Get(n);
|
|
|
|
if(ref.m_iModelIndex.Get() == (s32)iModelIndex)
|
|
{
|
|
#if AI_OPTIMISATIONS_OFF
|
|
Assertf(ref.m_pBackUpNavmeshCopy, "Path request was on a dynamic entity whose model type supports having navmesh, but entity's navmesh wasn't loaded. Maybe it wasn't streamed in yet?");
|
|
#endif
|
|
if(ref.m_pBackUpNavmeshCopy)
|
|
{
|
|
pathRequest.m_iIndexOfDynamicNavMesh = NAVMESH_INDEX_FIRST_DYNAMIC + n;
|
|
}
|
|
else
|
|
{
|
|
pathRequest.m_iIndexOfDynamicNavMesh = NAVMESH_NAVMESH_INDEX_NONE;
|
|
}
|
|
|
|
#ifdef NAVGEN_TOOL
|
|
pathRequest.m_PathResultInfo.m_MatrixOfEntityAtPathCreationTime = pEntityPathIsOn->GetMatrix();
|
|
#else
|
|
pathRequest.m_PathResultInfo.m_MatrixOfEntityAtPathCreationTime = MAT34V_TO_MATRIX34(pEntityPathIsOn->GetMatrix());
|
|
#endif
|
|
|
|
pathRequest.m_PathResultInfo.m_bFoundDynamicEntityPathIsOn = true;
|
|
pathRequest.m_PathResultInfo.m_iDynObjIndex = iDynObjIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assertf(n!=fwPathServer::GetDynamicNavMeshStore().GetNum(), "Didn't find dynamic navmesh instance to navigate on");
|
|
}
|
|
|
|
//*********************************************************************************************************
|
|
}
|
|
|
|
|
|
void CPathServerGameInterfaceGta::SetPathFindMovementCostsFromPed(CPathFindMovementCosts& costsOut, const CPed * pPed)
|
|
{
|
|
float fMod = pPed->GetPedModelInfo()->GetPersonalitySettings().GetMovementCostModifier();
|
|
Assert(fMod >= 1.f && fMod <= 5.f);
|
|
|
|
costsOut.m_fClimbHighPenalty = CPathFindMovementCosts::ms_fDefaultClimbHighPenalty * fMod;
|
|
costsOut.m_fClimbLowPenalty = CPathFindMovementCosts::ms_fDefaultClimbLowPenalty * fMod;
|
|
costsOut.m_fDropDownPenalty = CPathFindMovementCosts::ms_fDefaultDropDownPenalty * fMod;
|
|
costsOut.m_fClimbLadderPenalty = CPathFindMovementCosts::ms_fDefaultClimbLadderPenalty * fMod;
|
|
costsOut.m_fEnterWaterPenalty = CPathFindMovementCosts::ms_fDefaultEnterWaterPenalty * fMod;
|
|
|
|
// designers have ability to modify climbing costs with a multiplier
|
|
costsOut.m_fClimbHighPenalty *= pPed->GetPedIntelligence()->GetNavCapabilities().GetClimbCostModifier();
|
|
costsOut.m_fClimbLowPenalty *= pPed->GetPedIntelligence()->GetNavCapabilities().GetClimbCostModifier();
|
|
|
|
if( pPed->GetPedIntelligence()->GetNavCapabilities().IsFlagSet(CPedNavCapabilityInfo::FLAG_PREFER_TO_AVOID_WATER) == false )
|
|
{
|
|
costsOut.m_fEnterWaterPenalty = 0.0f;
|
|
costsOut.m_fLeaveWaterPenalty = 0.0f;
|
|
costsOut.m_fBeInWaterPenalty = 0.0f;
|
|
}
|
|
}
|
|
|
|
#endif // GTA_ENGINE
|