Files
GTASource/game/pathserver/PathServer_PathStringPull.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

975 lines
38 KiB
C++

#include "PathServer\PathServer.h"
// Rage headers
#include "atl/inmap.h"
#include "system/xtl.h"
#include "vector/geometry.h"
// Framework headers
#include "fwmaths/Vector.h"
#include "fwmaths/random.h"
NAVMESH_OPTIMISATIONS()
//******************************************************************************************************************
// RefinePathAndCreateNodes
// This function takes the list of polygons (and the chosen waypoints within these polygons) which were found
// by the A* pathfind function. It then preforms the process known as "string-pulling" which removes kinks in
// the path, and reduces it to a small number of straight line-segments with visibility between them.
// These become the final waypoints which make up the path.
//******************************************************************************************************************
#define __BREAK_ON_POS 0
bool CPathServerThread::ConvertPointsInPolyToVertexList(
CPathRequest * pPathRequest,
const Vector3 & vStartPos,
const Vector3 & vEndPos,
Vector3 & vOut_StartWaypoint,
TNavMeshPoly *& pOut_StartPoly,
Vector3 & vOut_EndWaypoint,
TNavMeshPoly *& pOut_EndPoly,
s32 & iOut_NumPointsToTest)
{
if(m_Vars.m_iNumPathPolys == 0)
{
// If m_iNumPathPolys was zero, and pPathRequest->m_iNumPoints is 2 - it means
// we resolved the path with a simple LOS.
if(pPathRequest->m_iNumPoints == 2)
return false;
// Otherwise, we couldn't find a path - so reset the number of points
pPathRequest->m_iNumPoints = 0;
}
pPathRequest->m_iNumPoints = 0;
Vector3 vPolyPts[NAVMESHPOLY_MAX_NUM_VERTICES];
pOut_StartPoly = m_Vars.m_PathPolys[0];
pOut_EndPoly = m_Vars.m_PathPolys[ m_Vars.m_iNumPathPolys-1 ];
vOut_StartWaypoint = vStartPos;
vOut_EndWaypoint = vEndPos;
static const Vector3 vRaiseVecForVolumePolys(0.0f, 0.0f, UNDERWATER_DEFAULT_RAISE_UP_Z_AMT);
m_Vars.m_PathPolys[m_Vars.m_iNumPathPolys] = pOut_EndPoly;
//const float fEntityRadius = pPathRequest->m_fEntityRadius;
const aiNavDomain domain = pPathRequest->GetMeshDataSet();
u32 v;
u32 t;
int iStartOfPts;
CNavMesh * pNavMesh;
// If the start poly in the path is actually the m_pStartPoly, then use the vStartWaypoint
// for the point-in-poly. m_pStartPoly won't have a valid point enum.
if(m_Vars.m_PathPolys[0]==m_Vars.m_pStartPoly)
{
m_Vars.m_vClosestPointInPathPolys[0] = vOut_StartWaypoint;
iStartOfPts = 1;
}
// Otherwise we must have started from a poly added during the tessellation around the start-pos.
// In this case the vStartWaypoint may be well outside of the first polygon, so use its pointenum.
// NB : Will poly[0] even have a correct iPointEnum - its never set for starting polys..
else
{
iStartOfPts = 0;
}
for(t=iStartOfPts; t<m_Vars.m_iNumPathPolys; t++)
{
pNavMesh = CPathServer::GetNavMeshFromIndex(m_Vars.m_PathPolys[t]->GetNavMeshIndex(), domain);
Assert(pNavMesh);
Assert(!m_Vars.m_PathPolys[t]->GetIsDegenerateConnectionPoly());
if(m_Vars.m_PathPolys[t]->GetPointEnum() == NAVMESHPOLY_POINTENUM_CENTROID)
{
if( pNavMesh->GetIndexOfMesh() != NAVMESH_INDEX_TESSELLATION)
{
pNavMesh->GetPolyCentroidQuick(m_Vars.m_PathPolys[t], m_Vars.m_vClosestPointInPathPolys[t]);
}
else
{
pNavMesh->GetPolyCentroid( pNavMesh->GetPolyIndex(m_Vars.m_PathPolys[t]), m_Vars.m_vClosestPointInPathPolys[t]);
}
}
else
{
for(v=0; v<m_Vars.m_PathPolys[t]->GetNumVertices(); v++)
{
pNavMesh->GetVertex( pNavMesh->GetPolyVertexIndex(m_Vars.m_PathPolys[t], v), vPolyPts[v] );
}
bool bAddedLinkPos = false;
const s32 iPointEnum = m_Vars.m_PathPolys[t]->GetPointEnum();
if(iPointEnum == NAVMESHPOLY_POINTENUM_SPECIAL_LINK_ENDPOS)
{
Assert(t > 0);
if(t > 0)
{
// Find the special link from the pPathParent poly, to this poly
TNavMeshPoly * pSpecialLinkStartPoly = m_Vars.m_PathPolys[t-1];
if(pSpecialLinkStartPoly)
{
CNavMesh * pSpecialLinkStartNavMesh = CPathServer::GetNavMeshFromIndex(pSpecialLinkStartPoly->GetNavMeshIndex(), pPathRequest->GetMeshDataSet());
TNavMeshPoly * pSpecialLinkEndPoly = m_Vars.m_PathPolys[t];
CNavMesh * pSpecialLinkEndNavMesh = CPathServer::GetNavMeshFromIndex(pSpecialLinkEndPoly->GetNavMeshIndex(), pPathRequest->GetMeshDataSet());
const u32 iStartNavMesh = pSpecialLinkStartNavMesh->GetIndexOfMesh();
const u32 iEndNavMesh = pSpecialLinkEndNavMesh->GetIndexOfMesh();
const u32 iStartPoly = pSpecialLinkStartNavMesh->GetPolyIndex( pSpecialLinkStartPoly );
const u32 iEndPoly = pSpecialLinkEndNavMesh->GetPolyIndex( pSpecialLinkEndPoly );
if(pSpecialLinkStartPoly->GetIsTessellatedFragment())
{
pSpecialLinkStartNavMesh = CPathServer::GetNavMeshFromIndex( CPathServer::GetTessInfo(pSpecialLinkStartPoly)->m_iNavMeshIndex, pPathRequest->GetMeshDataSet() );
pSpecialLinkStartPoly = pSpecialLinkStartNavMesh->GetPoly( CPathServer::GetTessInfo(pSpecialLinkStartPoly)->m_iPolyIndex );
}
if(pSpecialLinkEndPoly->GetIsTessellatedFragment())
{
pSpecialLinkEndNavMesh = CPathServer::GetNavMeshFromIndex( CPathServer::GetTessInfo(pSpecialLinkEndPoly)->m_iNavMeshIndex, pPathRequest->GetMeshDataSet() );
pSpecialLinkEndPoly = pSpecialLinkEndNavMesh->GetPoly( CPathServer::GetTessInfo(pSpecialLinkEndPoly)->m_iPolyIndex );
}
s32 iLinkLookupIndex = pSpecialLinkStartPoly->GetSpecialLinksStartIndex();
for(s32 s=0; s<pSpecialLinkStartPoly->GetNumSpecialLinks(); s++)
{
u16 iLinkIndex = pSpecialLinkStartNavMesh->GetSpecialLinkIndex(iLinkLookupIndex++);
Assert(iLinkIndex < pSpecialLinkStartNavMesh->GetNumSpecialLinks());
CSpecialLinkInfo & linkInfo = pSpecialLinkStartNavMesh->GetSpecialLinks()[iLinkIndex];
if( linkInfo.GetLinkFromNavMesh()==iStartNavMesh &&
linkInfo.GetLinkFromPoly()==iStartPoly &&
linkInfo.GetLinkToNavMesh()==iEndNavMesh &&
linkInfo.GetLinkToPoly()==iEndPoly )
{
Vector3 vLinkFromPos, vLinkToPos;
CNavMesh::DecompressVertex(vLinkFromPos, linkInfo.GetLinkFromPosX(), linkInfo.GetLinkFromPosY(), linkInfo.GetLinkFromPosZ(), pSpecialLinkStartNavMesh->GetQuadTree()->m_Mins, pSpecialLinkStartNavMesh->GetExtents());
CNavMesh::DecompressVertex(vLinkToPos, linkInfo.GetLinkToPosX(), linkInfo.GetLinkToPosY(), linkInfo.GetLinkToPosZ(), pSpecialLinkEndNavMesh->GetQuadTree()->m_Mins, pSpecialLinkEndNavMesh->GetExtents());
Vector3 vLinkVec = vLinkToPos - vLinkFromPos;
vLinkVec.z = 0.0f;
vLinkVec.Normalize();
Vector3 vPointInPolyOffset = vLinkVec * (pPathRequest->m_fEntityRadius + 0.1f);
m_Vars.m_vClosestPointInPathPolys[t] = vLinkToPos + vPointInPolyOffset;
bAddedLinkPos = true;
break;
}
}
}
}
if(!bAddedLinkPos)
{
m_Vars.m_PathPolys[t]->SetPointEnum(NAVMESHPOLY_POINTENUM_CENTROID);
if( pNavMesh->GetIndexOfMesh() != NAVMESH_INDEX_TESSELLATION)
{
pNavMesh->GetPolyCentroidQuick(m_Vars.m_PathPolys[t], m_Vars.m_vClosestPointInPathPolys[t]);
}
else
{
pNavMesh->GetPolyCentroid( pNavMesh->GetPolyIndex(m_Vars.m_PathPolys[t]), m_Vars.m_vClosestPointInPathPolys[t]);
}
bAddedLinkPos = true;
}
}
if(!bAddedLinkPos)
{
GetPointInPolyFromPointEnum(
pNavMesh,
m_Vars.m_PathPolys[t],
vPolyPts,
&m_Vars.m_vClosestPointInPathPolys[t],
iPointEnum,
ShouldUseMorePointsForPoly(*m_Vars.m_PathPolys[t], pPathRequest) );
}
}
#if __STORE_POLYS_IN_PATH_REQUEST
pPathRequest->m_InitiallyFoundPathPointInPolys[t] = m_Vars.m_vClosestPointInPathPolys[t];
#endif
}
#if __STORE_POLYS_IN_PATH_REQUEST
pPathRequest->m_InitiallyFoundPathPointInPolys[0] = vOut_StartWaypoint;
pPathRequest->m_InitiallyFoundPathPointInPolys[m_Vars.m_iNumPathPolys-1] = vOut_EndWaypoint;
#endif
if(!CPathServer::ms_bRefinePaths)
{
for(u32 f=1; f<MIN(m_Vars.m_iNumPathPolys,MAX_NUM_PATH_POINTS) ; f++)
{
#if __STORE_POLYS_IN_PATH_REQUEST
pPathRequest->m_InitiallyFoundPathPointInPolys[f] = m_Vars.m_vClosestPointInPathPolys[f];
#endif
pPathRequest->m_PathPoints[f] = m_Vars.m_vClosestPointInPathPolys[f];
pPathRequest->m_PathPolys[f] = m_Vars.m_PathPolys[f];
pPathRequest->m_WaypointFlags[f] = m_Vars.m_iPolyWaypointFlags[f];
}
pPathRequest->m_PathPolys[0] = m_Vars.m_PathPolys[0];
pPathRequest->m_PathPoints[0] = vOut_StartWaypoint;
pPathRequest->m_PathPolys[m_Vars.m_iNumPathPolys-1] = m_Vars.m_PathPolys[m_Vars.m_iNumPathPolys-1];
pPathRequest->m_PathPoints[m_Vars.m_iNumPathPolys-1] = vOut_EndWaypoint;
pPathRequest->m_iNumPoints = MIN(m_Vars.m_iNumPathPolys,MAX_NUM_PATH_POINTS);
return false;
}
iOut_NumPointsToTest = 0;
// If we're not fleeing, then the end point is taken explicitly from the path request
if(!pPathRequest->m_bFleeTarget && !pPathRequest->m_bWander)
{
// If we didn't actually get exactly to the endpoint, but terminated within the completion radius -
// then see how far off we were. If outside some set distance, then we shall use the endpoint
// we achieved instead of the actual path endpoint.
if(pPathRequest->m_PathResultInfo.m_bUsedCompletionRadius)
{
static const float fUseRealEndPointEpsSqr = 0.5f*0.5f;
Vector3 vDiffToActualEndPoint = m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys-1] - pPathRequest->m_vPathEnd;
if(vDiffToActualEndPoint.Mag2() < fUseRealEndPointEpsSqr)
{
m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys] = vOut_EndWaypoint;
}
else
{
vOut_EndWaypoint = m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys-1];
m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys] = vOut_EndWaypoint;
}
}
else
{
m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys] = vOut_EndWaypoint;
}
iOut_NumPointsToTest = m_Vars.m_iNumPathPolys;
}
// Otherwise, it is the position in the poly at which we terminated the flee path
else
{
vOut_EndWaypoint = m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys-1];
m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys] = vOut_EndWaypoint;
iOut_NumPointsToTest = m_Vars.m_iNumPathPolys-1;
}
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vOut_StartWaypoint;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = pOut_StartPoly;
pPathRequest->m_WaypointFlags[ pPathRequest->m_iNumPoints ] = m_Vars.m_iPolyWaypointFlags[0];
pPathRequest->m_iNumPoints++;
return true;
}
bool CPathServerThread::RefinePathAndCreateNodes(CPathRequest * pPathRequest, const Vector3 & vStartPos, const Vector3 & vEndPos, const s32 iMaxNumPoints)
{
#if !__FINAL
m_PerfTimer->Reset();
m_PerfTimer->Start();
#endif
#if __BREAK_ON_POS
static Vector3 vBreakOnPos(9999.9f,9999.9f,9999.9f);
static bool bPause = false;
#endif
//*************************************************************
Vector3 vStartWaypoint, vEndWaypoint;
TNavMeshPoly * pStartPoly, * pEndPoly;
s32 iNumPointsToTest;
if( !ConvertPointsInPolyToVertexList(pPathRequest, vStartPos, vEndPos, vStartWaypoint, pStartPoly, vEndWaypoint, pEndPoly, iNumPointsToTest))
{
return true;
}
//*************************************************************
const float fEntityRadius = pPathRequest->m_fEntityRadius;
const aiNavDomain domain = pPathRequest->GetMeshDataSet();
m_iNumRefinePathIterations = 0;
Vector3 vCurrentStartPos = vStartWaypoint;
TNavMeshPoly * pCurrentStartPoly = pStartPoly;
u32 iCurrentStartIndex = 0;
u32 iCurrentTestIndex = 1;
bool bLOS = false, bDynObjLOS = false, bNavMeshLOS = false;
DEV_ONLY(s32 iNumIterations=0;)
while(iCurrentTestIndex <= iNumPointsToTest)
{
#if __DEV
iNumIterations++;
Assertf(iNumIterations < 1000, "Possible infinite loop found in string pull (see url:bugstar:984258)");
#endif
if(CPathServer::m_bForceAbortCurrentRequest)
{
pPathRequest->m_iCompletionCode = PATH_ABORTED_DUE_TO_ANOTHER_THREAD;
return false;
}
// Early out if path is cancelled during processing
if(pPathRequest->m_iCompletionCode == PATH_CANCELLED)
{
return false;
}
//**************************************************************************************
TNavMeshPoly * pTestPoly = m_Vars.m_PathPolys[iCurrentTestIndex];
Vector3 vTestPos = m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex];
// Increment timestamp before LOS. This prevents infinite recursion in some tricky boundary cases.
IncTimeStamp();
m_bLosHitInfluenceBoundary = false;
m_bLosCrossedCoverBoundary = false;
bool bPointAdded = false;
static dev_bool bDoExtend = false;
static const float fExtendDist = 0.1f;
Vector3 vExtend = VEC3_ZERO;
if(bDoExtend)
{
vExtend = m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex] - m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex];
vExtend.Normalize();
vExtend *= fExtendDist;
}
bNavMeshLOS = TestNavMeshLOS(
m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex] - vExtend,
m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex] + vExtend,
m_Vars.m_PathPolys[iCurrentTestIndex],
pCurrentStartPoly,
NULL,
m_Vars.m_iLosFlags,
domain);
if(/*bNavMeshLOS && */CPathServer::m_eObjectAvoidanceMode != CPathServer::ENoObjectAvoidance)
{
bDynObjLOS = TestDynamicObjectLOS(
m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex],
m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex]
);
}
else
{
bDynObjLOS = true;
}
if(iCurrentTestIndex <= iCurrentStartIndex+1)
{
m_bLosHitInfluenceBoundary = false;
m_bLosCrossedCoverBoundary = false;
}
bLOS = (bNavMeshLOS && bDynObjLOS);
//***************************************************************************
// No line-of-sight at all, or we hit an openable/pushable/climbable obejct
if((!bLOS) || m_bLosHitInfluenceBoundary || m_bLosCrossedCoverBoundary)
{
//***********************************************************************************************************
// If the current poly was reached via a non-standard adjacency (ie. a climb-up, drop-down, etc), then
// we must ensure that one point is added exactly halfway along the edge from which this polygon was
// reached & another must be added as close to is as possible within this poly. It should ideally be
// exactly in the direction pointed by the previously traversed edge's normal. (NB: In fact, we could
// generate it manually since we're not restricted to the pointEnum scheme here...)
//***********************************************************************************************************
if(m_Vars.m_PathPolys[iCurrentTestIndex]->TestFlags(NAVMESHPOLY_REACHEDBY_MASK) &&
((iCurrentTestIndex <= iCurrentStartIndex + 1) || bDynObjLOS))
{
TNavMeshPoly * pCurrentTestPoly = m_Vars.m_PathPolys[iCurrentTestIndex];
CNavMesh * pCurrentNavMesh = CPathServer::GetNavMeshFromIndex(pCurrentTestPoly->GetNavMeshIndex(), domain);
// CL:3638784 (B* 1024539: If a climb point is blocked by a dynamic object in the path we now fail the path.)
// JB: Unless this is a special link
if (!bDynObjLOS && pCurrentTestPoly->TestFlags(NAVMESHPOLY_WAS_REACHED_VIA_SPECIAL_LINK)==false)
{
pPathRequest->m_iCompletionCode = PATH_NOT_FOUND;
return false;
}
//**********************************************************
// Special link type of adjacency led to this poly
if(pCurrentTestPoly->TestFlags(NAVMESHPOLY_WAS_REACHED_VIA_SPECIAL_LINK))
{
Assert(pCurrentTestPoly->m_PathParentPoly != NULL);
TNavMeshPoly * pParentPoly = pCurrentTestPoly->m_PathParentPoly;
CNavMesh * pParentNavMesh = CPathServer::GetNavMeshFromIndex(pParentPoly->GetNavMeshIndex(), domain);
// Find the special link from the previous poly to this poly
CNavMesh * pOriginalFromNavMesh, * pOriginalToNavMesh;
TNavMeshPoly * pOriginalFromPoly, * pOriginalToPoly;
// Find the original 'from' poly, if the pParentPoly is in the tessellation navmesh
if(pParentNavMesh->GetIndexOfMesh() == NAVMESH_INDEX_TESSELLATION)
{
const int iPolyIndexInTessMesh = pParentNavMesh->GetPolyIndex(pParentPoly);
TTessInfo & pTessInfo = CPathServer::m_PolysTessellatedFrom[iPolyIndexInTessMesh];
pOriginalFromNavMesh = CPathServer::GetNavMeshFromIndex(pTessInfo.m_iNavMeshIndex, domain);
pOriginalFromPoly = pOriginalFromNavMesh->GetPoly(pTessInfo.m_iPolyIndex);
}
else
{
pOriginalFromNavMesh = pParentNavMesh;
pOriginalFromPoly = pParentPoly;
}
// Find the original 'to' poly, if the pCurrentTestPoly is in the tessellation navmesh
if(pCurrentNavMesh->GetIndexOfMesh() == NAVMESH_INDEX_TESSELLATION)
{
int iPolyIndexInTessMesh = pCurrentNavMesh->GetPolyIndex(pCurrentTestPoly);
TTessInfo & pTessInfo = CPathServer::m_PolysTessellatedFrom[iPolyIndexInTessMesh];
pOriginalToNavMesh = CPathServer::GetNavMeshFromIndex(pTessInfo.m_iNavMeshIndex, domain);
pOriginalToPoly = pOriginalFromNavMesh->GetPoly(pTessInfo.m_iPolyIndex);
}
else
{
pOriginalToNavMesh = pCurrentNavMesh;
pOriginalToPoly = pCurrentTestPoly;
}
//s32 iLinkLookupIndex = pParentPoly->GetSpecialLinksStartIndex();
//for(s32 s=0; s<pParentPoly->GetNumSpecialLinks(); s++)
s32 iLinkLookupIndex = pOriginalFromPoly->GetSpecialLinksStartIndex();
for(s32 s=0; s<pOriginalFromPoly->GetNumSpecialLinks(); s++)
{
u16 iLinkIndex = pOriginalFromNavMesh->GetSpecialLinkIndex(iLinkLookupIndex++);
Assert(iLinkIndex < pOriginalFromNavMesh->GetNumSpecialLinks());
CSpecialLinkInfo & linkInfo = pOriginalFromNavMesh->GetSpecialLinks()[iLinkIndex];
// if(linkInfo.m_Struct1.m_iOriginalLinkFromNavMesh==pOriginalFromPoly->GetNavMeshIndex() &&
// linkInfo.m_iOriginalLinkFromPoly==pOriginalFromNavMesh->GetPolyIndex(pOriginalFromPoly) &&
// linkInfo.m_Struct2.m_iOriginalLinkToNavMesh==pOriginalToPoly->GetNavMeshIndex() &&
// linkInfo.m_iOriginalLinkToPoly==pOriginalToNavMesh->GetPolyIndex(pOriginalToPoly))
if( linkInfo.GetLinkFromNavMesh()==pParentNavMesh->GetIndexOfMesh() &&
linkInfo.GetLinkFromPoly()==pParentNavMesh->GetPolyIndex(pParentPoly) &&
linkInfo.GetLinkToNavMesh()==pCurrentNavMesh->GetIndexOfMesh() &&
linkInfo.GetLinkToPoly()==pCurrentNavMesh->GetPolyIndex(pCurrentTestPoly) )
{
Assert(linkInfo.GetAStarTimeStamp()==m_iAStarTimeStamp);
// This is the special link from the previous poly to this poly.
// We need to add the start & end point to the path
Vector3 vSpecialLinkStart, vSpecialLinkEnd;
pOriginalFromNavMesh->DecompressVertex(vSpecialLinkStart, linkInfo.GetLinkFromPosX(), linkInfo.GetLinkFromPosY(), linkInfo.GetLinkFromPosZ());
pOriginalToNavMesh->DecompressVertex(vSpecialLinkEnd, linkInfo.GetLinkToPosX(), linkInfo.GetLinkToPosY(), linkInfo.GetLinkToPosZ());
switch(linkInfo.GetLinkType())
{
case NAVMESH_LINKTYPE_CLIMB_LADDER:
case NAVMESH_LINKTYPE_DESCEND_LADDER:
case NAVMESH_LINKTYPE_CLIMB_OBJECT:
{
// Bail out if we don't have enough room to set up this climb
if(pPathRequest->m_iNumPoints + 3 >= iMaxNumPoints)
{
return true;
}
#ifdef GTA_ENGINE
// Locally turn off ladders if too many wants to use them within a short time
if ( !pPathRequest->m_bCoverFinderPath &&
(linkInfo.GetLinkType() == NAVMESH_LINKTYPE_CLIMB_LADDER ||
linkInfo.GetLinkType() == NAVMESH_LINKTYPE_DESCEND_LADDER))
{
m_BlockedLadders.UpdateUsage(vSpecialLinkStart.z < vSpecialLinkEnd.z ? vSpecialLinkStart : vSpecialLinkEnd);
}
#endif // GTA_ENGINE
// We have to add normally the point in the previous polygon we visited prior to
// this ladder climb's top/bottom points. This is because we cannot necessarily
// see the get-on point from the last route point we added.
if(iCurrentTestIndex > 0)
{
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex-1];
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = m_Vars.m_PathPolys[iCurrentTestIndex-1];
// Prev point will have been standard
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].Clear();
pPathRequest->m_iNumPoints++;
Assert(pPathRequest->m_iNumPoints < iMaxNumPoints);
}
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vSpecialLinkStart;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = NULL;
// Set the special action depending upon the link-type
switch(linkInfo.GetLinkType())
{
case NAVMESH_LINKTYPE_CLIMB_LADDER:
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].m_iSpecialActionFlags = (u16)WAYPOINT_FLAG_CLIMB_LADDER;
break;
case NAVMESH_LINKTYPE_DESCEND_LADDER:
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].m_iSpecialActionFlags = (u16)WAYPOINT_FLAG_DESCEND_LADDER;
break;
case NAVMESH_LINKTYPE_CLIMB_OBJECT:
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].m_iSpecialActionFlags = (u16)WAYPOINT_FLAG_CLIMB_OBJECT;
break;
default:
Assert(false);
break;
}
// We must set the water flag or it gets lost and we get troubles navigating to climb points in water
if (m_Vars.m_iPolyWaypointFlags[iCurrentTestIndex-1].m_iSpecialActionFlags & WAYPOINT_FLAG_IS_ON_WATER_SURFACE)
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].m_iSpecialActionFlags |= WAYPOINT_FLAG_IS_ON_WATER_SURFACE;
// NEW: Set the heading of this waypoint from the heading of the link (store in m_iLinkFlags)
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].m_iHeading = (u8)linkInfo.GetLinkFlags();
pPathRequest->m_iNumPoints++;
Assert(pPathRequest->m_iNumPoints < iMaxNumPoints);
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vSpecialLinkEnd;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = NULL;
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].Clear();
pPathRequest->m_iNumPoints++;
Assert(pPathRequest->m_iNumPoints < iMaxNumPoints);
break;
}
default:
{
Assert(0);
break;
}
}
// We've found the special link, so quit
break;
}
}
}
//****************************************************************************************************
// We arrived in this poly via a navmesh non-standard adjacency. In other words, the adjacency
// is encoded in the previous path-poly's TAdjPoly adjacency array.
else
{
// Find the edge on the previous poly which adjoined to this poly
if(iCurrentTestIndex > 0)
{
TNavMeshPoly * pPrevPoly = m_Vars.m_PathPolys[iCurrentTestIndex-1];
CNavMesh * pPrevNavMesh = CPathServer::GetNavMeshFromIndex(pPrevPoly->GetNavMeshIndex(), domain);
// If the parent polygon was a tessellated fragment, then look to the original untesselated polygon
// to locate the TAdjPoly which contains the adjacency info.
// NB: We need to do the same for the pCurrentTestPoly polygon & pCurrentNavMesh, since this might have been
// tessellated- and the nonstandard adjacency in the parent polygon will still point to the original polygon/navmesh
if(pPrevPoly->GetIsTessellatedFragment())
{
Assert(pPrevNavMesh->GetIndexOfMesh()==NAVMESH_INDEX_TESSELLATION);
TTessInfo * pTessInfo = CPathServer::GetTessInfo(pPrevPoly);
Assert(pTessInfo);
pPrevNavMesh = CPathServer::GetNavMeshFromIndex(pTessInfo->m_iNavMeshIndex, pPathRequest->GetMeshDataSet());
pPrevPoly = pPrevNavMesh->GetPoly(pTessInfo->m_iPolyIndex);
}
CNavMesh * pCurrentNavMesh = CPathServer::GetNavMeshFromIndex(pCurrentTestPoly->GetNavMeshIndex(), domain);
if(pPrevNavMesh && pCurrentNavMesh)
{
u32 e;
for(e=0; e<pPrevPoly->GetNumVertices(); e++)
{
const TAdjPoly & adjPoly = pPrevNavMesh->GetAdjacentPoly( pPrevPoly->GetFirstVertexIndex() + e );
if(adjPoly.GetNavMeshIndex(pPrevNavMesh->GetAdjacentMeshes()) == pCurrentTestPoly->GetNavMeshIndex())
{
TNavMeshPoly * pNextPoly = pCurrentNavMesh->GetPoly(adjPoly.GetPolyIndex());
if(pNextPoly == pCurrentTestPoly)
break;
}
}
//************************************************************************************
// Add a vertex just before the adjacency. The vertex is half-way along the
// previous poly's edge which we arrived in this poly from. (That is the per-edge
// location which is checked for jumps/drop-downs/etc during the navmesh creation)
if(e != pPrevPoly->GetNumVertices())
{
Vector3 vEdgeVec1, vEdgeVec2, vPointOnEdge;
pPrevNavMesh->GetVertex( pPrevNavMesh->GetPolyVertexIndex(pPrevPoly, e), vEdgeVec1);
pPrevNavMesh->GetVertex( pPrevNavMesh->GetPolyVertexIndex(pPrevPoly, ((e+1)%pPrevPoly->GetNumVertices()) ), vEdgeVec2);
vPointOnEdge = (vEdgeVec1 + vEdgeVec2) * 0.5f;
Vector3 vEdgeVec = vEdgeVec2 - vEdgeVec1;
vEdgeVec.Normalize();
Vector3 vEdgeNormal;
vEdgeNormal.Cross(vEdgeVec, Vector3(0,0,1.0f));
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vPointOnEdge;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = NULL;
// Prev point will have been standard
pPathRequest->m_WaypointFlags[ pPathRequest->m_iNumPoints ] = m_Vars.m_iPolyWaypointFlags[iCurrentTestIndex];
#if __BREAK_ON_POS
if(vBreakOnPos.IsClose(pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ], 0.25f))
bPause = true;
#endif
// Find the closest point in pCurrentTestPoly, to the just-added vertex
Vector3 vClosestPoint1;
Vector3 vPolyPts[NAVMESHPOLY_MAX_NUM_VERTICES];
u32 v;
for(v=0; v<pCurrentTestPoly->GetNumVertices(); v++)
{
pCurrentNavMesh->GetVertex( pCurrentNavMesh->GetPolyVertexIndex(pCurrentTestPoly, v), vPolyPts[v]);
}
// Firstly find the closest point on the polygon edge
float fLeastDist2 = FLT_MAX;
u32 lastv = pCurrentTestPoly->GetNumVertices()-1;
for(v=0; v<pCurrentTestPoly->GetNumVertices(); v++)
{
const Vector3 vVec = vPolyPts[v]-vPolyPts[lastv];
const float T = geomTValues::FindTValueSegToPoint(vPolyPts[lastv], vVec, vPointOnEdge);
const Vector3 vPos = vPolyPts[lastv] + (vVec * T);
const float fDiff2 = (vPos - vPointOnEdge).XYMag2();
if( fDiff2 < fLeastDist2 )
{
fLeastDist2 = fDiff2;
vClosestPoint1 = vPos;
}
lastv = v;
}
// Secondly, generate a set of candidate points within the polygon body
m_VisitPolyVars.m_pFromNavMesh = NULL;
m_VisitPolyVars.m_pFromPoly = NULL;
m_VisitPolyVars.m_pToNavMesh = pCurrentNavMesh;
m_VisitPolyVars.m_pToPoly = pCurrentTestPoly;
m_VisitPolyVars.m_pToPolyPoints = vPolyPts;
m_VisitPolyVars.m_vTargetPos = vPointOnEdge;
s32 iDummyPointEnum;
Vector3 vClosestPoint2;
GetClosestPointInPolyToTargetDistSqr(
m_VisitPolyVars,
TVisitPolyStruct::FLAG_CONSIDER_MAXIMUM_NUM_VERTICES,
&vClosestPoint2,
iDummyPointEnum);
const Vector3 vClosestPoint = (vClosestPoint1 - vPointOnEdge).XYMag2() < (vClosestPoint2 - vPointOnEdge).XYMag2() ? vClosestPoint1 : vClosestPoint2;
#ifdef GTA_ENGINE
Vector3 vVecToClosestPoint = vClosestPoint - vPointOnEdge;
//float fWptHeading = fwAngle::GetRadianAngleBetweenPoints(vClosestPoint.x + vEdgeNormal.x, vClosestPoint.y + vEdgeNormal.y, vPointOnEdge.x, vPointOnEdge.y);
float fWptHeading = fwAngle::GetRadianAngleBetweenPoints(vVecToClosestPoint.x, vVecToClosestPoint.y, 0.0f, 0.0f);
while(fWptHeading < 0.0f) fWptHeading += TWO_PI;
while(fWptHeading >= TWO_PI) fWptHeading -= TWO_PI;
pPathRequest->m_WaypointFlags[ pPathRequest->m_iNumPoints ].m_iHeading = (u8) ((fWptHeading / TWO_PI) * 255.0f);
#endif
pPathRequest->m_iNumPoints++;
if(pPathRequest->m_iNumPoints >= iMaxNumPoints)
return true;
// ( NB : Maybe move the point away from the edge, because if we're not careful we may end up climbing
// up the wall again in our attempt to get to this waypoint ? )
// Add this point too
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vClosestPoint + vEdgeNormal;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = NULL;
// The poly at 'iCurrentTestIndex' was connected to by a non-standard adjacency
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].Clear();
#if __BREAK_ON_POS
if(vBreakOnPos.IsClose(pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ], 0.25f))
bPause = true;
#endif
pPathRequest->m_iNumPoints++;
if(pPathRequest->m_iNumPoints >= iMaxNumPoints)
return true;
}
}
}
}
// JB: No need for this, since it is done later at the end of the loop.
// Doing it here means that the progress gets advanced too far..
/*
iCurrentStartIndex = iCurrentTestIndex;
iCurrentTestIndex++;
pCurrentStartPoly = m_Vars.m_PathPolys[iCurrentStartIndex];
vCurrentStartPos = m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex];
*/
}
//**********************************************************************************************
// Otherwise - this is a normal poly->poly connection in the navmesh
//
// If the poly we are testing is further on than the one we are starting from.
// ie. the LOS is blocked, but not between adjacent polys (we'll handle that
// case in a sec).
else if((iCurrentTestIndex > iCurrentStartIndex + 1) || m_bLosHitInfluenceBoundary || m_bLosCrossedCoverBoundary)
{
// What we could do here to improve the path, is to adjust the point
// "m_vClosestPointInPathPolys[iCurrentStartIndex]" repeatedly until we get
// a better point. This should be a point which is closer to lying on the
// line between the last point and the next point ??
// In the case of an influence boundary, we will just start from the next poly.
// It's not important whether or not we have a direct LOS, as there is no physical obstruction
if(bLOS && (m_bLosHitInfluenceBoundary || m_bLosCrossedCoverBoundary))
{
iCurrentStartIndex = iCurrentTestIndex;
//iCurrentTestIndex++;
}
else
{
iCurrentStartIndex = iCurrentTestIndex - 1;
iCurrentTestIndex = iCurrentTestIndex - 1;
}
/*
pCurrentStartPoly = m_Vars.m_PathPolys[iCurrentStartIndex];
vCurrentStartPos = m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex];
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vCurrentStartPos;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = pCurrentStartPoly;
// Prev point will have been standard
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].Clear();
#if __BREAK_ON_POS
if(vBreakOnPos.IsClose(pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ], 0.25f))
bPause = true;
#endif
pPathRequest->m_iNumPoints++;
if(pPathRequest->m_iNumPoints >= iMaxNumPoints)
return true;
*/
}
else
{
// We cannot see from one adjacent poly to the next poly.
// Add a point along the shared edge between each poly.
bool bFoundNewPoint = false;
bool bLineActuallyIntersectsEdge = false;
if(bNavMeshLOS && !bDynObjLOS)
{
// If the obstruction was purely due to a dynamic object, then choose a position along
// the shared edge for which there is visibility between the last two points.
bFoundNewPoint = GetPointWithDynamicObjectVisiblityAlongSharedEdge(
m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex],
m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex],
pCurrentStartPoly,
pTestPoly,
fEntityRadius,
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ],
domain
);
}
else
{
// Find which side of the line defined by the best points in "pCurrentStartPoly to pTestPoly",
// the two points defining the shared edge between the polys lies.
bFoundNewPoint = GetPointAlongSharedEdge(
m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex],
m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex],
pCurrentStartPoly,
pTestPoly,
fEntityRadius,
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ],
bLineActuallyIntersectsEdge,
domain
);
}
#if __BREAK_ON_POS
if(vBreakOnPos.IsClose(m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex], 0.25f))
bPause = true;
#endif
if(bLineActuallyIntersectsEdge)
{
// No need to add a new point here after all, since the line from start->test points
// in fact intersects the shared edge between the two polygons. By NOT adding the point
// here, we'll enable ourselves more useful path points later on in the route.
}
else if(!bFoundNewPoint)
{
// If the pTestPoly is an alternative starting polygon, then there is not necessarily an adjacency with the
// previous polygon (which will be the m_pStartPoly). In this case just assume we have LOS, because this was
// a prerequisive of having marked the polygon as 'NAVMESHPOLY_ALTERNATIVE_STARTING_POLY'
if( pTestPoly->TestFlags(NAVMESHPOLY_ALTERNATIVE_STARTING_POLY) && (pTestPoly->m_PathParentPoly == m_Vars.m_pStartPoly || pTestPoly->m_PathParentPoly == NULL))
{
}
else
{
#if NAVMESH_OPTIMISATIONS_OFF
//Assertf(false, "!bFoundNewPoint :-(");
#endif
// 7/12/05 - This line used to just return FALSE. However that was causing problems because the
// last point in the route might not be at/near the target & we'd get navmesh routes not completing.
// I've just added the midpoint between the two polys & will hope for the best.
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] =
(m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex] +
m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex]) * 0.5f;
#if __BREAK_ON_POS
if(vBreakOnPos.IsClose(m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex], 0.25f))
bPause = true;
#endif
pPathRequest->m_iNumPoints++;
if(pPathRequest->m_iNumPoints >= iMaxNumPoints)
return true;
bPointAdded = m_Vars.InsertPoly(iCurrentStartIndex + 1,
pPathRequest->m_PathPoints[pPathRequest->m_iNumPoints -1],
pCurrentStartPoly,
m_Vars.m_iPolyWaypointFlags[iCurrentStartIndex]);
}
}
else
{
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = pCurrentStartPoly; //pTestPoly; //NULL;
pPathRequest->m_iNumPoints++;
if(pPathRequest->m_iNumPoints >= iMaxNumPoints)
return true;
bPointAdded = m_Vars.InsertPoly(iCurrentStartIndex + 1,
pPathRequest->m_PathPoints[pPathRequest->m_iNumPoints - 1],
pPathRequest->m_PathPolys[pPathRequest->m_iNumPoints - 1],
m_Vars.m_iPolyWaypointFlags[iCurrentStartIndex]);
}
}
iCurrentStartIndex = iCurrentTestIndex;
pCurrentStartPoly = m_Vars.m_PathPolys[iCurrentStartIndex];
vCurrentStartPos = m_Vars.m_vClosestPointInPathPolys[iCurrentStartIndex];
if(iCurrentTestIndex != 0 && !bPointAdded)
{
// Add a new point in the destination poly, this will become the new starting point
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = m_Vars.m_vClosestPointInPathPolys[iCurrentTestIndex];
// NB : we could set this to be either poly, and we allow us to modify the point later??
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = pCurrentStartPoly;
// Prev point will have been standard
pPathRequest->m_WaypointFlags[pPathRequest->m_iNumPoints].Clear();
pPathRequest->m_iNumPoints++;
if(pPathRequest->m_iNumPoints >= iMaxNumPoints)
return true;
}
iCurrentTestIndex++;
}
//************************************************************************
// Clear line-of-sight / didn't hit an openable/pushable/climbable object
else
{
iCurrentTestIndex++;
}
}
if( pPathRequest->m_iNumPoints == 1)
{
if( (pPathRequest->m_PathPoints[0] - m_Vars.m_vClosestPointInPathPolys[0]).Mag2() < 0.001f )
{
pPathRequest->m_PathPoints[1] = m_Vars.m_vClosestPointInPathPolys[m_Vars.m_iNumPathPolys-1];
pPathRequest->m_PathPolys[1] = m_Vars.m_PathPolys[m_Vars.m_iNumPathPolys-1];
pPathRequest->m_WaypointFlags[1] = m_Vars.m_iPolyWaypointFlags[m_Vars.m_iNumPathPolys-1];
pPathRequest->m_iNumPoints++;
}
}
//**************************************************************************
// Make sure that the final waypoint is added to the path, if not already
if( (pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints-1 ] - vEndWaypoint).XYMag2() > 0.01f )
{
//*********************************************************************************
// If we can directly see the final waypoint, from the penultimate point, then we
// just replace the final point rather than appending another..
if( pPathRequest->m_iNumPoints >= 2 )
{
bNavMeshLOS = TestNavMeshLOS(
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints-2 ],
vEndWaypoint,
pEndPoly,
m_Vars.m_PathPolys[ pPathRequest->m_iNumPoints-2 ],
NULL,
m_Vars.m_iLosFlags,
domain);
if(bNavMeshLOS && CPathServer::m_eObjectAvoidanceMode != CPathServer::ENoObjectAvoidance)
{
bDynObjLOS = TestDynamicObjectLOS(
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints-2 ],
vEndWaypoint
);
}
}
if( ! (bNavMeshLOS && bDynObjLOS) )
{
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints ] = vEndWaypoint;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints ] = pEndPoly;
pPathRequest->m_WaypointFlags[ pPathRequest->m_iNumPoints ] = m_Vars.m_iPolyWaypointFlags[m_Vars.m_iNumPathPolys-1];
pPathRequest->m_iNumPoints++;
}
else
{
pPathRequest->m_PathPoints[ pPathRequest->m_iNumPoints-1 ] = vEndWaypoint;
pPathRequest->m_PathPolys[ pPathRequest->m_iNumPoints-1 ] = pEndPoly;
}
}
return true;
}