2019-12-26 14:45:42 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
2018-03-27 14:27:07 -04:00
# include "NavMesh/PImplRecastNavMesh.h"
# include "NavigationSystem.h"
2014-03-14 14:13:41 -04:00
# if WITH_RECAST
// recast includes
2018-03-27 14:27:07 -04:00
# include "NavMesh/RecastHelpers.h"
# include "NavMesh/RecastVersion.h"
2014-03-14 14:13:41 -04:00
// recast includes
2016-11-23 15:48:37 -05:00
# include "Detour/DetourNode.h"
2021-10-25 20:05:28 -04:00
# include "Detour/DetourNavMesh.h"
2016-11-23 15:48:37 -05:00
# include "Recast/RecastAlloc.h"
2021-11-18 14:37:34 -05:00
# include "DetourTileCache/DetourTileCacheBuilder.h"
2018-03-27 14:27:07 -04:00
# include "NavAreas/NavArea.h"
# include "NavMesh/RecastNavMeshGenerator.h"
# include "NavMesh/RecastQueryFilter.h"
# include "NavLinkCustomInterface.h"
2014-10-27 08:35:45 -04:00
# include "VisualLogger/VisualLogger.h"
Remove more headers from Engine.h (StaticMeshResources.h, AnimTree.h, SkeletalMeshTypes.h, SkeletalMeshActor.h, LightingBuildOptions.h, PixelFormat.h, WorldComposition.h, VisualLog.h, StaticLighting.h, Lightmap.h, ShadowMap.h, Model.h)
2014-05-29 17:21:47 -04:00
2021-10-25 20:05:28 -04:00
# include "Misc/LargeWorldCoordinates.h"
# include "DebugUtils/DebugDrawLargeWorldCoordinates.h"
2014-05-29 17:06:50 -04:00
2014-03-14 14:13:41 -04:00
//----------------------------------------------------------------------//
// bunch of compile-time checks to assure types used by Recast and our
// mid-layer are the same size
//----------------------------------------------------------------------//
2014-06-16 08:04:54 -04:00
static_assert ( sizeof ( NavNodeRef ) = = sizeof ( dtPolyRef ) , " NavNodeRef and dtPolyRef should be the same size. " ) ;
static_assert ( RECAST_MAX_AREAS < = DT_MAX_AREAS , " Number of allowed areas cannot exceed DT_MAX_AREAS. " ) ;
static_assert ( RECAST_STRAIGHTPATH_OFFMESH_CONNECTION = = DT_STRAIGHTPATH_OFFMESH_CONNECTION , " Path flags values differ. " ) ;
static_assert ( RECAST_UNWALKABLE_POLY_COST = = DT_UNWALKABLE_POLY_COST , " Unwalkable poly cost differ. " ) ;
2021-10-25 20:05:28 -04:00
static_assert ( TIsSame < FVector : : FReal , dtReal > : : Value , " FReal and dtReal must be the same type! " ) ;
static_assert ( TIsSame < FVector : : FReal , rcReal > : : Value , " FReal and rcReal must be the same type! " ) ;
static_assert ( TIsSame < FVector : : FReal , duReal > : : Value , " FReal and duReal must be the same type! " ) ;
2014-03-14 14:13:41 -04:00
/// Helper for accessing navigation query from different threads
2014-04-23 19:29:53 -04:00
# define INITIALIZE_NAVQUERY_SIMPLE(NavQueryVariable, NumNodes) \
dtNavMeshQuery NavQueryVariable##Private; \
dtNavMeshQuery& NavQueryVariable = IsInGameThread() ? SharedNavQuery : NavQueryVariable##Private; \
NavQueryVariable.init(DetourNavMesh, NumNodes);
# define INITIALIZE_NAVQUERY(NavQueryVariable, NumNodes, LinkFilter) \
dtNavMeshQuery NavQueryVariable##Private; \
dtNavMeshQuery& NavQueryVariable = IsInGameThread() ? SharedNavQuery : NavQueryVariable##Private; \
NavQueryVariable.init(DetourNavMesh, NumNodes, &LinkFilter);
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
static void * DetourMalloc ( int Size , dtAllocHint Hint )
2014-03-14 14:13:41 -04:00
{
2020-01-16 14:32:33 -05:00
LLM_SCOPE ( ELLMTag : : NavigationRecast ) ;
2014-03-14 14:13:41 -04:00
void * Result = FMemory : : Malloc ( uint32 ( Size ) ) ;
# if STATS
const uint32 ActualSize = FMemory : : GetAllocSize ( Result ) ;
2021-10-25 20:05:28 -04:00
switch ( Hint )
{
case DT_ALLOC_TEMP :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourTEMP , ActualSize ) ;
break ;
case DT_ALLOC_PERM_AVOIDANCE :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_AVOIDANCE , ActualSize ) ;
break ;
case DT_ALLOC_PERM_CROWD :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_CROWD , ActualSize ) ;
break ;
case DT_ALLOC_PERM_LOOKUP :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_LOOKUP , ActualSize ) ;
break ;
case DT_ALLOC_PERM_NAVQUERY :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_NAVQUERY , ActualSize ) ;
break ;
case DT_ALLOC_PERM_NAVMESH :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_NAVMESH , ActualSize ) ;
break ;
case DT_ALLOC_PERM_NODE_POOL :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_NODE_POOL , ActualSize ) ;
break ;
case DT_ALLOC_PERM_PATH_CORRIDOR :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_PATH_CORRIDOR , ActualSize ) ;
break ;
case DT_ALLOC_PERM_PATH_QUEUE :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_PATH_QUEUE , ActualSize ) ;
break ;
case DT_ALLOC_PERM_PROXIMITY_GRID :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_PROXY_GRID , ActualSize ) ;
break ;
case DT_ALLOC_PERM_TILE_DATA :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILE_DATA , ActualSize ) ;
break ;
case DT_ALLOC_PERM_TILE_DYNLINK_OFFMESH :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILE_DYNLINK_OFFMESH , ActualSize ) ;
break ;
case DT_ALLOC_PERM_TILE_DYNLINK_CLUSTER :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILE_DYNLINK_CLUSTER , ActualSize ) ;
break ;
case DT_ALLOC_PERM_TILES :
INC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILES , ActualSize ) ;
break ;
default :
ensureMsgf ( false , TEXT ( " Unsupported allocation hint %d " ) , Hint ) ;
break ;
}
2015-12-02 16:42:06 -05:00
INC_DWORD_STAT_BY ( STAT_NavigationMemory , ActualSize ) ;
INC_MEMORY_STAT_BY ( STAT_Navigation_RecastMemory , ActualSize ) ;
2014-03-14 14:13:41 -04:00
# endif // STATS
return Result ;
}
static void * RecastMalloc ( int Size , rcAllocHint )
{
2020-01-16 14:32:33 -05:00
LLM_SCOPE ( ELLMTag : : NavigationRecast ) ;
2014-03-14 14:13:41 -04:00
void * Result = FMemory : : Malloc ( uint32 ( Size ) ) ;
# if STATS
const uint32 ActualSize = FMemory : : GetAllocSize ( Result ) ;
2015-12-02 16:42:06 -05:00
INC_DWORD_STAT_BY ( STAT_NavigationMemory , ActualSize ) ;
INC_MEMORY_STAT_BY ( STAT_Navigation_RecastMemory , ActualSize ) ;
2014-03-14 14:13:41 -04:00
# endif // STATS
return Result ;
}
2021-10-25 20:05:28 -04:00
static void DetourFree ( void * Original , dtAllocHint Hint )
{
# if STATS
const uint32 Size = FMemory : : GetAllocSize ( Original ) ;
switch ( Hint )
{
case DT_ALLOC_TEMP :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourTEMP , Size ) ;
break ;
case DT_ALLOC_PERM_AVOIDANCE :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_AVOIDANCE , Size ) ;
break ;
case DT_ALLOC_PERM_CROWD :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_CROWD , Size ) ;
break ;
case DT_ALLOC_PERM_LOOKUP :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_LOOKUP , Size ) ;
break ;
case DT_ALLOC_PERM_NAVQUERY :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_NAVQUERY , Size ) ;
break ;
case DT_ALLOC_PERM_NAVMESH :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_NAVMESH , Size ) ;
break ;
case DT_ALLOC_PERM_NODE_POOL :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_NODE_POOL , Size ) ;
break ;
case DT_ALLOC_PERM_PATH_CORRIDOR :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_PATH_CORRIDOR , Size ) ;
break ;
case DT_ALLOC_PERM_PATH_QUEUE :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_PATH_QUEUE , Size ) ;
break ;
case DT_ALLOC_PERM_PROXIMITY_GRID :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_PROXY_GRID , Size ) ;
break ;
case DT_ALLOC_PERM_TILE_DATA :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILE_DATA , Size ) ;
break ;
case DT_ALLOC_PERM_TILE_DYNLINK_OFFMESH :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILE_DYNLINK_OFFMESH , Size ) ;
break ;
case DT_ALLOC_PERM_TILE_DYNLINK_CLUSTER :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILE_DYNLINK_CLUSTER , Size ) ;
break ;
case DT_ALLOC_PERM_TILES :
DEC_MEMORY_STAT_BY ( STAT_Navigation_DetourPERM_TILES , Size ) ;
break ;
default :
ensureMsgf ( false , TEXT ( " Unsupported allocation hint %d " ) , Hint ) ;
break ;
}
DEC_DWORD_STAT_BY ( STAT_NavigationMemory , Size ) ;
DEC_MEMORY_STAT_BY ( STAT_Navigation_RecastMemory , Size ) ;
# endif // STATS
FMemory : : Free ( Original ) ;
}
static void RecastFree ( void * Original )
2014-03-14 14:13:41 -04:00
{
# if STATS
const uint32 Size = FMemory : : GetAllocSize ( Original ) ;
2021-10-25 20:05:28 -04:00
DEC_DWORD_STAT_BY ( STAT_NavigationMemory , Size ) ;
2015-12-02 16:42:06 -05:00
DEC_MEMORY_STAT_BY ( STAT_Navigation_RecastMemory , Size ) ;
2014-03-14 14:13:41 -04:00
# endif // STATS
FMemory : : Free ( Original ) ;
}
2021-10-25 20:05:28 -04:00
static void DetourStatsPostAddTile ( const dtMeshTile & TileAdded )
{
FDetourTileLayout TileLayout ( TileAdded ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileMemory , TileLayout . TileSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileMeshHeaderMemory , TileLayout . HeaderSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileNavVertsMemory , TileLayout . VertsSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileNavPolysMemory , TileLayout . PolysSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileLinksMemory , TileLayout . LinksSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileDetailMeshesMemory , TileLayout . DetailMeshesSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileDetailVertsMemory , TileLayout . DetailVertsSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileDetailTrisMemory , TileLayout . DetailTrisSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileBVTreeMemory , TileLayout . BvTreeSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileOffMeshConsMemory , TileLayout . OffMeshConsSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileOffMeshSegsMemory , TileLayout . OffMeshSegsSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTileClustersMemory , TileLayout . ClustersSize ) ;
INC_MEMORY_STAT_BY ( STAT_DetourTilePolyClustersMemory , TileLayout . PolyClustersSize ) ;
}
static void DetourStatsPreRemoveTile ( const dtMeshTile & TileRemoving )
{
FDetourTileLayout TileLayout ( TileRemoving ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileMemory , TileLayout . TileSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileMeshHeaderMemory , TileLayout . HeaderSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileNavVertsMemory , TileLayout . VertsSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileNavPolysMemory , TileLayout . PolysSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileLinksMemory , TileLayout . LinksSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileDetailMeshesMemory , TileLayout . DetailMeshesSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileDetailVertsMemory , TileLayout . DetailVertsSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileDetailTrisMemory , TileLayout . DetailTrisSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileBVTreeMemory , TileLayout . BvTreeSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileOffMeshConsMemory , TileLayout . OffMeshConsSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileOffMeshSegsMemory , TileLayout . OffMeshSegsSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTileClustersMemory , TileLayout . ClustersSize ) ;
DEC_MEMORY_STAT_BY ( STAT_DetourTilePolyClustersMemory , TileLayout . PolyClustersSize ) ;
}
2014-03-14 14:13:41 -04:00
struct FRecastInitialSetup
{
FRecastInitialSetup ( )
{
2021-10-25 20:05:28 -04:00
dtAllocSetCustom ( DetourMalloc , DetourFree ) ;
2014-03-14 14:13:41 -04:00
rcAllocSetCustom ( RecastMalloc , RecastFree ) ;
2021-10-25 20:05:28 -04:00
dtStatsSetCustom ( DetourStatsPostAddTile , DetourStatsPreRemoveTile ) ;
2014-03-14 14:13:41 -04:00
}
} ;
static FRecastInitialSetup RecastSetup ;
2021-10-25 20:05:28 -04:00
2014-03-14 14:13:41 -04:00
/****************************
* helpers
****************************/
2021-10-25 20:05:28 -04:00
static void Unr2RecastVector ( FVector const & V , FVector : : FReal * R )
2014-03-14 14:13:41 -04:00
{
// @todo: speed this up with axis swaps instead of a full transform?
FVector const RecastV = Unreal2RecastPoint ( V ) ;
R [ 0 ] = RecastV . X ;
R [ 1 ] = RecastV . Y ;
R [ 2 ] = RecastV . Z ;
}
2021-10-25 20:05:28 -04:00
static void Unr2RecastSizeVector ( FVector const & V , FVector : : FReal * R )
2014-03-14 14:13:41 -04:00
{
// @todo: speed this up with axis swaps instead of a full transform?
FVector const RecastVAbs = Unreal2RecastPoint ( V ) . GetAbs ( ) ;
R [ 0 ] = RecastVAbs . X ;
R [ 1 ] = RecastVAbs . Y ;
R [ 2 ] = RecastVAbs . Z ;
}
2021-10-25 20:05:28 -04:00
static FVector Recast2UnrVector ( FVector : : FReal const * R )
2014-03-14 14:13:41 -04:00
{
return Recast2UnrealPoint ( R ) ;
}
ENavigationQueryResult : : Type DTStatusToNavQueryResult ( dtStatus Status )
{
// @todo look at possible dtStatus values (in DetourStatus.h), there's more data that can be retrieved from it
2016-09-13 18:15:38 -04:00
// Partial paths are treated by Recast as Success while we treat as fail
2014-03-14 14:13:41 -04:00
return dtStatusSucceed ( Status ) ? ( dtStatusDetail ( Status , DT_PARTIAL_RESULT ) ? ENavigationQueryResult : : Fail : ENavigationQueryResult : : Success )
: ( dtStatusDetail ( Status , DT_INVALID_PARAM ) ? ENavigationQueryResult : : Error : ENavigationQueryResult : : Fail ) ;
}
//----------------------------------------------------------------------//
// FRecastQueryFilter();
//----------------------------------------------------------------------//
2014-05-29 17:46:51 -04:00
2014-08-06 13:22:12 -04:00
FRecastQueryFilter : : FRecastQueryFilter ( bool bIsVirtual )
: dtQueryFilter ( bIsVirtual )
2014-07-01 11:28:39 -04:00
{
SetExcludedArea ( RECAST_NULL_AREA ) ;
}
2014-05-29 17:46:51 -04:00
INavigationQueryFilterInterface * FRecastQueryFilter : : CreateCopy ( ) const
{
return new FRecastQueryFilter ( * this ) ;
}
2014-08-06 13:22:12 -04:00
void FRecastQueryFilter : : SetIsVirtual ( bool bIsVirtual )
{
2020-09-01 14:07:48 -04:00
isVirtual = bIsVirtual ;
2014-08-06 13:22:12 -04:00
}
2014-03-14 14:13:41 -04:00
void FRecastQueryFilter : : Reset ( )
{
2020-09-01 14:07:48 -04:00
// resetting just the cost data, we don't want to override the vf table like we did before (UE-95704)
new ( & data ) dtQueryFilterData ( ) ;
2014-07-01 11:28:39 -04:00
SetExcludedArea ( RECAST_NULL_AREA ) ;
2014-03-14 14:13:41 -04:00
}
void FRecastQueryFilter : : SetAreaCost ( uint8 AreaType , float Cost )
{
setAreaCost ( AreaType , Cost ) ;
}
void FRecastQueryFilter : : SetFixedAreaEnteringCost ( uint8 AreaType , float Cost )
{
# if WITH_FIXED_AREA_ENTERING_COST
setAreaFixedCost ( AreaType , Cost ) ;
# endif // WITH_FIXED_AREA_ENTERING_COST
}
void FRecastQueryFilter : : SetExcludedArea ( uint8 AreaType )
{
setAreaCost ( AreaType , DT_UNWALKABLE_POLY_COST ) ;
}
void FRecastQueryFilter : : SetAllAreaCosts ( const float * CostArray , const int32 Count )
{
// @todo could get away with memcopying to m_areaCost, but it's private and would require a little hack
// need to consider if it's wort a try (not sure there'll be any perf gain)
if ( Count > RECAST_MAX_AREAS )
{
UE_LOG ( LogNavigation , Warning , TEXT ( " FRecastQueryFilter: Trying to set cost to more areas than allowed! Discarding redundant values. " ) ) ;
}
const int32 ElementsCount = FPlatformMath : : Min ( Count , RECAST_MAX_AREAS ) ;
for ( int32 i = 0 ; i < ElementsCount ; + + i )
{
setAreaCost ( i , CostArray [ i ] ) ;
}
}
2021-10-25 20:05:28 -04:00
// LWC_TODO_AI: These costs should probably be FVector::FReal. Not until after 5.0!
2014-03-14 14:13:41 -04:00
void FRecastQueryFilter : : GetAllAreaCosts ( float * CostArray , float * FixedCostArray , const int32 Count ) const
{
2021-10-25 20:05:28 -04:00
const FVector : : FReal * DetourCosts = getAllAreaCosts ( ) ;
const FVector : : FReal * DetourFixedCosts = getAllFixedAreaCosts ( ) ;
const int32 NumItems = FMath : : Min ( Count , RECAST_MAX_AREAS ) ;
for ( int i = 0 ; i < NumItems ; + + i )
{
// LWC_TODO_AI: Replace with memcpy when we move to FReal. Not until after 5.0!
CostArray [ i ] = UE_REAL_TO_FLOAT_CLAMPED ( DetourCosts [ i ] ) ;
FixedCostArray [ i ] = UE_REAL_TO_FLOAT_CLAMPED ( DetourFixedCosts [ i ] ) ;
}
2014-03-14 14:13:41 -04:00
}
void FRecastQueryFilter : : SetBacktrackingEnabled ( const bool bBacktracking )
{
setIsBacktracking ( bBacktracking ) ;
}
2018-11-14 19:05:13 -05:00
void FRecastQueryFilter : : SetShouldIgnoreClosedNodes ( const bool bIgnoreClosed )
{
setShouldIgnoreClosedNodes ( bIgnoreClosed ) ;
}
2014-03-14 14:13:41 -04:00
bool FRecastQueryFilter : : IsBacktrackingEnabled ( ) const
{
return getIsBacktracking ( ) ;
}
2019-11-05 10:54:41 -05:00
float FRecastQueryFilter : : GetHeuristicScale ( ) const
{
2021-10-25 20:05:28 -04:00
return UE_REAL_TO_FLOAT ( getHeuristicScale ( ) ) ;
2019-11-05 10:54:41 -05:00
}
2014-03-14 14:13:41 -04:00
bool FRecastQueryFilter : : IsEqual ( const INavigationQueryFilterInterface * Other ) const
{
// @NOTE: not type safe, should be changed when another filter type is introduced
2014-04-23 18:49:47 -04:00
return FMemory : : Memcmp ( this , Other , sizeof ( FRecastQueryFilter ) ) = = 0 ;
2014-03-14 14:13:41 -04:00
}
void FRecastQueryFilter : : SetIncludeFlags ( uint16 Flags )
{
setIncludeFlags ( Flags ) ;
}
uint16 FRecastQueryFilter : : GetIncludeFlags ( ) const
{
return getIncludeFlags ( ) ;
}
void FRecastQueryFilter : : SetExcludeFlags ( uint16 Flags )
{
setExcludeFlags ( Flags ) ;
}
uint16 FRecastQueryFilter : : GetExcludeFlags ( ) const
{
return getExcludeFlags ( ) ;
}
2014-04-23 19:29:53 -04:00
bool FRecastSpeciaLinkFilter : : isLinkAllowed ( const int32 UserId ) const
{
2014-07-14 19:41:38 -04:00
const INavLinkCustomInterface * CustomLink = NavSys ? NavSys - > GetCustomLink ( UserId ) : NULL ;
2015-06-11 16:25:03 -04:00
return ( CustomLink ! = NULL ) & & CustomLink - > IsLinkPathfindingAllowed ( CachedOwnerOb ) ;
2014-04-23 19:29:53 -04:00
}
2015-06-11 16:25:03 -04:00
void FRecastSpeciaLinkFilter : : initialize ( )
{
CachedOwnerOb = SearchOwner . Get ( ) ;
}
2014-04-23 19:29:53 -04:00
2014-03-14 14:13:41 -04:00
//----------------------------------------------------------------------//
// FPImplRecastNavMesh
//----------------------------------------------------------------------//
FPImplRecastNavMesh : : FPImplRecastNavMesh ( ARecastNavMesh * Owner )
: NavMeshOwner ( Owner )
, DetourNavMesh ( NULL )
{
check ( Owner & & " Owner must never be NULL " ) ;
INC_DWORD_STAT_BY ( STAT_NavigationMemory
, Owner - > HasAnyFlags ( RF_ClassDefaultObject ) = = false ? sizeof ( * this ) : 0 ) ;
} ;
FPImplRecastNavMesh : : ~ FPImplRecastNavMesh ( )
2014-11-18 19:04:38 -05:00
{
ReleaseDetourNavMesh ( ) ;
DEC_DWORD_STAT_BY ( STAT_NavigationMemory , sizeof ( * this ) ) ;
} ;
void FPImplRecastNavMesh : : ReleaseDetourNavMesh ( )
2014-03-14 14:13:41 -04:00
{
// release navmesh only if we own it
2014-10-21 18:45:47 -04:00
if ( DetourNavMesh ! = nullptr )
2014-03-14 14:13:41 -04:00
{
dtFreeNavMesh ( DetourNavMesh ) ;
}
2014-10-21 18:45:47 -04:00
DetourNavMesh = nullptr ;
2015-01-12 21:18:44 -05:00
CompressedTileCacheLayers . Empty ( ) ;
2020-02-06 14:37:16 -05:00
# if RECAST_INTERNAL_DEBUG_DATA
DebugDataMap . Empty ( ) ;
# endif
2014-11-18 19:04:38 -05:00
}
2014-03-14 14:13:41 -04:00
2021-11-18 14:37:34 -05:00
// LWC_TODO_AI: Remove prior to UE5 5.0 Release.
// Currenlty floats are serialized as doubles for the navigation data so it can be loaded in LWC and non LWC builds (mainly used for regression testing).
class FSerializeFloatAsDoubleHack
{
public :
FSerializeFloatAsDoubleHack ( FArchive & InArchive )
: Archive ( InArchive )
{ }
/** Returns true if this archive is for loading data. */
FORCEINLINE bool IsLoading ( ) const
{
return Archive . IsLoading ( ) ;
}
/** Returns true if this archive is for saving data, this can also be a pre-save preparation archive. */
FORCEINLINE bool IsSaving ( ) const
{
return Archive . IsSaving ( ) ;
}
operator FArchive & ( ) const { return Archive ; }
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , uint8 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , int8 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , uint16 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , int16 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , uint32 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , bool & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , int32 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , float & Value )
{
double DoubleValue = Value ;
Ar . Archive < < DoubleValue ;
Value = DoubleValue ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , double & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
FORCEINLINE friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , uint64 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
friend FSerializeFloatAsDoubleHack & operator < < ( FSerializeFloatAsDoubleHack & Ar , int64 & Value )
{
Ar . Archive < < Value ;
return Ar ;
}
virtual void Serialize ( void * Value , int64 Length )
{
Archive . Serialize ( Value , Length ) ;
}
protected :
FArchive & Archive ;
} ;
2014-03-14 14:13:41 -04:00
/**
* Serialization.
* @param Ar - The archive with which to serialize.
* @returns true if serialization was successful.
*/
2021-11-18 14:37:34 -05:00
void FPImplRecastNavMesh : : Serialize ( FArchive & ArWrapped , int32 NavMeshVersion )
2014-03-14 14:13:41 -04:00
{
2021-11-18 14:37:34 -05:00
FSerializeFloatAsDoubleHack Ar ( ArWrapped ) ; // LWC_TODO_AI: Remove prior to UE5 5.0 Release.
2014-03-14 14:13:41 -04:00
//@todo: How to handle loading nav meshes saved w/ recast when recast isn't present????
if ( ! Ar . IsLoading ( ) & & DetourNavMesh = = NULL )
{
// nothing to write
return ;
}
// All we really need to do is read/write the data blob for each tile
if ( Ar . IsLoading ( ) )
{
// allocate the navmesh object
2014-11-21 22:57:05 -05:00
ReleaseDetourNavMesh ( ) ;
2014-03-14 14:13:41 -04:00
DetourNavMesh = dtAllocNavMesh ( ) ;
2014-11-21 22:57:05 -05:00
2014-03-14 14:13:41 -04:00
if ( DetourNavMesh = = NULL )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Error , TEXT ( " Failed to allocate Recast navmesh " ) ) ;
}
}
int32 NumTiles = 0 ;
2014-10-30 09:53:10 -04:00
TArray < int32 > TilesToSave ;
2014-03-14 14:13:41 -04:00
if ( Ar . IsSaving ( ) )
{
2014-10-30 09:53:10 -04:00
TilesToSave . Reserve ( DetourNavMesh - > getMaxTiles ( ) ) ;
2015-01-12 21:18:44 -05:00
2020-12-11 14:21:20 -04:00
const UNavigationSystemV1 * NavSys = FNavigationSystem : : GetCurrent < const UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) ;
2022-01-19 11:44:26 -05:00
if ( NavMeshOwner - > bIsWorldPartitioned )
2014-03-14 14:13:41 -04:00
{
2022-01-19 11:44:26 -05:00
// Ignore (leave TilesToSave empty so no tiles are saved).
// Navmesh data are stored in ANavigationDataChunkActor.
UE_LOG ( LogNavigation , VeryVerbose , TEXT ( " %s Ar.IsSaving() no tiles are being saved because bIsWorldPartitioned=true in %s. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) , * NavMeshOwner - > GetFullName ( ) ) ;
2014-10-30 09:53:10 -04:00
}
else
{
2022-01-19 11:44:26 -05:00
// Need to keep the check !IsRunningCommandlet() for the case where maps are cooked and saved from UCookCommandlet.
// In that flow the nav bounds are not set (no bounds means no tiles to save and the navmesh would be saved without tiles).
// This flow would benefit to be revisited since navmesh serialization should not be different whether it was run or not by a commandlet.
// Fixes missing navmesh regression (UE-103604).
if ( NavMeshOwner - > SupportsStreaming ( ) & & NavSys & & ! IsRunningCommandlet ( ) )
2014-03-14 14:13:41 -04:00
{
2022-01-19 11:44:26 -05:00
// We save only tiles that belongs to this level
GetNavMeshTilesIn ( NavMeshOwner - > GetNavigableBoundsInLevel ( NavMeshOwner - > GetLevel ( ) ) , TilesToSave ) ;
}
else
{
// Otherwise all valid tiles
dtNavMesh const * ConstNavMesh = DetourNavMesh ;
for ( int i = 0 ; i < ConstNavMesh - > getMaxTiles ( ) ; + + i )
2014-10-30 09:53:10 -04:00
{
2022-01-19 11:44:26 -05:00
const dtMeshTile * Tile = ConstNavMesh - > getTile ( i ) ;
if ( Tile ! = NULL & & Tile - > header ! = NULL & & Tile - > dataSize > 0 )
{
TilesToSave . Add ( i ) ;
}
2014-10-30 09:53:10 -04:00
}
2014-03-14 14:13:41 -04:00
}
}
2014-10-30 09:53:10 -04:00
NumTiles = TilesToSave . Num ( ) ;
2020-12-11 14:21:20 -04:00
UE_LOG ( LogNavigation , VeryVerbose , TEXT ( " %s Ar.IsSaving() %i tiles in %s. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) , NumTiles , * NavMeshOwner - > GetFullName ( ) ) ;
2014-03-14 14:13:41 -04:00
}
Ar < < NumTiles ;
dtNavMeshParams Params = * DetourNavMesh - > getParams ( ) ;
Ar < < Params . orig [ 0 ] < < Params . orig [ 1 ] < < Params . orig [ 2 ] ;
Ar < < Params . tileWidth ; ///< The width of each tile. (Along the x-axis.)
Ar < < Params . tileHeight ; ///< The height of each tile. (Along the z-axis.)
Ar < < Params . maxTiles ; ///< The maximum number of tiles the navigation mesh can contain.
Ar < < Params . maxPolys ;
2022-01-31 12:29:15 -05:00
if ( NavMeshOwner - > NavMeshVersion > = NAVMESHVER_OPTIM_FIX_SERIALIZE_PARAMS )
{
Ar < < Params . walkableHeight ;
Ar < < Params . walkableRadius ;
Ar < < Params . walkableClimb ;
Ar < < Params . bvQuantFactor ;
}
else
{
Params . walkableHeight = NavMeshOwner - > AgentHeight ;
Params . walkableRadius = NavMeshOwner - > AgentRadius ;
Params . walkableClimb = NavMeshOwner - > AgentMaxStepHeight ;
Params . bvQuantFactor = 1.f / NavMeshOwner - > CellSize ;
}
2014-03-14 14:13:41 -04:00
if ( Ar . IsLoading ( ) )
{
// at this point we can tell whether navmesh being loaded is in line
// ARecastNavMesh's params. If not, just skip it.
// assumes tiles are rectangular
2021-10-25 20:05:28 -04:00
const FVector : : FReal ActorsTileSize = FVector : : FReal ( int32 ( NavMeshOwner - > TileSizeUU / NavMeshOwner - > CellSize ) * NavMeshOwner - > CellSize ) ;
2014-03-14 14:13:41 -04:00
if ( ActorsTileSize ! = Params . tileWidth )
{
// just move archive position
2014-11-21 22:57:05 -05:00
ReleaseDetourNavMesh ( ) ;
2014-03-14 14:13:41 -04:00
for ( int i = 0 ; i < NumTiles ; + + i )
{
dtTileRef TileRef = MAX_uint64 ;
int32 TileDataSize = 0 ;
Ar < < TileRef < < TileDataSize ;
if ( TileRef = = MAX_uint64 | | TileDataSize = = 0 )
{
continue ;
}
unsigned char * TileData = NULL ;
TileDataSize = 0 ;
2015-02-11 07:45:26 -05:00
SerializeRecastMeshTile ( Ar , NavMeshVersion , TileData , TileDataSize ) ;
2014-03-14 14:13:41 -04:00
if ( TileData ! = NULL )
{
dtMeshHeader * const TileHeader = ( dtMeshHeader * ) TileData ;
2021-10-25 20:05:28 -04:00
dtFree ( TileHeader , DT_ALLOC_PERM_TILE_DATA ) ;
2015-01-12 21:18:44 -05:00
2021-10-13 12:36:30 -04:00
unsigned char * ComressedTileData = NULL ;
int32 CompressedTileDataSize = 0 ;
SerializeCompressedTileCacheData ( Ar , NavMeshVersion , ComressedTileData , CompressedTileDataSize ) ;
2021-10-25 20:05:28 -04:00
dtFree ( ComressedTileData , DT_ALLOC_PERM_TILE_DATA ) ;
2014-03-14 14:13:41 -04:00
}
}
}
else
{
// regular loading
dtStatus Status = DetourNavMesh - > init ( & Params ) ;
if ( dtStatusFailed ( Status ) )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Error , TEXT ( " Failed to initialize NavMesh " ) ) ;
}
for ( int i = 0 ; i < NumTiles ; + + i )
{
dtTileRef TileRef = MAX_uint64 ;
int32 TileDataSize = 0 ;
Ar < < TileRef < < TileDataSize ;
if ( TileRef = = MAX_uint64 | | TileDataSize = = 0 )
{
continue ;
}
unsigned char * TileData = NULL ;
TileDataSize = 0 ;
2015-02-11 07:45:26 -05:00
SerializeRecastMeshTile ( Ar , NavMeshVersion , TileData , TileDataSize ) ;
2014-03-14 14:13:41 -04:00
if ( TileData ! = NULL )
{
dtMeshHeader * const TileHeader = ( dtMeshHeader * ) TileData ;
DetourNavMesh - > addTile ( TileData , TileDataSize , DT_TILE_FREE_DATA , TileRef , NULL ) ;
2015-01-12 21:18:44 -05:00
// Serialize compressed tile cache layer
2021-10-13 12:36:30 -04:00
uint8 * ComressedTileData = nullptr ;
int32 CompressedTileDataSize = 0 ;
SerializeCompressedTileCacheData ( Ar , NavMeshVersion , ComressedTileData , CompressedTileDataSize ) ;
if ( CompressedTileDataSize > 0 )
2015-01-12 21:18:44 -05:00
{
2021-10-13 12:36:30 -04:00
AddTileCacheLayer ( TileHeader - > x , TileHeader - > y , TileHeader - > layer ,
FNavMeshTileData ( ComressedTileData , CompressedTileDataSize , TileHeader - > layer , Recast2UnrealBox ( TileHeader - > bmin , TileHeader - > bmax ) ) ) ;
2015-01-12 21:18:44 -05:00
}
2014-03-14 14:13:41 -04:00
}
}
}
}
else if ( Ar . IsSaving ( ) )
{
2015-01-12 21:18:44 -05:00
const bool bSupportsRuntimeGeneration = NavMeshOwner - > SupportsRuntimeGeneration ( ) ;
2014-03-14 14:13:41 -04:00
dtNavMesh const * ConstNavMesh = DetourNavMesh ;
2015-01-12 21:18:44 -05:00
2014-10-30 09:53:10 -04:00
for ( int TileIndex : TilesToSave )
2014-03-14 14:13:41 -04:00
{
2014-10-30 09:53:10 -04:00
const dtMeshTile * Tile = ConstNavMesh - > getTile ( TileIndex ) ;
dtTileRef TileRef = ConstNavMesh - > getTileRef ( Tile ) ;
int32 TileDataSize = Tile - > dataSize ;
Ar < < TileRef < < TileDataSize ;
2014-03-14 14:13:41 -04:00
2014-10-30 09:53:10 -04:00
unsigned char * TileData = Tile - > data ;
2015-02-11 07:45:26 -05:00
SerializeRecastMeshTile ( Ar , NavMeshVersion , TileData , TileDataSize ) ;
2015-01-12 21:18:44 -05:00
// Serialize compressed tile cache layer only if navmesh requires it
{
2015-06-25 00:20:07 -04:00
FNavMeshTileData TileCacheLayer ;
2015-01-12 21:18:44 -05:00
uint8 * CompressedData = nullptr ;
int32 CompressedDataSize = 0 ;
if ( bSupportsRuntimeGeneration )
{
2015-06-25 00:20:07 -04:00
TileCacheLayer = GetTileCacheLayer ( Tile - > header - > x , Tile - > header - > y , Tile - > header - > layer ) ;
2015-01-12 21:18:44 -05:00
CompressedData = TileCacheLayer . GetDataSafe ( ) ;
CompressedDataSize = TileCacheLayer . DataSize ;
}
2015-02-11 07:45:26 -05:00
SerializeCompressedTileCacheData ( Ar , NavMeshVersion , CompressedData , CompressedDataSize ) ;
2015-01-12 21:18:44 -05:00
}
2014-03-14 14:13:41 -04:00
}
}
}
2021-11-18 14:37:34 -05:00
void FPImplRecastNavMesh : : SerializeRecastMeshTile ( FArchive & ArWrapped , int32 NavMeshVersion , unsigned char * & TileData , int32 & TileDataSize )
2014-03-14 14:13:41 -04:00
{
// The strategy here is to serialize the data blob that is passed into addTile()
// @see dtCreateNavMeshData() for details on how this data is laid out
2021-11-18 14:37:34 -05:00
FSerializeFloatAsDoubleHack Ar ( ArWrapped ) ; // LWC_TODO_AI: Remove prior to UE5 5.0 Release.
2021-10-25 20:05:28 -04:00
FDetourTileSizeInfo SizeInfo ;
2014-03-14 14:13:41 -04:00
if ( Ar . IsSaving ( ) )
{
// fill in data to write
dtMeshHeader * const H = ( dtMeshHeader * ) TileData ;
2021-10-25 20:05:28 -04:00
SizeInfo . VertCount = H - > vertCount ;
SizeInfo . PolyCount = H - > polyCount ;
SizeInfo . MaxLinkCount = H - > maxLinkCount ;
SizeInfo . DetailMeshCount = H - > detailMeshCount ;
SizeInfo . DetailVertCount = H - > detailVertCount ;
SizeInfo . DetailTriCount = H - > detailTriCount ;
SizeInfo . BvNodeCount = H - > bvNodeCount ;
SizeInfo . OffMeshConCount = H - > offMeshConCount ;
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_SEGMENT_LINKS
2021-10-25 20:05:28 -04:00
SizeInfo . OffMeshSegConCount = H - > offMeshSegConCount ;
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_SEGMENT_LINKS
2021-10-25 20:05:28 -04:00
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2021-10-25 20:05:28 -04:00
SizeInfo . ClusterCount = H - > clusterCount ;
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
}
2021-10-25 20:05:28 -04:00
Ar < < SizeInfo . VertCount < < SizeInfo . PolyCount < < SizeInfo . MaxLinkCount ;
Ar < < SizeInfo . DetailMeshCount < < SizeInfo . DetailVertCount < < SizeInfo . DetailTriCount ;
Ar < < SizeInfo . BvNodeCount < < SizeInfo . OffMeshConCount < < SizeInfo . OffMeshSegConCount ;
Ar < < SizeInfo . ClusterCount ;
SizeInfo . OffMeshBase = SizeInfo . DetailMeshCount ;
const int32 polyClusterCount = SizeInfo . OffMeshBase ;
2014-03-14 14:13:41 -04:00
// calc sizes for our data so we know how much to allocate and where to read/write stuff
// note this may not match the on-disk size or the in-memory size on the machine that generated that data
2020-06-23 18:40:00 -04:00
2021-10-25 20:05:28 -04:00
FDetourTileLayout TileLayout ( SizeInfo ) ;
2014-03-14 14:13:41 -04:00
if ( Ar . IsLoading ( ) )
{
check ( TileData = = NULL ) ;
2021-10-25 20:05:28 -04:00
TileDataSize = TileLayout . TileSize ;
TileData = ( unsigned char * ) dtAlloc ( sizeof ( unsigned char ) * TileDataSize , DT_ALLOC_PERM_TILE_DATA ) ;
2014-03-14 14:13:41 -04:00
if ( ! TileData )
{
UE_LOG ( LogNavigation , Error , TEXT ( " Failed to alloc navmesh tile " ) ) ;
}
FMemory : : Memset ( TileData , 0 , TileDataSize ) ;
}
else if ( Ar . IsSaving ( ) )
{
// TileData and TileDataSize should already be set, verify
check ( TileData ! = NULL ) ;
2021-10-25 20:05:28 -04:00
check ( TileLayout . TileSize = = TileDataSize ) ;
2014-03-14 14:13:41 -04:00
}
if ( TileData ! = NULL )
{
// sort out where various data types do/will live
unsigned char * d = TileData ;
2021-10-25 20:05:28 -04:00
dtMeshHeader * Header = ( dtMeshHeader * ) d ; d + = TileLayout . HeaderSize ;
FVector : : FReal * NavVerts = ( FVector : : FReal * ) d ; d + = TileLayout . VertsSize ;
dtPoly * NavPolys = ( dtPoly * ) d ; d + = TileLayout . PolysSize ;
d + = TileLayout . LinksSize ; // @fixme, are links autogenerated on addTile?
dtPolyDetail * DetailMeshes = ( dtPolyDetail * ) d ; d + = TileLayout . DetailMeshesSize ;
FVector : : FReal * DetailVerts = ( FVector : : FReal * ) d ; d + = TileLayout . DetailVertsSize ;
unsigned char * DetailTris = ( unsigned char * ) d ; d + = TileLayout . DetailTrisSize ;
dtBVNode * BVTree = ( dtBVNode * ) d ; d + = TileLayout . BvTreeSize ;
dtOffMeshConnection * OffMeshCons = ( dtOffMeshConnection * ) d ; d + = TileLayout . OffMeshConsSize ;
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_SEGMENT_LINKS
2021-10-25 20:05:28 -04:00
dtOffMeshSegmentConnection * OffMeshSegs = ( dtOffMeshSegmentConnection * ) d ; d + = TileLayout . OffMeshSegsSize ;
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_SEGMENT_LINKS
# if WITH_NAVMESH_CLUSTER_LINKS
2021-10-25 20:05:28 -04:00
dtCluster * Clusters = ( dtCluster * ) d ; d + = TileLayout . ClustersSize ;
unsigned short * PolyClusters = ( unsigned short * ) d ; d + = TileLayout . PolyClustersSize ;
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
check ( d = = ( TileData + TileDataSize ) ) ;
// now serialize the data in the blob!
// header
2022-01-13 10:09:07 -05:00
Ar < < Header - > version < < Header - > x < < Header - > y ;
Ar < < Header - > layer < < Header - > polyCount < < Header - > vertCount ;
2014-03-14 14:13:41 -04:00
Ar < < Header - > maxLinkCount < < Header - > detailMeshCount < < Header - > detailVertCount < < Header - > detailTriCount ;
Ar < < Header - > bvNodeCount < < Header - > offMeshConCount < < Header - > offMeshBase ;
Ar < < Header - > bmin [ 0 ] < < Header - > bmin [ 1 ] < < Header - > bmin [ 2 ] ;
Ar < < Header - > bmax [ 0 ] < < Header - > bmax [ 1 ] < < Header - > bmax [ 2 ] ;
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2014-07-01 11:28:39 -04:00
Ar < < Header - > clusterCount ;
2020-06-23 18:40:00 -04:00
# else
2022-01-13 10:09:07 -05:00
unsigned short DummyClusterCount = 0 ;
2020-06-23 18:40:00 -04:00
Ar < < DummyClusterCount ;
# endif // WITH_NAVMESH_CLUSTER_LINKS
# if WITH_NAVMESH_SEGMENT_LINKS
2014-03-14 14:13:41 -04:00
Ar < < Header - > offMeshSegConCount < < Header - > offMeshSegPolyBase < < Header - > offMeshSegVertBase ;
2020-06-23 18:40:00 -04:00
# else
2022-01-13 10:09:07 -05:00
unsigned short DummySegmentInt = 0 ;
2020-06-23 18:40:00 -04:00
Ar < < DummySegmentInt < < DummySegmentInt < < DummySegmentInt ;
# endif // WITH_NAVMESH_SEGMENT_LINKS
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
// mesh and offmesh connection vertices, just an array of reals (one real triplet per vert)
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
FVector : : FReal * F = NavVerts ;
for ( int32 VertIdx = 0 ; VertIdx < SizeInfo . VertCount ; VertIdx + + )
2014-03-14 14:13:41 -04:00
{
Ar < < * F ; F + + ;
Ar < < * F ; F + + ;
Ar < < * F ; F + + ;
}
}
// mesh and off-mesh connection polys
2021-10-25 20:05:28 -04:00
for ( int32 PolyIdx = 0 ; PolyIdx < SizeInfo . PolyCount ; + + PolyIdx )
2014-03-14 14:13:41 -04:00
{
dtPoly & P = NavPolys [ PolyIdx ] ;
Ar < < P . firstLink ;
for ( uint32 VertIdx = 0 ; VertIdx < DT_VERTS_PER_POLYGON ; + + VertIdx )
{
Ar < < P . verts [ VertIdx ] ;
}
for ( uint32 NeiIdx = 0 ; NeiIdx < DT_VERTS_PER_POLYGON ; + + NeiIdx )
{
Ar < < P . neis [ NeiIdx ] ;
}
Ar < < P . flags < < P . vertCount < < P . areaAndtype ;
}
// serialize detail meshes
2021-10-25 20:05:28 -04:00
for ( int32 MeshIdx = 0 ; MeshIdx < SizeInfo . DetailMeshCount ; + + MeshIdx )
2014-03-14 14:13:41 -04:00
{
dtPolyDetail & DM = DetailMeshes [ MeshIdx ] ;
Ar < < DM . vertBase < < DM . triBase < < DM . vertCount < < DM . triCount ;
}
2021-10-25 20:05:28 -04:00
// serialize detail verts (one real triplet per vert)
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
FVector : : FReal * F = DetailVerts ;
for ( int32 VertIdx = 0 ; VertIdx < SizeInfo . DetailVertCount ; + + VertIdx )
2014-03-14 14:13:41 -04:00
{
Ar < < * F ; F + + ;
Ar < < * F ; F + + ;
Ar < < * F ; F + + ;
}
}
// serialize detail tris (4 one-byte indices per tri)
{
unsigned char * V = DetailTris ;
2021-10-25 20:05:28 -04:00
for ( int32 TriIdx = 0 ; TriIdx < SizeInfo . DetailTriCount ; + + TriIdx )
2014-03-14 14:13:41 -04:00
{
Ar < < * V ; V + + ;
Ar < < * V ; V + + ;
Ar < < * V ; V + + ;
Ar < < * V ; V + + ;
}
}
// serialize BV tree
2021-10-25 20:05:28 -04:00
for ( int32 NodeIdx = 0 ; NodeIdx < SizeInfo . BvNodeCount ; + + NodeIdx )
2014-03-14 14:13:41 -04:00
{
dtBVNode & Node = BVTree [ NodeIdx ] ;
Ar < < Node . bmin [ 0 ] < < Node . bmin [ 1 ] < < Node . bmin [ 2 ] ;
Ar < < Node . bmax [ 0 ] < < Node . bmax [ 1 ] < < Node . bmax [ 2 ] ;
Ar < < Node . i ;
}
// serialize off-mesh connections
2021-10-25 20:05:28 -04:00
for ( int32 ConnIdx = 0 ; ConnIdx < SizeInfo . OffMeshConCount ; + + ConnIdx )
2014-03-14 14:13:41 -04:00
{
dtOffMeshConnection & Conn = OffMeshCons [ ConnIdx ] ;
Ar < < Conn . pos [ 0 ] < < Conn . pos [ 1 ] < < Conn . pos [ 2 ] < < Conn . pos [ 3 ] < < Conn . pos [ 4 ] < < Conn . pos [ 5 ] ;
Ar < < Conn . rad < < Conn . poly < < Conn . flags < < Conn . side < < Conn . userId ;
}
2015-02-11 07:45:26 -05:00
if ( NavMeshVersion > = NAVMESHVER_OFFMESH_HEIGHT_BUG )
{
2021-10-25 20:05:28 -04:00
for ( int32 ConnIdx = 0 ; ConnIdx < SizeInfo . OffMeshConCount ; + + ConnIdx )
2015-02-11 07:45:26 -05:00
{
dtOffMeshConnection & Conn = OffMeshCons [ ConnIdx ] ;
Ar < < Conn . height ;
}
}
2021-10-25 20:05:28 -04:00
for ( int32 SegIdx = 0 ; SegIdx < SizeInfo . OffMeshSegConCount ; + + SegIdx )
2014-03-14 14:13:41 -04:00
{
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_SEGMENT_LINKS
2014-03-14 14:13:41 -04:00
dtOffMeshSegmentConnection & Seg = OffMeshSegs [ SegIdx ] ;
Ar < < Seg . startA [ 0 ] < < Seg . startA [ 1 ] < < Seg . startA [ 2 ] ;
Ar < < Seg . startB [ 0 ] < < Seg . startB [ 1 ] < < Seg . startB [ 2 ] ;
Ar < < Seg . endA [ 0 ] < < Seg . endA [ 1 ] < < Seg . endA [ 2 ] ;
Ar < < Seg . endB [ 0 ] < < Seg . endB [ 1 ] < < Seg . endB [ 2 ] ;
Ar < < Seg . rad < < Seg . firstPoly < < Seg . npolys < < Seg . flags < < Seg . userId ;
2020-06-23 18:40:00 -04:00
# else
2021-10-25 20:05:28 -04:00
FVector : : FReal DummySegmentConReal ;
2021-11-18 14:37:34 -05:00
unsigned int DummySegmentConInt ;
unsigned short DummySegmentConShort ;
unsigned char DummySegmentConChar ;
2021-10-25 20:05:28 -04:00
Ar < < DummySegmentConReal < < DummySegmentConReal < < DummySegmentConReal ; // real startA[3]; ///< Start point of segment A
Ar < < DummySegmentConReal < < DummySegmentConReal < < DummySegmentConReal ; // real endA[3]; ///< End point of segment A
Ar < < DummySegmentConReal < < DummySegmentConReal < < DummySegmentConReal ; // real startB[3]; ///< Start point of segment B
Ar < < DummySegmentConReal < < DummySegmentConReal < < DummySegmentConReal ; // real endB[3]; ///< End point of segment B
Ar < < DummySegmentConReal < < DummySegmentConShort < < DummySegmentConChar < < DummySegmentConChar < < DummySegmentConInt ; // real rad, short firstPoly, char npolys, char flags, int userId
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_SEGMENT_LINKS
2014-03-14 14:13:41 -04:00
}
// serialize clusters
2021-10-25 20:05:28 -04:00
for ( int32 CIdx = 0 ; CIdx < SizeInfo . ClusterCount ; + + CIdx )
2014-03-14 14:13:41 -04:00
{
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
dtCluster & Cluster = Clusters [ CIdx ] ;
Ar < < Cluster . center [ 0 ] < < Cluster . center [ 1 ] < < Cluster . center [ 2 ] ;
# else
2021-10-25 20:05:28 -04:00
FVector : : FReal DummyCluster [ 3 ] ;
2020-06-23 18:40:00 -04:00
Ar < < DummyCluster [ 0 ] < < DummyCluster [ 1 ] < < DummyCluster [ 2 ] ;
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
}
// serialize poly clusters map
{
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
unsigned short * C = PolyClusters ;
for ( int32 PolyClusterIdx = 0 ; PolyClusterIdx < polyClusterCount ; + + PolyClusterIdx )
{
Ar < < * C ; C + + ;
}
2020-06-23 18:40:00 -04:00
# else
unsigned short DummyPolyCluster = 0 ;
for ( int32 PolyClusterIdx = 0 ; PolyClusterIdx < polyClusterCount ; + + PolyClusterIdx )
{
Ar < < DummyPolyCluster ;
}
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
}
}
}
2021-11-18 14:37:34 -05:00
void FPImplRecastNavMesh : : SerializeCompressedTileCacheData ( FArchive & ArWrapped , int32 NavMeshVersion , unsigned char * & CompressedData , int32 & CompressedDataSize )
2015-01-12 21:18:44 -05:00
{
2021-11-18 14:37:34 -05:00
// LWC_TODO_AI: Remove prior to UE5 5.0 Release.
FSerializeFloatAsDoubleHack Ar ( ArWrapped ) ;
constexpr int32 EmptyDataValue = - 1 ;
2015-01-12 21:18:44 -05:00
2021-11-18 14:37:34 -05:00
// Note when saving the CompressedDataSize is either 0 or it must be big enough to include the size of the uncompressed dtTileCacheLayerHeader.
checkf ( ( Ar . IsSaving ( ) = = false ) | | CompressedDataSize = = 0 | | CompressedDataSize > = dtAlign ( sizeof ( dtTileCacheLayerHeader ) ) , TEXT ( " When saving CompressedDataSize must either be zero or large enough to hold dtTileCacheLayerHeader! " ) ) ;
checkf ( ( Ar . IsSaving ( ) = = false ) | | CompressedDataSize = = 0 | | CompressedData ! = nullptr , TEXT ( " When saving CompressedDataSize must either be zero or CompressedData must be != nullptr " ) ) ;
if ( Ar . IsLoading ( ) )
2015-01-12 21:18:44 -05:00
{
2021-11-18 14:37:34 -05:00
// Initialize to 0 if we are loading as this is calculated and used duiring processing.
CompressedDataSize = 0 ;
}
// There are 3 cases that need to be serialized here, no header no compresseed data, header only no compressed data or header and compressed data.
// CompressedDataSizeNoHeader == NoHeaderValue, indicates we have no header and no compressed data.
// CompressedDataSizeNoHeader == 0, indicates we have a header only no compressed data.
// CompressedDataSizeNoHeader > 0, indicates we have a header and compressed data.
int32 CompressedDataSizeNoHeader = 0 ;
if ( Ar . IsSaving ( ) )
{
// Handle invalid CompressedDataSize ( i.e. CompressedDataSize > 0 and CompressedDataSize < dtAlign(sizeof(dtTileCacheLayerHeader))
// as well as valid CompressedDataSize == 0, to make this function atleast as robust as it was.
if ( CompressedDataSize < dtAlign ( sizeof ( dtTileCacheLayerHeader ) ) )
2015-01-12 21:18:44 -05:00
{
2021-11-18 14:37:34 -05:00
CompressedDataSizeNoHeader = EmptyDataValue ;
2015-01-12 21:18:44 -05:00
}
2021-11-18 14:37:34 -05:00
else
{
CompressedDataSizeNoHeader = CompressedDataSize - dtAlign ( sizeof ( dtTileCacheLayerHeader ) ) ;
}
}
2015-01-12 21:18:44 -05:00
2021-11-18 14:37:34 -05:00
Ar < < CompressedDataSizeNoHeader ;
const bool bHasHeader = CompressedDataSizeNoHeader > = 0 ;
if ( ! bHasHeader )
{
return ;
}
if ( Ar . IsLoading ( ) )
{
CompressedDataSize = CompressedDataSizeNoHeader + dtAlign ( sizeof ( dtTileCacheLayerHeader ) ) ;
CompressedData = ( unsigned char * ) dtAlloc ( sizeof ( unsigned char ) * CompressedDataSize , DT_ALLOC_PERM_TILE_DATA ) ;
if ( ! CompressedData )
{
UE_LOG ( LogNavigation , Error , TEXT ( " Failed to alloc tile compressed data " ) ) ;
}
FMemory : : Memset ( CompressedData , 0 , CompressedDataSize ) ;
}
check ( CompressedData ! = nullptr ) ;
// Serialize dtTileCacheLayerHeader by hand so we can account for the FReals always being serialized as doubles
dtTileCacheLayerHeader * Header = ( dtTileCacheLayerHeader * ) CompressedData ;
Ar < < Header - > version ;
Ar < < Header - > tx ;
Ar < < Header - > ty ;
Ar < < Header - > tlayer ;
for ( int i = 0 ; i < 3 ; + + i )
{
Ar < < Header - > bmin [ i ] ;
Ar < < Header - > bmax [ i ] ;
}
Ar < < Header - > hmin ;
Ar < < Header - > hmax ;
Ar < < Header - > width ;
Ar < < Header - > height ;
Ar < < Header - > minx ;
Ar < < Header - > maxx ;
Ar < < Header - > miny ;
Ar < < Header - > maxy ;
if ( CompressedDataSizeNoHeader > 0 )
{
// @todo this does not appear to be accounting for potential endian differences!
Ar . Serialize ( CompressedData + dtAlign ( sizeof ( dtTileCacheLayerHeader ) ) , CompressedDataSizeNoHeader ) ;
2015-01-12 21:18:44 -05:00
}
}
2014-11-21 22:57:05 -05:00
void FPImplRecastNavMesh : : SetRecastMesh ( dtNavMesh * NavMesh )
2014-03-14 14:13:41 -04:00
{
if ( NavMesh = = DetourNavMesh )
{
return ;
}
2014-11-21 22:57:05 -05:00
ReleaseDetourNavMesh ( ) ;
2014-03-14 14:13:41 -04:00
DetourNavMesh = NavMesh ;
2014-05-22 14:14:52 -04:00
if ( NavMeshOwner )
{
NavMeshOwner - > UpdateNavObject ( ) ;
}
2016-09-22 15:33:34 -04:00
// reapply area sort order in new detour navmesh
OnAreaCostChanged ( ) ;
2014-03-14 14:13:41 -04:00
}
2016-01-08 19:10:43 -05:00
void FPImplRecastNavMesh : : Raycast ( const FVector & StartLoc , const FVector & EndLoc , const FNavigationQueryFilter & InQueryFilter , const UObject * Owner ,
ARecastNavMesh : : FRaycastResult & RaycastResult , NavNodeRef StartNode ) const
2014-03-14 14:13:41 -04:00
{
if ( DetourNavMesh = = NULL | | NavMeshOwner = = NULL )
{
return ;
}
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( InQueryFilter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
if ( QueryFilter = = NULL )
{
2016-01-08 19:10:43 -05:00
UE_VLOG ( NavMeshOwner , LogNavigation , Warning , TEXT ( " FPImplRecastNavMesh::Raycast failing due to QueryFilter == NULL " ) ) ;
2014-03-14 14:13:41 -04:00
return ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , InQueryFilter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
const FVector NavExtent = NavMeshOwner - > GetModifiedQueryExtent ( NavMeshOwner - > GetDefaultQueryExtent ( ) ) ;
const FVector : : FReal Extent [ 3 ] = { NavExtent . X , NavExtent . Z , NavExtent . Y } ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
const FVector RecastStart = Unreal2RecastPoint ( StartLoc ) ;
const FVector RecastEnd = Unreal2RecastPoint ( EndLoc ) ;
2014-03-14 14:13:41 -04:00
2016-01-08 19:10:43 -05:00
if ( StartNode = = INVALID_NAVNODEREF )
{
NavQuery . findNearestContainingPoly ( & RecastStart . X , Extent , QueryFilter , & StartNode , NULL ) ;
}
2014-08-06 16:12:41 -04:00
2016-01-28 16:03:26 -05:00
NavNodeRef EndNode = INVALID_NAVNODEREF ;
NavQuery . findNearestContainingPoly ( & RecastEnd . X , Extent , QueryFilter , & EndNode , NULL ) ;
2014-08-06 16:12:41 -04:00
if ( StartNode ! = INVALID_NAVNODEREF )
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
FVector : : FReal RecastHitNormal [ 3 ] ;
2015-07-29 07:56:50 -04:00
2014-08-06 16:12:41 -04:00
const dtStatus RaycastStatus = NavQuery . raycast ( StartNode , & RecastStart . X , & RecastEnd . X
2015-07-29 07:56:50 -04:00
, QueryFilter , & RaycastResult . HitTime , RecastHitNormal
2014-08-06 16:12:41 -04:00
, RaycastResult . CorridorPolys , & RaycastResult . CorridorPolysCount , RaycastResult . GetMaxCorridorSize ( ) ) ;
2015-07-29 07:56:50 -04:00
RaycastResult . HitNormal = Recast2UnrVector ( RecastHitNormal ) ;
2016-01-28 16:03:26 -05:00
RaycastResult . bIsRaycastEndInCorridor = dtStatusSucceed ( RaycastStatus ) & & ( RaycastResult . GetLastNodeRef ( ) = = EndNode ) ;
2014-08-06 16:12:41 -04:00
}
2014-10-18 19:14:36 -04:00
else
{
RaycastResult . HitTime = 0.f ;
2014-11-26 10:01:12 -05:00
RaycastResult . HitNormal = ( StartLoc - EndLoc ) . GetSafeNormal ( ) ;
2014-10-18 19:14:36 -04:00
}
2014-03-14 14:13:41 -04:00
}
2019-11-05 10:54:41 -05:00
ENavigationQueryResult : : Type FPImplRecastNavMesh : : FindPath ( const FVector & StartLoc , const FVector & EndLoc , FNavMeshPath & Path , const FNavigationQueryFilter & Filter , const UObject * Owner ) const
{
return FindPath ( StartLoc , EndLoc , FLT_MAX , Path , Filter , Owner ) ;
}
2014-03-14 14:13:41 -04:00
// @TODONAV
2019-11-06 10:31:29 -05:00
ENavigationQueryResult : : Type FPImplRecastNavMesh : : FindPath ( const FVector & StartLoc , const FVector & EndLoc , const float CostLimit , FNavMeshPath & Path , const FNavigationQueryFilter & InQueryFilter , const UObject * Owner ) const
2014-03-14 14:13:41 -04:00
{
// temporarily disabling this check due to it causing too much "crashes"
// @todo but it needs to be back at some point since it realy checks for a buggy setup
//ensure(DetourNavMesh != NULL || NavMeshOwner->bRebuildAtRuntime == false);
if ( DetourNavMesh = = NULL | | NavMeshOwner = = NULL )
{
return ENavigationQueryResult : : Error ;
}
const FRecastQueryFilter * FilterImplementation = ( const FRecastQueryFilter * ) ( InQueryFilter . GetImplementation ( ) ) ;
if ( FilterImplementation = = NULL )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Error , TEXT ( " FPImplRecastNavMesh::FindPath failed due to passed filter having NULL implementation! " ) ) ;
return ENavigationQueryResult : : Error ;
}
const dtQueryFilter * QueryFilter = FilterImplementation - > GetAsDetourQueryFilter ( ) ;
if ( QueryFilter = = NULL )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Warning , TEXT ( " FPImplRecastNavMesh::FindPath failing due to QueryFilter == NULL " ) ) ;
return ENavigationQueryResult : : Error ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , InQueryFilter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
FVector RecastStartPos , RecastEndPos ;
2014-03-14 14:13:41 -04:00
NavNodeRef StartPolyID , EndPolyID ;
const bool bCanSearch = InitPathfinding ( StartLoc , EndLoc , NavQuery , QueryFilter , RecastStartPos , StartPolyID , RecastEndPos , EndPolyID ) ;
if ( ! bCanSearch )
{
return ENavigationQueryResult : : Error ;
}
// get path corridor
2014-08-21 20:30:51 -04:00
dtQueryResult PathResult ;
2019-11-06 10:31:29 -05:00
const dtStatus FindPathStatus = NavQuery . findPath ( StartPolyID , EndPolyID , & RecastStartPos . X , & RecastEndPos . X , CostLimit , QueryFilter , PathResult , 0 ) ;
2014-03-14 14:13:41 -04:00
2020-10-29 13:38:15 -04:00
return PostProcessPathInternal ( FindPathStatus , Path , NavQuery , QueryFilter , StartPolyID , EndPolyID , RecastStartPos , RecastEndPos , PathResult ) ;
}
ENavigationQueryResult : : Type FPImplRecastNavMesh : : PostProcessPathInternal ( dtStatus FindPathStatus , FNavMeshPath & Path ,
const dtNavMeshQuery & NavQuery , const dtQueryFilter * QueryFilter ,
NavNodeRef StartPolyID , NavNodeRef EndPolyID ,
2021-10-25 20:05:28 -04:00
const FVector & RecastStartPos , const FVector & RecastEndPos ,
2020-10-29 13:38:15 -04:00
dtQueryResult & PathResult ) const
{
2014-03-14 14:13:41 -04:00
// check for special case, where path has not been found, and starting polygon
// was the one closest to the target
2014-08-21 20:30:51 -04:00
if ( PathResult . size ( ) = = 1 & & dtStatusDetail ( FindPathStatus , DT_PARTIAL_RESULT ) )
2014-03-14 14:13:41 -04:00
{
// in this case we find a point on starting polygon, that's closest to destination
// and store it as path end
2021-10-25 20:05:28 -04:00
FVector RecastHandPlacedPathEnd ;
2014-03-14 14:13:41 -04:00
NavQuery . closestPointOnPolyBoundary ( StartPolyID , & RecastEndPos . X , & RecastHandPlacedPathEnd . X ) ;
2015-03-11 09:29:07 -04:00
new ( Path . GetPathPoints ( ) ) FNavPathPoint ( Recast2UnrVector ( & RecastStartPos . X ) , StartPolyID ) ;
2014-07-14 19:41:38 -04:00
new ( Path . GetPathPoints ( ) ) FNavPathPoint ( Recast2UnrVector ( & RecastHandPlacedPathEnd . X ) , StartPolyID ) ;
2014-03-14 14:13:41 -04:00
2014-08-21 20:30:51 -04:00
Path . PathCorridor . Add ( PathResult . getRef ( 0 ) ) ;
2014-03-14 14:13:41 -04:00
Path . PathCorridorCost . Add ( CalcSegmentCostOnPoly ( StartPolyID , QueryFilter , RecastHandPlacedPathEnd , RecastStartPos ) ) ;
}
else
{
PostProcessPath ( FindPathStatus , Path , NavQuery , QueryFilter ,
2015-03-11 09:29:07 -04:00
StartPolyID , EndPolyID , Recast2UnrVector ( & RecastStartPos . X ) , Recast2UnrVector ( & RecastEndPos . X ) , RecastStartPos , RecastEndPos ,
2014-08-21 20:30:51 -04:00
PathResult ) ;
2014-03-14 14:13:41 -04:00
}
if ( dtStatusDetail ( FindPathStatus , DT_PARTIAL_RESULT ) )
{
Path . SetIsPartial ( true ) ;
// this means path finding algorithm reached the limit of InQueryFilter.GetMaxSearchNodes()
// nodes in A* node pool. This can mean resulting path is way off.
Path . SetSearchReachedLimit ( dtStatusDetail ( FindPathStatus , DT_OUT_OF_NODES ) ) ;
}
2015-04-24 14:07:47 -04:00
# if ENABLE_VISUAL_LOG
if ( dtStatusDetail ( FindPathStatus , DT_INVALID_CYCLE_PATH ) )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Error , TEXT ( " FPImplRecastNavMesh::FindPath resulted in a cyclic path! " ) ) ;
2016-01-15 13:01:30 -05:00
FVisualLogEntry * Entry = FVisualLogger : : Get ( ) . GetLastEntryForObject ( NavMeshOwner ) ;
if ( Entry )
{
Path . DescribeSelfToVisLog ( Entry ) ;
}
2015-04-24 14:07:47 -04:00
}
# endif // ENABLE_VISUAL_LOG
2014-03-14 14:13:41 -04:00
Path . MarkReady ( ) ;
return DTStatusToNavQueryResult ( FindPathStatus ) ;
}
2014-07-01 11:28:39 -04:00
ENavigationQueryResult : : Type FPImplRecastNavMesh : : TestPath ( const FVector & StartLoc , const FVector & EndLoc , const FNavigationQueryFilter & InQueryFilter , const UObject * Owner , int32 * NumVisitedNodes ) const
2014-03-14 14:13:41 -04:00
{
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( InQueryFilter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
if ( QueryFilter = = NULL )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Warning , TEXT ( " FPImplRecastNavMesh::FindPath failing due to QueryFilter == NULL " ) ) ;
return ENavigationQueryResult : : Error ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , InQueryFilter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
FVector RecastStartPos , RecastEndPos ;
2014-03-14 14:13:41 -04:00
NavNodeRef StartPolyID , EndPolyID ;
const bool bCanSearch = InitPathfinding ( StartLoc , EndLoc , NavQuery , QueryFilter , RecastStartPos , StartPolyID , RecastEndPos , EndPolyID ) ;
if ( ! bCanSearch )
{
return ENavigationQueryResult : : Error ;
}
// get path corridor
2014-08-21 20:30:51 -04:00
dtQueryResult PathResult ;
2021-10-25 20:05:28 -04:00
const FVector : : FReal CostLimit = TNumericLimits < FVector : : FReal > : : Max ( ) ;
2014-03-14 14:13:41 -04:00
const dtStatus FindPathStatus = NavQuery . findPath ( StartPolyID , EndPolyID ,
2019-11-06 10:31:29 -05:00
& RecastStartPos . X , & RecastEndPos . X , CostLimit , QueryFilter , PathResult , 0 ) ;
2014-03-14 14:13:41 -04:00
2014-07-01 11:28:39 -04:00
if ( NumVisitedNodes )
{
* NumVisitedNodes = NavQuery . getQueryNodes ( ) ;
}
2014-03-14 14:13:41 -04:00
return DTStatusToNavQueryResult ( FindPathStatus ) ;
}
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2014-07-01 11:28:39 -04:00
ENavigationQueryResult : : Type FPImplRecastNavMesh : : TestClusterPath ( const FVector & StartLoc , const FVector & EndLoc , int32 * NumVisitedNodes ) const
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
FVector RecastStartPos , RecastEndPos ;
2014-03-14 14:13:41 -04:00
NavNodeRef StartPolyID , EndPolyID ;
const dtQueryFilter * ClusterFilter = ( ( const FRecastQueryFilter * ) NavMeshOwner - > GetDefaultQueryFilterImpl ( ) ) - > GetAsDetourQueryFilter ( ) ;
2014-07-01 11:28:39 -04:00
INITIALIZE_NAVQUERY_SIMPLE ( ClusterQuery , NavMeshOwner - > DefaultMaxHierarchicalSearchNodes ) ;
2014-03-14 14:13:41 -04:00
const bool bCanSearch = InitPathfinding ( StartLoc , EndLoc , ClusterQuery , ClusterFilter , RecastStartPos , StartPolyID , RecastEndPos , EndPolyID ) ;
if ( ! bCanSearch )
{
return ENavigationQueryResult : : Error ;
}
const dtStatus status = ClusterQuery . testClusterPath ( StartPolyID , EndPolyID ) ;
2014-07-01 11:28:39 -04:00
if ( NumVisitedNodes )
2014-03-14 14:13:41 -04:00
{
2014-07-01 11:28:39 -04:00
* NumVisitedNodes = ClusterQuery . getQueryNodes ( ) ;
2014-03-14 14:13:41 -04:00
}
return DTStatusToNavQueryResult ( status ) ;
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
bool FPImplRecastNavMesh : : InitPathfinding ( const FVector & UnrealStart , const FVector & UnrealEnd ,
const dtNavMeshQuery & Query , const dtQueryFilter * Filter ,
2021-10-25 20:05:28 -04:00
FVector & RecastStart , dtPolyRef & StartPoly ,
FVector & RecastEnd , dtPolyRef & EndPoly ) const
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
const FVector NavExtent = NavMeshOwner - > GetModifiedQueryExtent ( NavMeshOwner - > GetDefaultQueryExtent ( ) ) ;
const FVector : : FReal Extent [ 3 ] = { NavExtent . X , NavExtent . Z , NavExtent . Y } ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
const FVector RecastStartToProject = Unreal2RecastPoint ( UnrealStart ) ;
const FVector RecastEndToProject = Unreal2RecastPoint ( UnrealEnd ) ;
2014-03-14 14:13:41 -04:00
StartPoly = INVALID_NAVNODEREF ;
2015-03-11 09:29:07 -04:00
Query . findNearestPoly ( & RecastStartToProject . X , Extent , Filter , & StartPoly , & RecastStart . X ) ;
2014-03-14 14:13:41 -04:00
if ( StartPoly = = INVALID_NAVNODEREF )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Warning , TEXT ( " FPImplRecastNavMesh::InitPathfinding start point not on navmesh " ) ) ;
2014-05-29 17:06:50 -04:00
UE_VLOG_SEGMENT ( NavMeshOwner , LogNavigation , Warning , UnrealStart , UnrealEnd , FColor : : Red , TEXT ( " Failed path " ) ) ;
UE_VLOG_LOCATION ( NavMeshOwner , LogNavigation , Warning , UnrealStart , 15 , FColor : : Red , TEXT ( " Start failed " ) ) ;
UE_VLOG_BOX ( NavMeshOwner , LogNavigation , Warning , FBox ( UnrealStart - NavExtent , UnrealStart + NavExtent ) , FColor : : Red , TEXT_EMPTY ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
EndPoly = INVALID_NAVNODEREF ;
2015-03-11 09:29:07 -04:00
Query . findNearestPoly ( & RecastEndToProject . X , Extent , Filter , & EndPoly , & RecastEnd . X ) ;
2014-03-14 14:13:41 -04:00
if ( EndPoly = = INVALID_NAVNODEREF )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Warning , TEXT ( " FPImplRecastNavMesh::InitPathfinding end point not on navmesh " ) ) ;
2014-05-29 17:06:50 -04:00
UE_VLOG_SEGMENT ( NavMeshOwner , LogNavigation , Warning , UnrealEnd , UnrealEnd , FColor : : Red , TEXT ( " Failed path " ) ) ;
UE_VLOG_LOCATION ( NavMeshOwner , LogNavigation , Warning , UnrealEnd , 15 , FColor : : Red , TEXT ( " End failed " ) ) ;
UE_VLOG_BOX ( NavMeshOwner , LogNavigation , Warning , FBox ( UnrealEnd - NavExtent , UnrealEnd + NavExtent ) , FColor : : Red , TEXT_EMPTY ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
return true ;
}
2021-10-25 20:05:28 -04:00
float FPImplRecastNavMesh : : CalcSegmentCostOnPoly ( NavNodeRef PolyID , const dtQueryFilter * Filter , const FVector & StartLoc , const FVector & EndLoc ) const
2014-03-14 14:13:41 -04:00
{
uint8 AreaID = RECAST_DEFAULT_AREA ;
DetourNavMesh - > getPolyArea ( PolyID , & AreaID ) ;
2021-10-25 20:05:28 -04:00
const FVector : : FReal AreaTravelCost = Filter - > getAreaCost ( AreaID ) ;
return UE_REAL_TO_FLOAT_CLAMPED_MAX ( AreaTravelCost * ( EndLoc - StartLoc ) . Size ( ) ) ;
2014-03-14 14:13:41 -04:00
}
void FPImplRecastNavMesh : : PostProcessPath ( dtStatus FindPathStatus , FNavMeshPath & Path ,
const dtNavMeshQuery & NavQuery , const dtQueryFilter * Filter ,
NavNodeRef StartPolyID , NavNodeRef EndPolyID ,
2021-10-25 20:05:28 -04:00
FVector StartLoc , FVector EndLoc ,
FVector RecastStartPos , FVector RecastEndPos ,
2014-08-21 20:30:51 -04:00
dtQueryResult & PathResult ) const
2014-03-14 14:13:41 -04:00
{
2018-11-14 19:05:13 -05:00
check ( Filter ) ;
2014-03-14 14:13:41 -04:00
// note that for recast partial path is successful, while we treat it as failed, just marking it as partial
if ( dtStatusSucceed ( FindPathStatus ) )
{
2017-02-08 17:53:41 -05:00
// check if navlink poly at end of path is allowed
int32 PathSize = PathResult . size ( ) ;
if ( ( PathSize > 1 ) & & NavMeshOwner & & ! NavMeshOwner - > bAllowNavLinkAsPathEnd )
{
uint16 PolyFlags = 0 ;
DetourNavMesh - > getPolyFlags ( PathResult . getRef ( PathSize - 1 ) , & PolyFlags ) ;
if ( PolyFlags & ARecastNavMesh : : GetNavLinkFlag ( ) )
{
PathSize - - ;
}
}
Path . PathCorridorCost . AddUninitialized ( PathSize ) ;
if ( PathSize = = 1 )
2014-03-14 14:13:41 -04:00
{
// failsafe cost for single poly corridor
Path . PathCorridorCost [ 0 ] = CalcSegmentCostOnPoly ( StartPolyID , Filter , EndLoc , StartLoc ) ;
}
else
{
2017-02-08 17:53:41 -05:00
for ( int32 i = 0 ; i < PathSize ; i + + )
2014-03-14 14:13:41 -04:00
{
2014-08-21 20:30:51 -04:00
Path . PathCorridorCost [ i ] = PathResult . getCost ( i ) ;
2014-03-14 14:13:41 -04:00
}
}
// copy over corridor poly data
2017-02-08 17:53:41 -05:00
Path . PathCorridor . AddUninitialized ( PathSize ) ;
2014-09-29 04:23:44 -04:00
NavNodeRef * DestCorridorPoly = Path . PathCorridor . GetData ( ) ;
2017-02-08 17:53:41 -05:00
for ( int i = 0 ; i < PathSize ; + + i , + + DestCorridorPoly )
2014-03-14 14:13:41 -04:00
{
2014-08-21 20:30:51 -04:00
* DestCorridorPoly = PathResult . getRef ( i ) ;
2014-03-14 14:13:41 -04:00
}
2018-11-14 19:05:13 -05:00
Path . OnPathCorridorUpdated ( ) ;
// if we're backtracking this is the time to reverse the path.
if ( Filter - > getIsBacktracking ( ) )
{
// for a proper string-pulling of a backtracking path we need to
// reverse the data right now.
Path . Invert ( ) ;
Swap ( StartPolyID , EndPolyID ) ;
Swap ( StartLoc , EndLoc ) ;
Swap ( RecastStartPos , RecastEndPos ) ;
}
2014-03-14 14:13:41 -04:00
# if STATS
if ( dtStatusDetail ( FindPathStatus , DT_OUT_OF_NODES ) )
{
INC_DWORD_STAT ( STAT_Navigation_OutOfNodesPath ) ;
}
if ( dtStatusDetail ( FindPathStatus , DT_PARTIAL_RESULT ) )
{
INC_DWORD_STAT ( STAT_Navigation_PartialPath ) ;
}
# endif
if ( Path . WantsStringPulling ( ) )
{
2014-09-12 05:08:35 -04:00
FVector UseEndLoc = EndLoc ;
2014-03-14 14:13:41 -04:00
// if path is partial (path corridor doesn't contain EndPolyID), find new RecastEndPos on last poly in corridor
if ( dtStatusDetail ( FindPathStatus , DT_PARTIAL_RESULT ) )
{
2014-08-21 20:30:51 -04:00
NavNodeRef LastPolyID = Path . PathCorridor . Last ( ) ;
2021-10-25 20:05:28 -04:00
FVector : : FReal NewEndPoint [ 3 ] ;
2014-03-14 14:13:41 -04:00
const dtStatus NewEndPointStatus = NavQuery . closestPointOnPoly ( LastPolyID , & RecastEndPos . X , NewEndPoint ) ;
if ( dtStatusSucceed ( NewEndPointStatus ) )
{
2014-09-12 05:08:35 -04:00
UseEndLoc = Recast2UnrealPoint ( NewEndPoint ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-09-12 05:08:35 -04:00
Path . PerformStringPulling ( StartLoc , UseEndLoc ) ;
2014-03-14 14:13:41 -04:00
}
else
{
// make sure at least beginning and end of path are added
2014-07-14 19:41:38 -04:00
new ( Path . GetPathPoints ( ) ) FNavPathPoint ( StartLoc , StartPolyID ) ;
new ( Path . GetPathPoints ( ) ) FNavPathPoint ( EndLoc , EndPolyID ) ;
2014-07-07 17:19:49 -04:00
// collect all custom links Ids
for ( int32 Idx = 0 ; Idx < Path . PathCorridor . Num ( ) ; Idx + + )
{
const dtOffMeshConnection * OffMeshCon = DetourNavMesh - > getOffMeshConnectionByRef ( Path . PathCorridor [ Idx ] ) ;
if ( OffMeshCon )
{
Path . CustomLinkIds . Add ( OffMeshCon - > userId ) ;
}
}
2014-03-14 14:13:41 -04:00
}
if ( Path . WantsPathCorridor ( ) )
{
TArray < FNavigationPortalEdge > PathCorridorEdges ;
GetEdgesForPathCorridorImpl ( & Path . PathCorridor , & PathCorridorEdges , NavQuery ) ;
Path . SetPathCorridorEdges ( PathCorridorEdges ) ;
}
}
}
2014-09-12 05:08:35 -04:00
bool FPImplRecastNavMesh : : FindStraightPath ( const FVector & StartLoc , const FVector & EndLoc , const TArray < NavNodeRef > & PathCorridor , TArray < FNavPathPoint > & PathPoints , TArray < uint32 > * CustomLinks ) const
{
INITIALIZE_NAVQUERY_SIMPLE ( NavQuery , RECAST_MAX_SEARCH_NODES ) ;
2021-10-25 20:05:28 -04:00
const FVector RecastStartPos = Unreal2RecastPoint ( StartLoc ) ;
const FVector RecastEndPos = Unreal2RecastPoint ( EndLoc ) ;
2014-09-12 05:08:35 -04:00
bool bResult = false ;
dtQueryResult StringPullResult ;
const dtStatus StringPullStatus = NavQuery . findStraightPath ( & RecastStartPos . X , & RecastEndPos . X ,
2014-09-29 04:23:44 -04:00
PathCorridor . GetData ( ) , PathCorridor . Num ( ) , StringPullResult , DT_STRAIGHTPATH_AREA_CROSSINGS ) ;
2014-09-12 05:08:35 -04:00
PathPoints . Reset ( ) ;
if ( dtStatusSucceed ( StringPullStatus ) )
{
PathPoints . AddZeroed ( StringPullResult . size ( ) ) ;
// convert to desired format
2014-09-29 04:23:44 -04:00
FNavPathPoint * CurVert = PathPoints . GetData ( ) ;
2014-09-12 05:08:35 -04:00
for ( int32 VertIdx = 0 ; VertIdx < StringPullResult . size ( ) ; + + VertIdx )
{
2021-10-25 20:05:28 -04:00
const FVector : : FReal * CurRecastVert = StringPullResult . getPos ( VertIdx ) ;
2014-09-12 05:08:35 -04:00
CurVert - > Location = Recast2UnrVector ( CurRecastVert ) ;
CurVert - > NodeRef = StringPullResult . getRef ( VertIdx ) ;
FNavMeshNodeFlags CurNodeFlags ( 0 ) ;
CurNodeFlags . PathFlags = StringPullResult . getFlag ( VertIdx ) ;
uint8 AreaID = RECAST_DEFAULT_AREA ;
DetourNavMesh - > getPolyArea ( CurVert - > NodeRef , & AreaID ) ;
CurNodeFlags . Area = AreaID ;
const UClass * AreaClass = NavMeshOwner - > GetAreaClass ( AreaID ) ;
const UNavArea * DefArea = AreaClass ? ( ( UClass * ) AreaClass ) - > GetDefaultObject < UNavArea > ( ) : NULL ;
CurNodeFlags . AreaFlags = DefArea ? DefArea - > GetAreaFlags ( ) : 0 ;
CurVert - > Flags = CurNodeFlags . Pack ( ) ;
// include smart link data
// if there will be more "edge types" we change this implementation to something more generic
if ( CustomLinks & & ( CurNodeFlags . PathFlags & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) )
{
const dtOffMeshConnection * OffMeshCon = DetourNavMesh - > getOffMeshConnectionByRef ( CurVert - > NodeRef ) ;
if ( OffMeshCon )
{
CurVert - > CustomLinkId = OffMeshCon - > userId ;
CustomLinks - > Add ( OffMeshCon - > userId ) ;
}
}
CurVert + + ;
}
// findStraightPath returns 0 for polyId of last point for some reason, even though it knows the poly id. We will fill that in correctly with the last poly id of the corridor.
// @TODO shouldn't it be the same as EndPolyID? (nope, it could be partial path)
PathPoints . Last ( ) . NodeRef = PathCorridor . Last ( ) ;
bResult = true ;
}
return bResult ;
}
2015-03-09 05:40:56 -04:00
static bool IsDebugNodeModified ( const FRecastDebugPathfindingNode & NodeData , const FRecastDebugPathfindingData & PreviousStep )
2014-03-14 14:13:41 -04:00
{
const FRecastDebugPathfindingNode * PrevNodeData = PreviousStep . Nodes . Find ( NodeData ) ;
if ( PrevNodeData )
{
const bool bModified = PrevNodeData - > bOpenSet ! = NodeData . bOpenSet | |
PrevNodeData - > TotalCost ! = NodeData . TotalCost | |
PrevNodeData - > Cost ! = NodeData . Cost | |
PrevNodeData - > ParentRef ! = NodeData . ParentRef | |
! PrevNodeData - > NodePos . Equals ( NodeData . NodePos , SMALL_NUMBER ) ;
return bModified ;
}
return true ;
}
2017-12-05 21:57:41 -05:00
static void StorePathfindingDebugData ( const dtNavMeshQuery & NavQuery , const dtNavMesh * NavMesh , FRecastDebugPathfindingData & Data )
2015-03-09 05:40:56 -04:00
{
2017-12-05 21:57:41 -05:00
const dtNodePool * NodePool = NavQuery . getNodePool ( ) ;
check ( NodePool ) ;
const int32 NodeCount = NodePool - > getNodeCount ( ) ;
if ( NodeCount < = 0 )
2015-03-09 05:40:56 -04:00
{
return ;
}
2017-12-05 21:57:41 -05:00
// cache path lengths for all nodes in pool, indexed by poolIdx (idx + 1)
2021-10-25 20:05:28 -04:00
TArray < FVector : : FReal > NodePathLength ;
2017-12-05 21:57:41 -05:00
if ( Data . Flags & ERecastDebugPathfindingFlags : : PathLength )
2015-03-09 05:40:56 -04:00
{
2017-12-05 21:57:41 -05:00
NodePathLength . AddZeroed ( NodeCount + 1 ) ;
2015-03-09 05:40:56 -04:00
}
2017-12-05 21:57:41 -05:00
Data . Nodes . Reserve ( NodeCount ) ;
for ( int32 Idx = 0 ; Idx < NodeCount ; Idx + + )
2014-03-14 14:13:41 -04:00
{
2017-12-05 21:57:41 -05:00
const int32 NodePoolIdx = Idx + 1 ;
const dtNode * Node = NodePool - > getNodeAtIdx ( NodePoolIdx ) ;
check ( Node ) ;
const dtNode * ParentNode = Node - > pidx ? NodePool - > getNodeAtIdx ( Node - > pidx ) : nullptr ;
2015-03-09 05:40:56 -04:00
2014-03-14 14:13:41 -04:00
FRecastDebugPathfindingNode NodeInfo ;
NodeInfo . PolyRef = Node - > id ;
2017-12-05 21:57:41 -05:00
NodeInfo . ParentRef = ParentNode ? ParentNode - > id : 0 ;
2021-10-25 20:05:28 -04:00
NodeInfo . Cost = UE_REAL_TO_FLOAT_CLAMPED_MAX ( Node - > cost ) ; // LWC_TODO_AI: Make FRecastDebugPathfindingNode::Cost and FRecastDebugPathfindingNode::Total FVector::FReal. Not until after 5.0!
NodeInfo . TotalCost = UE_REAL_TO_FLOAT_CLAMPED_MAX ( Node - > total ) ;
2017-12-05 21:57:41 -05:00
NodeInfo . Length = 0.0f ;
NodeInfo . bOpenSet = ( Node - > flags & DT_NODE_OPEN ) ! = 0 ;
2014-03-14 14:13:41 -04:00
NodeInfo . bModified = true ;
NodeInfo . NodePos = Recast2UnrealPoint ( & Node - > pos [ 0 ] ) ;
const dtPoly * NavPoly = 0 ;
const dtMeshTile * NavTile = 0 ;
NavMesh - > getTileAndPolyByRef ( Node - > id , & NavTile , & NavPoly ) ;
NodeInfo . bOffMeshLink = NavPoly ? ( NavPoly - > getType ( ) ! = DT_POLYTYPE_GROUND ) : false ;
2015-03-09 05:40:56 -04:00
if ( Data . Flags & ERecastDebugPathfindingFlags : : Vertices )
2014-03-14 14:13:41 -04:00
{
2016-06-01 12:08:56 -04:00
check ( NavPoly ) ;
2017-12-05 21:57:41 -05:00
NodeInfo . NumVerts = NavPoly - > vertCount ;
for ( int32 VertIdx = 0 ; VertIdx < NavPoly - > vertCount ; VertIdx + + )
2015-03-09 05:40:56 -04:00
{
2022-02-02 07:59:31 -05:00
NodeInfo . Verts . Add ( ( FVector3f ) Recast2UnrealPoint ( & NavTile - > verts [ NavPoly - > verts [ VertIdx ] * 3 ] ) ) ;
2015-03-09 05:40:56 -04:00
}
2014-03-14 14:13:41 -04:00
}
2017-12-05 21:57:41 -05:00
if ( ( Data . Flags & ERecastDebugPathfindingFlags : : PathLength ) & & ParentNode )
2014-03-14 14:13:41 -04:00
{
2017-12-05 21:57:41 -05:00
const FVector ParentPos = Recast2UnrealPoint ( & ParentNode - > pos [ 0 ] ) ;
2021-10-25 20:05:28 -04:00
const FVector : : FReal NodeLinkLen = FVector : : Dist ( NodeInfo . NodePos , ParentPos ) ;
2017-12-05 21:57:41 -05:00
// no point in validating, it would already crash on reading ParentNode (no validation in NodePool.getNodeAtIdx)
2021-10-25 20:05:28 -04:00
const FVector : : FReal ParentPathLength = NodePathLength [ Node - > pidx ] ;
2017-12-05 21:57:41 -05:00
2021-10-25 20:05:28 -04:00
const FVector : : FReal LinkAndParentLength = NodeLinkLen + ParentPathLength ;
NodePathLength [ NodePoolIdx ] = LinkAndParentLength ;
// LWC_TODO_AI: Make Length FVector::FReal. Not until after 5.0!
NodeInfo . Length = UE_REAL_TO_FLOAT_CLAMPED_MAX ( LinkAndParentLength ) ;
2014-03-14 14:13:41 -04:00
}
2017-12-05 21:57:41 -05:00
Data . Nodes . Add ( NodeInfo ) ;
2014-03-14 14:13:41 -04:00
}
2017-12-05 21:57:41 -05:00
if ( Data . Flags & ERecastDebugPathfindingFlags : : BestNode )
2015-03-09 05:40:56 -04:00
{
2017-12-05 21:57:41 -05:00
dtNode * BestNode = nullptr ;
2021-10-25 20:05:28 -04:00
FVector : : FReal BestNodeCost = 0.0f ;
2017-12-05 21:57:41 -05:00
NavQuery . getCurrentBestResult ( BestNode , BestNodeCost ) ;
if ( BestNode )
2015-03-09 05:40:56 -04:00
{
2017-12-05 21:57:41 -05:00
const FRecastDebugPathfindingNode BestNodeKey ( BestNode - > id ) ;
Data . BestNode = Data . Nodes . FindId ( BestNodeKey ) ;
2015-03-09 05:40:56 -04:00
}
}
}
static void StorePathfindingDebugStep ( const dtNavMeshQuery & NavQuery , const dtNavMesh * NavMesh , TArray < FRecastDebugPathfindingData > & Steps )
{
const int StepIdx = Steps . AddZeroed ( 1 ) ;
FRecastDebugPathfindingData & StepInfo = Steps [ StepIdx ] ;
StepInfo . Flags = ERecastDebugPathfindingFlags : : BestNode | ERecastDebugPathfindingFlags : : Vertices ;
StorePathfindingDebugData ( NavQuery , NavMesh , StepInfo ) ;
2014-03-14 14:13:41 -04:00
if ( Steps . Num ( ) > 1 )
{
2015-03-09 05:40:56 -04:00
FRecastDebugPathfindingData & PrevStepInfo = Steps [ StepIdx - 1 ] ;
2014-03-14 14:13:41 -04:00
for ( TSet < FRecastDebugPathfindingNode > : : TIterator It ( StepInfo . Nodes ) ; It ; + + It )
{
FRecastDebugPathfindingNode & NodeData = * It ;
NodeData . bModified = IsDebugNodeModified ( NodeData , PrevStepInfo ) ;
}
}
}
2019-11-06 10:31:29 -05:00
int32 FPImplRecastNavMesh : : DebugPathfinding ( const FVector & StartLoc , const FVector & EndLoc , const float CostLimit , const FNavigationQueryFilter & Filter , const UObject * Owner , TArray < FRecastDebugPathfindingData > & Steps )
2014-03-14 14:13:41 -04:00
{
int32 NumSteps = 0 ;
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
if ( QueryFilter = = NULL )
{
UE_VLOG ( NavMeshOwner , LogNavigation , Warning , TEXT ( " FPImplRecastNavMesh::DebugPathfinding failing due to QueryFilter == NULL " ) ) ;
return NumSteps ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , Filter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
FVector RecastStartPos , RecastEndPos ;
2014-03-14 14:13:41 -04:00
NavNodeRef StartPolyID , EndPolyID ;
const bool bCanSearch = InitPathfinding ( StartLoc , EndLoc , NavQuery , QueryFilter , RecastStartPos , StartPolyID , RecastEndPos , EndPolyID ) ;
if ( ! bCanSearch )
{
return NumSteps ;
}
2019-11-06 10:31:29 -05:00
dtStatus status = NavQuery . initSlicedFindPath ( StartPolyID , EndPolyID , & RecastStartPos . X , & RecastEndPos . X , CostLimit , QueryFilter ) ;
2014-03-14 14:13:41 -04:00
while ( dtStatusInProgress ( status ) )
{
StorePathfindingDebugStep ( NavQuery , DetourNavMesh , Steps ) ;
NumSteps + + ;
status = NavQuery . updateSlicedFindPath ( 1 , 0 ) ;
}
static const int32 MAX_TEMP_POLYS = 16 ;
NavNodeRef TempPolys [ MAX_TEMP_POLYS ] ;
int32 NumTempPolys ;
NavQuery . finalizeSlicedFindPath ( TempPolys , & NumTempPolys , MAX_TEMP_POLYS ) ;
return NumSteps ;
}
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
NavNodeRef FPImplRecastNavMesh : : GetClusterRefFromPolyRef ( const NavNodeRef PolyRef ) const
{
if ( DetourNavMesh )
{
const dtMeshTile * Tile = DetourNavMesh - > getTileByRef ( PolyRef ) ;
uint32 PolyIdx = DetourNavMesh - > decodePolyIdPoly ( PolyRef ) ;
if ( Tile & & Tile - > polyClusters & & PolyIdx < ( uint32 ) Tile - > header - > offMeshBase )
{
return DetourNavMesh - > getClusterRefBase ( Tile ) | Tile - > polyClusters [ PolyIdx ] ;
}
}
return 0 ;
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
2014-04-23 19:29:53 -04:00
FNavLocation FPImplRecastNavMesh : : GetRandomPoint ( const FNavigationQueryFilter & Filter , const UObject * Owner ) const
2014-03-14 14:13:41 -04:00
{
FNavLocation OutLocation ;
if ( DetourNavMesh = = NULL )
{
return OutLocation ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , Filter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
// inits to "pass all"
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
ensure ( QueryFilter ) ;
if ( QueryFilter )
{
dtPolyRef Poly ;
2021-10-25 20:05:28 -04:00
FVector : : FReal RandPt [ 3 ] ;
2014-03-14 14:13:41 -04:00
dtStatus Status = NavQuery . findRandomPoint ( QueryFilter , FMath : : FRand , & Poly , RandPt ) ;
if ( dtStatusSucceed ( Status ) )
{
// arrange output
OutLocation . Location = Recast2UnrVector ( RandPt ) ;
OutLocation . NodeRef = Poly ;
}
}
return OutLocation ;
}
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
bool FPImplRecastNavMesh : : GetRandomPointInCluster ( NavNodeRef ClusterRef , FNavLocation & OutLocation ) const
{
if ( DetourNavMesh = = NULL | | ClusterRef = = 0 )
{
return false ;
}
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY_SIMPLE ( NavQuery , RECAST_MAX_SEARCH_NODES ) ;
2014-03-14 14:13:41 -04:00
dtPolyRef Poly ;
2021-10-25 20:05:28 -04:00
FVector : : FReal RandPt [ 3 ] ;
2014-03-14 14:13:41 -04:00
dtStatus Status = NavQuery . findRandomPointInCluster ( ClusterRef , FMath : : FRand , & Poly , RandPt ) ;
if ( dtStatusSucceed ( Status ) )
{
OutLocation = FNavLocation ( Recast2UnrVector ( RandPt ) , Poly ) ;
return true ;
}
return false ;
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
2020-04-30 09:48:28 -04:00
bool FPImplRecastNavMesh : : FindMoveAlongSurface ( const FNavLocation & StartLocation , const FVector & TargetPosition , FNavLocation & OutLocation , const FNavigationQueryFilter & Filter , const UObject * Owner ) const
{
// sanity check
if ( DetourNavMesh = = NULL )
{
return false ;
}
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2021-05-25 02:43:26 -04:00
INITIALIZE_NAVQUERY ( NavQuery , Filter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2020-04-30 09:48:28 -04:00
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
ensure ( QueryFilter ) ;
if ( ! QueryFilter )
{
return false ;
}
2021-10-25 20:05:28 -04:00
FVector RcStartPos = Unreal2RecastPoint ( StartLocation . Location ) ;
FVector RcEndPos = Unreal2RecastPoint ( TargetPosition ) ;
2020-04-30 09:48:28 -04:00
2021-10-25 20:05:28 -04:00
FVector : : FReal Result [ 3 ] ;
2020-04-30 09:48:28 -04:00
static const int MAX_VISITED = 16 ;
dtPolyRef Visited [ MAX_VISITED ] ;
int VisitedCount = 0 ;
dtStatus status = NavQuery . moveAlongSurface ( StartLocation . NodeRef , & RcStartPos . X , & RcEndPos . X , QueryFilter , Result , Visited , & VisitedCount , MAX_VISITED ) ;
if ( dtStatusFailed ( status ) )
{
return false ;
}
dtPolyRef ResultPoly = Visited [ VisitedCount - 1 ] ;
// Adjust the position to stay on top of the navmesh.
2021-10-25 20:05:28 -04:00
FVector : : FReal h = RcStartPos . Y ;
2020-04-30 09:48:28 -04:00
NavQuery . getPolyHeight ( ResultPoly , Result , & h ) ;
Result [ 1 ] = h ;
const FVector UnrealResult = Recast2UnrVector ( Result ) ;
OutLocation = FNavLocation ( UnrealResult , ResultPoly ) ;
return true ;
}
2014-04-23 19:29:53 -04:00
bool FPImplRecastNavMesh : : ProjectPointToNavMesh ( const FVector & Point , FNavLocation & Result , const FVector & Extent , const FNavigationQueryFilter & Filter , const UObject * Owner ) const
2014-03-14 14:13:41 -04:00
{
// sanity check
if ( DetourNavMesh = = NULL )
{
return false ;
}
bool bSuccess = false ;
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2018-09-25 10:11:35 -04:00
// using 0 as NumNodes since findNearestPoly2D, being the only dtNavMeshQuery
// function we're using, is not utilizing m_nodePool
INITIALIZE_NAVQUERY ( NavQuery , /*NumNodes=*/ 0 , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
ensure ( QueryFilter ) ;
if ( QueryFilter )
{
2021-10-25 20:05:28 -04:00
FVector : : FReal ClosestPoint [ 3 ] ;
2014-03-14 14:13:41 -04:00
2015-04-17 03:22:18 -04:00
const FVector ModifiedExtent = NavMeshOwner - > GetModifiedQueryExtent ( Extent ) ;
2021-10-25 20:05:28 -04:00
FVector RcExtent = Unreal2RecastPoint ( ModifiedExtent ) . GetAbs ( ) ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
FVector RcPoint = Unreal2RecastPoint ( Point ) ;
2014-03-14 14:13:41 -04:00
dtPolyRef PolyRef ;
2016-04-29 15:14:04 -04:00
NavQuery . findNearestPoly2D ( & RcPoint . X , & RcExtent . X , QueryFilter , & PolyRef , ClosestPoint ) ;
2014-03-14 14:13:41 -04:00
if ( PolyRef > 0 )
{
2014-07-01 11:28:39 -04:00
// one last step required due to recast's BVTree imprecision
const FVector & UnrealClosestPoint = Recast2UnrVector ( ClosestPoint ) ;
2014-12-15 15:29:48 -05:00
const FVector ClosestPointDelta = UnrealClosestPoint - Point ;
2016-04-29 15:14:04 -04:00
if ( - ModifiedExtent . X < = ClosestPointDelta . X & & ClosestPointDelta . X < = ModifiedExtent . X
& & - ModifiedExtent . Y < = ClosestPointDelta . Y & & ClosestPointDelta . Y < = ModifiedExtent . Y
& & - ModifiedExtent . Z < = ClosestPointDelta . Z & & ClosestPointDelta . Z < = ModifiedExtent . Z )
2014-07-01 11:28:39 -04:00
{
bSuccess = true ;
Result = FNavLocation ( UnrealClosestPoint , PolyRef ) ;
}
2022-01-19 08:25:34 -05:00
else
{
const UObject * LogOwner = Owner ? Owner : NavMeshOwner ;
UE_VLOG ( LogOwner , LogNavigation , Error , TEXT ( " ProjectPointToNavMesh failed due to ClosestPoint being too far away from projected point. " ) ) ;
UE_VLOG_LOCATION ( LogOwner , LogNavigation , Error , Point , 30.f , FColor : : Blue , TEXT ( " Requested point " ) ) ;
UE_VLOG_LOCATION ( LogOwner , LogNavigation , Error , UnrealClosestPoint , 30.f , FColor : : Red , TEXT ( " Projection result " ) ) ;
UE_VLOG_SEGMENT ( LogOwner , LogNavigation , Error , Point , UnrealClosestPoint , FColor : : Red , TEXT ( " " ) ) ;
}
2014-03-14 14:13:41 -04:00
}
}
return ( bSuccess ) ;
}
bool FPImplRecastNavMesh : : ProjectPointMulti ( const FVector & Point , TArray < FNavLocation > & Result , const FVector & Extent ,
2021-10-25 20:05:28 -04:00
FVector : : FReal MinZ , FVector : : FReal MaxZ , const FNavigationQueryFilter & Filter , const UObject * Owner ) const
2014-03-14 14:13:41 -04:00
{
// sanity check
if ( DetourNavMesh = = NULL )
{
return false ;
}
bool bSuccess = false ;
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , Filter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
ensure ( QueryFilter ) ;
if ( QueryFilter )
{
2015-04-17 03:22:18 -04:00
const FVector ModifiedExtent = NavMeshOwner - > GetModifiedQueryExtent ( Extent ) ;
2014-03-14 14:13:41 -04:00
const FVector AdjustedPoint ( Point . X , Point . Y , ( MaxZ + MinZ ) * 0.5f ) ;
2015-04-17 03:22:18 -04:00
const FVector AdjustedExtent ( ModifiedExtent . X , ModifiedExtent . Y , ( MaxZ - MinZ ) * 0.5f ) ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
const FVector RcPoint = Unreal2RecastPoint ( AdjustedPoint ) ;
const FVector RcExtent = Unreal2RecastPoint ( AdjustedExtent ) . GetAbs ( ) ;
2014-03-14 14:13:41 -04:00
const int32 MaxHitPolys = 256 ;
dtPolyRef HitPolys [ MaxHitPolys ] ;
int32 NumHitPolys = 0 ;
dtStatus status = NavQuery . queryPolygons ( & RcPoint . X , & RcExtent . X , QueryFilter , HitPolys , & NumHitPolys , MaxHitPolys ) ;
if ( dtStatusSucceed ( status ) )
{
for ( int32 i = 0 ; i < NumHitPolys ; i + + )
{
2021-10-25 20:05:28 -04:00
FVector : : FReal ClosestPoint [ 3 ] ;
2014-03-14 14:13:41 -04:00
2014-08-06 16:12:41 -04:00
status = NavQuery . projectedPointOnPoly ( HitPolys [ i ] , & RcPoint . X , ClosestPoint ) ;
2014-03-14 14:13:41 -04:00
if ( dtStatusSucceed ( status ) )
{
2021-10-25 20:05:28 -04:00
FVector : : FReal ExactZ = 0.0f ;
2015-04-01 08:37:22 -04:00
status = NavQuery . getPolyHeight ( HitPolys [ i ] , ClosestPoint , & ExactZ ) ;
if ( dtStatusSucceed ( status ) )
{
FNavLocation HitLocation ( Recast2UnrealPoint ( ClosestPoint ) , HitPolys [ i ] ) ;
HitLocation . Location . Z = ExactZ ;
ensure ( ( HitLocation . Location - AdjustedPoint ) . SizeSquared2D ( ) < KINDA_SMALL_NUMBER ) ;
Result . Add ( HitLocation ) ;
bSuccess = true ;
}
2014-03-14 14:13:41 -04:00
}
}
}
}
return bSuccess ;
}
2014-04-23 19:29:53 -04:00
NavNodeRef FPImplRecastNavMesh : : FindNearestPoly ( FVector const & Loc , FVector const & Extent , const FNavigationQueryFilter & Filter , const UObject * Owner ) const
2014-03-14 14:13:41 -04:00
{
// sanity check
if ( DetourNavMesh = = NULL )
{
return INVALID_NAVNODEREF ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY ( NavQuery , Filter . GetMaxSearchNodes ( ) , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
// inits to "pass all"
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
ensure ( QueryFilter ) ;
if ( QueryFilter )
{
2021-10-25 20:05:28 -04:00
FVector : : FReal RecastLoc [ 3 ] ;
2014-03-14 14:13:41 -04:00
Unr2RecastVector ( Loc , RecastLoc ) ;
2021-10-25 20:05:28 -04:00
FVector : : FReal RecastExtent [ 3 ] ;
2015-04-17 03:22:18 -04:00
Unr2RecastSizeVector ( NavMeshOwner - > GetModifiedQueryExtent ( Extent ) , RecastExtent ) ;
2014-03-14 14:13:41 -04:00
NavNodeRef OutRef ;
dtStatus Status = NavQuery . findNearestPoly ( RecastLoc , RecastExtent , QueryFilter , & OutRef , NULL ) ;
if ( dtStatusSucceed ( Status ) )
{
return OutRef ;
}
}
return INVALID_NAVNODEREF ;
}
2021-10-25 20:05:28 -04:00
bool FPImplRecastNavMesh : : FindPolysAroundCircle ( const FVector & CenterPos , const NavNodeRef CenterNodeRef , const FVector : : FReal Radius , const FNavigationQueryFilter & Filter , const UObject * Owner , TArray < NavNodeRef > * OutPolys , TArray < NavNodeRef > * OutPolysParent , TArray < float > * OutPolysCost , int * OutPolysCount ) const
2020-10-29 13:38:15 -04:00
{
// sanity check
if ( DetourNavMesh = = NULL | | NavMeshOwner = = NULL | | CenterNodeRef = = INVALID_NAVNODEREF )
{
return false ;
}
2021-10-25 20:05:28 -04:00
// LWC_TODO_AI: Remove this when costs are treated as FReals throughout. Not until after 5.0!
TArray < FVector : : FReal > PolysCost ;
2020-10-29 13:38:15 -04:00
// limit max number of polys found by that function
// if you need more, please scan manually using ARecastNavMesh::GetPolyNeighbors for A*/Dijkstra loop
const int32 MaxSearchLimit = 4096 ;
const int32 MaxSearchNodes = Filter . GetMaxSearchNodes ( ) ;
ensureMsgf ( MaxSearchNodes > 0 & & MaxSearchNodes < = MaxSearchLimit , TEXT ( " MaxSearchNodes:%d is not within range: 0..%d " ) , MaxSearchNodes , MaxSearchLimit ) ;
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
INITIALIZE_NAVQUERY ( NavQuery , Filter . GetMaxSearchNodes ( ) , LinkFilter ) ;
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
if ( ensure ( QueryFilter ) )
{
if ( OutPolys )
{
OutPolys - > Reset ( ) ;
OutPolys - > AddUninitialized ( MaxSearchNodes ) ;
}
if ( OutPolysParent )
{
OutPolysParent - > Reset ( ) ;
OutPolysParent - > AddUninitialized ( MaxSearchNodes ) ;
}
if ( OutPolysCost )
{
2022-03-18 18:51:49 -04:00
PolysCost . Reset ( ) ;
PolysCost . AddUninitialized ( MaxSearchNodes ) ;
2020-10-29 13:38:15 -04:00
}
2021-10-25 20:05:28 -04:00
FVector : : FReal RecastLoc [ 3 ] ;
2020-10-29 13:38:15 -04:00
Unr2RecastVector ( CenterPos , RecastLoc ) ;
2021-10-25 20:05:28 -04:00
const dtStatus Status = NavQuery . findPolysAroundCircle ( CenterNodeRef , RecastLoc , Radius , QueryFilter , OutPolys ? OutPolys - > GetData ( ) : nullptr , OutPolysParent ? OutPolysParent - > GetData ( ) : nullptr , OutPolysCost ? PolysCost . GetData ( ) : nullptr , OutPolysCount , MaxSearchNodes ) ;
if ( OutPolysCost )
{
* OutPolysCost = LWC : : ConvertArrayTypeClampMax < float > ( PolysCost ) ;
}
2020-10-29 13:38:15 -04:00
if ( dtStatusSucceed ( Status ) )
{
return true ;
}
}
return false ;
}
2021-10-25 20:05:28 -04:00
bool FPImplRecastNavMesh : : GetPolysWithinPathingDistance ( FVector const & StartLoc , const float PathingDistance ,
2015-03-09 05:40:56 -04:00
const FNavigationQueryFilter & Filter , const UObject * Owner ,
2020-02-06 14:37:16 -05:00
TArray < NavNodeRef > & FoundPolys , FRecastDebugPathfindingData * OutDebugData ) const
2014-03-14 14:13:41 -04:00
{
ensure ( PathingDistance > 0.0f & & " PathingDistance <= 0 doesn't make sense " ) ;
2017-09-30 03:42:01 -04:00
// limit max number of polys found by that function
2020-10-29 13:38:15 -04:00
// if you need more, please scan manually using ARecastNavMesh::GetPolyNeighbors for A*/Dijkstra loop
2017-09-30 03:42:01 -04:00
const int32 MaxSearchLimit = 4096 ;
const int32 MaxSearchNodes = Filter . GetMaxSearchNodes ( ) ;
ensureMsgf ( MaxSearchNodes > 0 & & MaxSearchNodes < = MaxSearchLimit , TEXT ( " MaxSearchNodes:%d is not within range: 0..%d " ) , MaxSearchNodes , MaxSearchLimit ) ;
2014-03-14 14:13:41 -04:00
// sanity check
2017-09-30 03:42:01 -04:00
if ( DetourNavMesh = = nullptr | | MaxSearchNodes < = 0 | | MaxSearchNodes > MaxSearchLimit )
2014-03-14 14:13:41 -04:00
{
return false ;
}
2018-03-27 14:27:07 -04:00
FRecastSpeciaLinkFilter LinkFilter ( FNavigationSystem : : GetCurrent < UNavigationSystemV1 > ( NavMeshOwner - > GetWorld ( ) ) , Owner ) ;
2017-09-30 03:42:01 -04:00
INITIALIZE_NAVQUERY ( NavQuery , MaxSearchNodes , LinkFilter ) ;
2014-03-14 14:13:41 -04:00
const dtQueryFilter * QueryFilter = ( ( const FRecastQueryFilter * ) ( Filter . GetImplementation ( ) ) ) - > GetAsDetourQueryFilter ( ) ;
ensure ( QueryFilter ) ;
2017-09-30 03:42:01 -04:00
if ( QueryFilter = = nullptr )
2014-03-14 14:13:41 -04:00
{
return false ;
}
// @todo this should be configurable in some kind of FindPathQuery structure
2021-10-25 20:05:28 -04:00
const FVector NavExtent = NavMeshOwner - > GetModifiedQueryExtent ( NavMeshOwner - > GetDefaultQueryExtent ( ) ) ;
const FVector : : FReal Extent [ 3 ] = { NavExtent . X , NavExtent . Z , NavExtent . Y } ;
2014-03-14 14:13:41 -04:00
2021-10-25 20:05:28 -04:00
FVector : : FReal RecastStartPos [ 3 ] ;
2014-03-14 14:13:41 -04:00
Unr2RecastVector ( StartLoc , RecastStartPos ) ;
// @TODO add failure handling
NavNodeRef StartPolyID = INVALID_NAVNODEREF ;
NavQuery . findNearestPoly ( RecastStartPos , Extent , QueryFilter , & StartPolyID , NULL ) ;
2017-09-30 03:42:01 -04:00
FoundPolys . Reset ( MaxSearchNodes ) ;
FoundPolys . AddUninitialized ( MaxSearchNodes ) ;
int32 NumPolys = 0 ;
2014-03-14 14:13:41 -04:00
2017-09-30 03:42:01 -04:00
NavQuery . findPolysInPathDistance ( StartPolyID , RecastStartPos , PathingDistance , QueryFilter , FoundPolys . GetData ( ) , & NumPolys , MaxSearchNodes ) ;
2014-03-14 14:13:41 -04:00
FoundPolys . RemoveAt ( NumPolys , FoundPolys . Num ( ) - NumPolys ) ;
2020-02-06 14:37:16 -05:00
if ( OutDebugData )
2015-03-09 05:40:56 -04:00
{
2020-02-06 14:37:16 -05:00
StorePathfindingDebugData ( NavQuery , DetourNavMesh , * OutDebugData ) ;
2015-03-09 05:40:56 -04:00
}
2014-03-14 14:13:41 -04:00
return FoundPolys . Num ( ) > 0 ;
}
void FPImplRecastNavMesh : : UpdateNavigationLinkArea ( int32 UserId , uint8 AreaType , uint16 PolyFlags ) const
{
if ( DetourNavMesh )
{
DetourNavMesh - > updateOffMeshConnectionByUserId ( UserId , AreaType , PolyFlags ) ;
}
}
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_SEGMENT_LINKS
2014-03-14 14:13:41 -04:00
void FPImplRecastNavMesh : : UpdateSegmentLinkArea ( int32 UserId , uint8 AreaType , uint16 PolyFlags ) const
{
if ( DetourNavMesh )
{
DetourNavMesh - > updateOffMeshSegmentConnectionByUserId ( UserId , AreaType , PolyFlags ) ;
}
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_SEGMENT_LINKS
2014-03-14 14:13:41 -04:00
bool FPImplRecastNavMesh : : GetPolyCenter ( NavNodeRef PolyID , FVector & OutCenter ) const
{
if ( DetourNavMesh )
{
// get poly data from recast
dtPoly const * Poly ;
dtMeshTile const * Tile ;
dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( ( dtPolyRef ) PolyID , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
// average verts
2021-10-25 20:05:28 -04:00
FVector : : FReal Center [ 3 ] = { 0 , 0 , 0 } ;
2014-03-14 14:13:41 -04:00
for ( uint32 VertIdx = 0 ; VertIdx < Poly - > vertCount ; + + VertIdx )
{
2021-10-25 20:05:28 -04:00
const FVector : : FReal * V = & Tile - > verts [ Poly - > verts [ VertIdx ] * 3 ] ;
2014-03-14 14:13:41 -04:00
Center [ 0 ] + = V [ 0 ] ;
Center [ 1 ] + = V [ 1 ] ;
Center [ 2 ] + = V [ 2 ] ;
}
2021-10-25 20:05:28 -04:00
const FVector : : FReal InvCount = 1.0f / Poly - > vertCount ;
2014-03-14 14:13:41 -04:00
Center [ 0 ] * = InvCount ;
Center [ 1 ] * = InvCount ;
Center [ 2 ] * = InvCount ;
2021-10-13 12:36:30 -04:00
// convert output to UE coords
2014-03-14 14:13:41 -04:00
OutCenter = Recast2UnrVector ( Center ) ;
return true ;
}
}
return false ;
}
bool FPImplRecastNavMesh : : GetPolyVerts ( NavNodeRef PolyID , TArray < FVector > & OutVerts ) const
{
if ( DetourNavMesh )
{
// get poly data from recast
dtPoly const * Poly ;
dtMeshTile const * Tile ;
dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( ( dtPolyRef ) PolyID , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
// flush and pre-size output array
2016-12-08 16:58:18 -05:00
OutVerts . Reset ( Poly - > vertCount ) ;
2014-03-14 14:13:41 -04:00
2021-10-13 12:36:30 -04:00
// convert to UE coords and copy verts into output array
2014-03-14 14:13:41 -04:00
for ( uint32 VertIdx = 0 ; VertIdx < Poly - > vertCount ; + + VertIdx )
{
2021-10-25 20:05:28 -04:00
const FVector : : FReal * V = & Tile - > verts [ Poly - > verts [ VertIdx ] * 3 ] ;
2014-03-14 14:13:41 -04:00
OutVerts . Add ( Recast2UnrVector ( V ) ) ;
}
return true ;
}
}
return false ;
}
2022-03-23 01:26:18 -04:00
bool FPImplRecastNavMesh : : GetRandomPointInPoly ( NavNodeRef PolyID , FVector & OutPoint ) const
{
if ( DetourNavMesh )
{
INITIALIZE_NAVQUERY_SIMPLE ( NavQuery , RECAST_MAX_SEARCH_NODES ) ;
FVector : : FReal RandPt [ 3 ] ;
dtStatus Status = NavQuery . findRandomPointInPoly ( ( dtPolyRef ) PolyID , FMath : : FRand , RandPt ) ;
if ( dtStatusSucceed ( Status ) )
{
OutPoint = Recast2UnrVector ( RandPt ) ;
return true ;
}
}
return false ;
}
2014-03-14 14:13:41 -04:00
uint32 FPImplRecastNavMesh : : GetPolyAreaID ( NavNodeRef PolyID ) const
{
uint32 AreaID = RECAST_NULL_AREA ;
if ( DetourNavMesh )
{
// get poly data from recast
dtPoly const * Poly ;
dtMeshTile const * Tile ;
dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( ( dtPolyRef ) PolyID , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
AreaID = Poly - > getArea ( ) ;
}
}
return AreaID ;
}
2015-08-31 22:22:58 -04:00
void FPImplRecastNavMesh : : SetPolyAreaID ( NavNodeRef PolyID , uint8 AreaID )
{
if ( DetourNavMesh )
{
DetourNavMesh - > setPolyArea ( ( dtPolyRef ) PolyID , AreaID ) ;
}
}
2014-05-22 14:14:52 -04:00
bool FPImplRecastNavMesh : : GetPolyData ( NavNodeRef PolyID , uint16 & Flags , uint8 & AreaType ) const
{
if ( DetourNavMesh )
{
// get poly data from recast
dtPoly const * Poly ;
dtMeshTile const * Tile ;
dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( ( dtPolyRef ) PolyID , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
Flags = Poly - > flags ;
AreaType = Poly - > getArea ( ) ;
return true ;
}
}
return false ;
}
2015-08-20 05:31:39 -04:00
bool FPImplRecastNavMesh : : GetPolyNeighbors ( NavNodeRef PolyID , TArray < FNavigationPortalEdge > & Neighbors ) const
{
if ( DetourNavMesh )
{
dtPolyRef PolyRef = ( dtPolyRef ) PolyID ;
dtPoly const * Poly = 0 ;
dtMeshTile const * Tile = 0 ;
dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( PolyRef , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
INITIALIZE_NAVQUERY_SIMPLE ( NavQuery , RECAST_MAX_SEARCH_NODES ) ;
2021-10-25 20:05:28 -04:00
FVector : : FReal RcLeft [ 3 ] , RcRight [ 3 ] ;
2015-08-20 05:31:39 -04:00
uint8 DummyType1 , DummyType2 ;
uint32 LinkIdx = Poly - > firstLink ;
while ( LinkIdx ! = DT_NULL_LINK )
{
const dtLink & Link = DetourNavMesh - > getLink ( Tile , LinkIdx ) ;
LinkIdx = Link . next ;
Status = NavQuery . getPortalPoints ( PolyRef , Link . ref , RcLeft , RcRight , DummyType1 , DummyType2 ) ;
if ( dtStatusSucceed ( Status ) )
{
FNavigationPortalEdge NeiData ;
NeiData . ToRef = Link . ref ;
NeiData . Left = Recast2UnrealPoint ( RcLeft ) ;
NeiData . Right = Recast2UnrealPoint ( RcRight ) ;
Neighbors . Add ( NeiData ) ;
}
}
return true ;
}
}
return false ;
}
2015-09-01 14:01:10 -04:00
bool FPImplRecastNavMesh : : GetPolyNeighbors ( NavNodeRef PolyID , TArray < NavNodeRef > & Neighbors ) const
{
if ( DetourNavMesh )
{
const dtPolyRef PolyRef = static_cast < dtPolyRef > ( PolyID ) ;
dtPoly const * Poly = 0 ;
dtMeshTile const * Tile = 0 ;
const dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( PolyRef , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
uint32 LinkIdx = Poly - > firstLink ;
Neighbors . Reserve ( DT_VERTS_PER_POLYGON ) ;
while ( LinkIdx ! = DT_NULL_LINK )
{
const dtLink & Link = DetourNavMesh - > getLink ( Tile , LinkIdx ) ;
LinkIdx = Link . next ;
Neighbors . Add ( Link . ref ) ;
}
return true ;
}
}
return false ;
}
2015-10-06 15:59:09 -04:00
bool FPImplRecastNavMesh : : GetPolyEdges ( NavNodeRef PolyID , TArray < FNavigationPortalEdge > & Edges ) const
{
if ( DetourNavMesh )
{
dtPolyRef PolyRef = ( dtPolyRef ) PolyID ;
dtPoly const * Poly = 0 ;
dtMeshTile const * Tile = 0 ;
dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( PolyRef , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
2016-03-14 21:21:09 -04:00
const bool bIsNavLink = ( Poly - > getType ( ) ! = DT_POLYTYPE_GROUND ) ;
2015-10-06 15:59:09 -04:00
2016-03-14 21:21:09 -04:00
for ( uint32 LinkIt = Poly - > firstLink ; LinkIt ! = DT_NULL_LINK ; )
{
const dtLink & LinkInfo = DetourNavMesh - > getLink ( Tile , LinkIt ) ;
if ( LinkInfo . edge > = 0 & & LinkInfo . edge < Poly - > vertCount )
{
FNavigationPortalEdge NeiData ;
NeiData . Left = Recast2UnrealPoint ( & Tile - > verts [ 3 * Poly - > verts [ LinkInfo . edge ] ] ) ;
NeiData . Right = bIsNavLink ? NeiData . Left : Recast2UnrealPoint ( & Tile - > verts [ 3 * Poly - > verts [ ( LinkInfo . edge + 1 ) % Poly - > vertCount ] ] ) ;
NeiData . ToRef = LinkInfo . ref ;
Edges . Add ( NeiData ) ;
}
LinkIt = LinkInfo . next ;
2015-10-06 15:59:09 -04:00
}
return true ;
}
}
return false ;
}
2014-05-22 14:14:52 -04:00
bool FPImplRecastNavMesh : : GetPolyTileIndex ( NavNodeRef PolyID , uint32 & PolyIndex , uint32 & TileIndex ) const
{
if ( DetourNavMesh & & PolyID )
{
uint32 SaltIdx = 0 ;
DetourNavMesh - > decodePolyId ( PolyID , SaltIdx , TileIndex , PolyIndex ) ;
return true ;
}
return false ;
}
2014-08-07 17:34:29 -04:00
bool FPImplRecastNavMesh : : GetClosestPointOnPoly ( NavNodeRef PolyID , const FVector & TestPt , FVector & PointOnPoly ) const
{
if ( DetourNavMesh & & PolyID )
{
INITIALIZE_NAVQUERY_SIMPLE ( NavQuery , RECAST_MAX_SEARCH_NODES ) ;
2021-10-25 20:05:28 -04:00
FVector : : FReal RcTestPos [ 3 ] = { 0.0f } ;
FVector : : FReal RcClosestPos [ 3 ] = { 0.0f } ;
2014-08-07 17:34:29 -04:00
Unr2RecastVector ( TestPt , RcTestPos ) ;
const dtStatus Status = NavQuery . closestPointOnPoly ( PolyID , RcTestPos , RcClosestPos ) ;
if ( dtStatusSucceed ( Status ) )
{
PointOnPoly = Recast2UnrealPoint ( RcClosestPos ) ;
return true ;
}
}
return false ;
}
2014-05-22 14:14:52 -04:00
uint32 FPImplRecastNavMesh : : GetLinkUserId ( NavNodeRef LinkPolyID ) const
{
uint32 UserID = 0 ;
if ( DetourNavMesh )
{
const dtOffMeshConnection * offmeshCon = DetourNavMesh - > getOffMeshConnectionByRef ( LinkPolyID ) ;
if ( offmeshCon )
{
UserID = offmeshCon - > userId ;
}
}
return UserID ;
}
bool FPImplRecastNavMesh : : GetLinkEndPoints ( NavNodeRef LinkPolyID , FVector & PointA , FVector & PointB ) const
{
if ( DetourNavMesh )
{
2021-10-25 20:05:28 -04:00
FVector : : FReal RcPointA [ 3 ] = { 0 } ;
FVector : : FReal RcPointB [ 3 ] = { 0 } ;
2014-05-22 14:14:52 -04:00
dtStatus status = DetourNavMesh - > getOffMeshConnectionPolyEndPoints ( 0 , LinkPolyID , 0 , RcPointA , RcPointB ) ;
if ( dtStatusSucceed ( status ) )
{
PointA = Recast2UnrealPoint ( RcPointA ) ;
PointB = Recast2UnrealPoint ( RcPointB ) ;
return true ;
}
}
return false ;
}
2015-01-15 11:57:16 -05:00
bool FPImplRecastNavMesh : : IsCustomLink ( NavNodeRef PolyRef ) const
{
if ( DetourNavMesh )
{
const dtOffMeshConnection * offMeshCon = DetourNavMesh - > getOffMeshConnectionByRef ( PolyRef ) ;
return offMeshCon & & offMeshCon - > userId ;
}
return false ;
}
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
bool FPImplRecastNavMesh : : GetClusterBounds ( NavNodeRef ClusterRef , FBox & OutBounds ) const
{
if ( DetourNavMesh = = NULL | | ! ClusterRef )
{
return false ;
}
const dtMeshTile * Tile = DetourNavMesh - > getTileByRef ( ClusterRef ) ;
uint32 ClusterIdx = DetourNavMesh - > decodeClusterIdCluster ( ClusterRef ) ;
int32 NumPolys = 0 ;
if ( Tile & & ClusterIdx < ( uint32 ) Tile - > header - > clusterCount )
{
for ( int32 i = 0 ; i < Tile - > header - > offMeshBase ; i + + )
{
if ( Tile - > polyClusters [ i ] = = ClusterIdx )
{
const dtPoly * Poly = & Tile - > polys [ i ] ;
for ( int32 iVert = 0 ; iVert < Poly - > vertCount ; iVert + + )
{
2021-10-25 20:05:28 -04:00
const FVector : : FReal * V = & Tile - > verts [ Poly - > verts [ iVert ] * 3 ] ;
2014-03-14 14:13:41 -04:00
OutBounds + = Recast2UnrealPoint ( V ) ;
}
NumPolys + + ;
}
}
}
return NumPolys > 0 ;
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2014-03-14 14:13:41 -04:00
2014-05-22 14:14:52 -04:00
FORCEINLINE void FPImplRecastNavMesh : : GetEdgesForPathCorridorImpl ( const TArray < NavNodeRef > * PathCorridor , TArray < FNavigationPortalEdge > * PathCorridorEdges , const dtNavMeshQuery & NavQuery ) const
2014-03-14 14:13:41 -04:00
{
const int32 CorridorLenght = PathCorridor - > Num ( ) ;
PathCorridorEdges - > Empty ( CorridorLenght - 1 ) ;
for ( int32 i = 0 ; i < CorridorLenght - 1 ; + + i )
{
unsigned char FromType = 0 , ToType = 0 ;
2021-10-25 20:05:28 -04:00
FVector : : FReal Left [ 3 ] = { 0.f } , Right [ 3 ] = { 0.f } ;
2014-03-14 14:13:41 -04:00
NavQuery . getPortalPoints ( ( * PathCorridor ) [ i ] , ( * PathCorridor ) [ i + 1 ] , Left , Right , FromType , ToType ) ;
PathCorridorEdges - > Add ( FNavigationPortalEdge ( Recast2UnrVector ( Left ) , Recast2UnrVector ( Right ) , ( * PathCorridor ) [ i + 1 ] ) ) ;
}
}
2014-05-22 14:14:52 -04:00
void FPImplRecastNavMesh : : GetEdgesForPathCorridor ( const TArray < NavNodeRef > * PathCorridor , TArray < FNavigationPortalEdge > * PathCorridorEdges ) const
2014-03-14 14:13:41 -04:00
{
// sanity check
if ( DetourNavMesh = = NULL )
{
return ;
}
2014-04-23 19:29:53 -04:00
INITIALIZE_NAVQUERY_SIMPLE ( NavQuery , RECAST_MAX_SEARCH_NODES ) ;
2014-03-14 14:13:41 -04:00
GetEdgesForPathCorridorImpl ( PathCorridor , PathCorridorEdges , NavQuery ) ;
}
2014-11-22 12:57:08 -05:00
bool FPImplRecastNavMesh : : FilterPolys ( TArray < NavNodeRef > & PolyRefs , const FRecastQueryFilter * Filter , const UObject * Owner ) const
2014-03-14 14:13:41 -04:00
{
if ( Filter = = NULL | | DetourNavMesh = = NULL )
{
return false ;
}
for ( int32 PolyIndex = PolyRefs . Num ( ) - 1 ; PolyIndex > = 0 ; - - PolyIndex )
{
dtPolyRef TestRef = PolyRefs [ PolyIndex ] ;
// get poly data from recast
dtPoly const * Poly = NULL ;
dtMeshTile const * Tile = NULL ;
const dtStatus Status = DetourNavMesh - > getTileAndPolyByRef ( TestRef , & Tile , & Poly ) ;
if ( dtStatusSucceed ( Status ) )
{
const bool bPassedFilter = Filter - > passFilter ( TestRef , Tile , Poly ) ;
const bool bWalkableArea = Filter - > getAreaCost ( Poly - > getArea ( ) ) > 0.0f ;
if ( bPassedFilter & & bWalkableArea )
{
continue ;
}
}
PolyRefs . RemoveAt ( PolyIndex , 1 ) ;
}
return true ;
}
bool FPImplRecastNavMesh : : GetPolysInTile ( int32 TileIndex , TArray < FNavPoly > & Polys ) const
{
if ( DetourNavMesh = = NULL | | TileIndex < 0 | | TileIndex > = DetourNavMesh - > getMaxTiles ( ) )
{
return false ;
}
const dtMeshTile * Tile = ( ( const dtNavMesh * ) DetourNavMesh ) - > getTile ( TileIndex ) ;
2015-10-06 15:59:09 -04:00
const int32 MaxPolys = Tile & & Tile - > header ? Tile - > header - > offMeshBase : 0 ;
2014-03-14 14:13:41 -04:00
if ( MaxPolys > 0 )
{
// only ground type polys
int32 BaseIdx = Polys . Num ( ) ;
Polys . AddZeroed ( MaxPolys ) ;
dtPoly * Poly = Tile - > polys ;
for ( int32 i = 0 ; i < MaxPolys ; i + + , Poly + + )
{
FVector PolyCenter ( 0 ) ;
for ( int k = 0 ; k < Poly - > vertCount ; + + k )
{
PolyCenter + = Recast2UnrealPoint ( & Tile - > verts [ Poly - > verts [ k ] * 3 ] ) ;
}
PolyCenter / = Poly - > vertCount ;
FNavPoly & OutPoly = Polys [ BaseIdx + i ] ;
OutPoly . Ref = DetourNavMesh - > encodePolyId ( Tile - > salt , TileIndex , i ) ;
OutPoly . Center = PolyCenter ;
}
}
return ( MaxPolys > 0 ) ;
}
/** Internal. Calculates squared 2d distance of given point PT to segment P-Q. Values given in Recast coordinates */
2021-10-25 20:05:28 -04:00
static FORCEINLINE FVector : : FReal PointDistToSegment2DSquared ( const FVector : : FReal * PT , const FVector : : FReal * P , const FVector : : FReal * Q )
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
FVector : : FReal pqx = Q [ 0 ] - P [ 0 ] ;
FVector : : FReal pqz = Q [ 2 ] - P [ 2 ] ;
FVector : : FReal dx = PT [ 0 ] - P [ 0 ] ;
FVector : : FReal dz = PT [ 2 ] - P [ 2 ] ;
FVector : : FReal d = pqx * pqx + pqz * pqz ;
FVector : : FReal t = pqx * dx + pqz * dz ;
2014-03-14 14:13:41 -04:00
if ( d ! = 0 ) t / = d ;
dx = P [ 0 ] + t * pqx - PT [ 0 ] ;
dz = P [ 2 ] + t * pqz - PT [ 2 ] ;
return dx * dx + dz * dz ;
}
/**
* Traverses given tile's edges and detects the ones that are either poly (i.e. not triangle, but whole navmesh polygon)
* or navmesh edge. Returns a pair of verts for each edge found.
*/
2015-03-04 08:31:40 -05:00
void FPImplRecastNavMesh : : GetDebugPolyEdges ( const dtMeshTile & Tile , bool bInternalEdges , bool bNavMeshEdges , TArray < FVector > & InternalEdgeVerts , TArray < FVector > & NavMeshEdgeVerts ) const
2014-03-14 14:13:41 -04:00
{
2021-10-25 20:05:28 -04:00
static const FVector : : FReal thr = FMath : : Square ( 0.01f ) ;
2014-03-14 14:13:41 -04:00
ensure ( bInternalEdges | | bNavMeshEdges ) ;
const bool bExportAllEdges = bInternalEdges & & ! bNavMeshEdges ;
2015-03-04 08:31:40 -05:00
for ( int i = 0 ; i < Tile . header - > polyCount ; + + i )
2014-03-14 14:13:41 -04:00
{
2015-03-04 08:31:40 -05:00
const dtPoly * Poly = & Tile . polys [ i ] ;
2014-03-14 14:13:41 -04:00
if ( Poly - > getType ( ) ! = DT_POLYTYPE_GROUND )
{
continue ;
}
2015-03-04 08:31:40 -05:00
const dtPolyDetail * pd = & Tile . detailMeshes [ i ] ;
2014-03-14 14:13:41 -04:00
for ( int j = 0 , nj = ( int ) Poly - > vertCount ; j < nj ; + + j )
{
bool bIsExternal = ! bExportAllEdges & & ( Poly - > neis [ j ] = = 0 | | Poly - > neis [ j ] & DT_EXT_LINK ) ;
bool bIsConnected = ! bIsExternal ;
if ( Poly - > getArea ( ) = = RECAST_NULL_AREA )
{
if ( Poly - > neis [ j ] & & ! ( Poly - > neis [ j ] & DT_EXT_LINK ) & &
2015-03-04 08:31:40 -05:00
Poly - > neis [ j ] < = Tile . header - > offMeshBase & &
Tile . polys [ Poly - > neis [ j ] - 1 ] . getArea ( ) ! = RECAST_NULL_AREA )
2014-03-14 14:13:41 -04:00
{
bIsExternal = true ;
bIsConnected = false ;
}
else if ( Poly - > neis [ j ] = = 0 )
{
bIsExternal = true ;
bIsConnected = false ;
}
}
else if ( bIsExternal )
{
unsigned int k = Poly - > firstLink ;
while ( k ! = DT_NULL_LINK )
{
2015-03-04 08:31:40 -05:00
const dtLink & link = DetourNavMesh - > getLink ( & Tile , k ) ;
2014-03-14 14:13:41 -04:00
k = link . next ;
if ( link . edge = = j )
{
bIsConnected = true ;
break ;
}
}
}
TArray < FVector > * EdgeVerts = bInternalEdges & & bIsConnected ? & InternalEdgeVerts
: ( bNavMeshEdges & & bIsExternal & & ! bIsConnected ? & NavMeshEdgeVerts : NULL ) ;
if ( EdgeVerts = = NULL )
{
continue ;
}
2021-10-25 20:05:28 -04:00
const FVector : : FReal * V0 = & Tile . verts [ Poly - > verts [ j ] * 3 ] ;
const FVector : : FReal * V1 = & Tile . verts [ Poly - > verts [ ( j + 1 ) % nj ] * 3 ] ;
2014-03-14 14:13:41 -04:00
// Draw detail mesh edges which align with the actual poly edge.
// This is really slow.
for ( int32 k = 0 ; k < pd - > triCount ; + + k )
{
2015-03-04 08:31:40 -05:00
const unsigned char * t = & ( Tile . detailTris [ ( pd - > triBase + k ) * 4 ] ) ;
2021-10-25 20:05:28 -04:00
const FVector : : FReal * tv [ 3 ] ;
2014-03-14 14:13:41 -04:00
for ( int32 m = 0 ; m < 3 ; + + m )
{
if ( t [ m ] < Poly - > vertCount )
{
2015-03-04 08:31:40 -05:00
tv [ m ] = & Tile . verts [ Poly - > verts [ t [ m ] ] * 3 ] ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-03-04 08:31:40 -05:00
tv [ m ] = & Tile . detailVerts [ ( pd - > vertBase + ( t [ m ] - Poly - > vertCount ) ) * 3 ] ;
2014-03-14 14:13:41 -04:00
}
}
for ( int m = 0 , n = 2 ; m < 3 ; n = m + + )
{
if ( ( ( t [ 3 ] > > ( n * 2 ) ) & 0x3 ) = = 0 )
{
continue ; // Skip inner detail edges.
}
if ( PointDistToSegment2DSquared ( tv [ n ] , V0 , V1 ) < thr & & PointDistToSegment2DSquared ( tv [ m ] , V0 , V1 ) < thr )
{
int32 const AddIdx = ( * EdgeVerts ) . AddZeroed ( 2 ) ;
( * EdgeVerts ) [ AddIdx ] = Recast2UnrVector ( tv [ n ] ) ;
( * EdgeVerts ) [ AddIdx + 1 ] = Recast2UnrVector ( tv [ m ] ) ;
}
}
}
}
}
}
2015-03-04 08:31:40 -05:00
uint8 GetValidEnds ( const dtNavMesh & NavMesh , const dtMeshTile & Tile , const dtPoly & Poly )
2014-03-14 14:13:41 -04:00
{
2015-03-31 20:12:31 -04:00
if ( Poly . getType ( ) = = DT_POLYTYPE_GROUND )
2014-03-14 14:13:41 -04:00
{
return false ;
}
uint8 ValidEnds = FRecastDebugGeometry : : OMLE_None ;
2015-03-04 08:31:40 -05:00
unsigned int k = Poly . firstLink ;
2014-03-14 14:13:41 -04:00
while ( k ! = DT_NULL_LINK )
{
2015-03-04 08:31:40 -05:00
const dtLink & link = NavMesh . getLink ( & Tile , k ) ;
2014-03-14 14:13:41 -04:00
k = link . next ;
if ( link . edge = = 0 )
{
ValidEnds | = FRecastDebugGeometry : : OMLE_Left ;
}
if ( link . edge = = 1 )
{
ValidEnds | = FRecastDebugGeometry : : OMLE_Right ;
}
}
return ValidEnds ;
}
2022-05-05 16:48:48 -04:00
bool FPImplRecastNavMesh : : GetDebugGeometryForTile ( FRecastDebugGeometry & OutGeometry , int32 TileIndex ) const
2014-03-14 14:13:41 -04:00
{
2022-05-05 16:48:48 -04:00
bool bDone = false ;
2015-03-04 08:31:40 -05:00
if ( DetourNavMesh = = nullptr | | TileIndex > = DetourNavMesh - > getMaxTiles ( ) )
2014-03-14 14:13:41 -04:00
{
2022-05-05 16:48:48 -04:00
bDone = true ;
return bDone ;
2015-03-04 08:31:40 -05:00
}
2014-03-14 14:13:41 -04:00
2015-03-04 08:31:40 -05:00
check ( NavMeshOwner ) ;
2014-03-14 14:13:41 -04:00
2015-03-04 08:31:40 -05:00
const dtNavMesh * const ConstNavMesh = DetourNavMesh ;
// presize our tarrays for efficiency
const int32 NumTiles = TileIndex = = INDEX_NONE ? ConstNavMesh - > getMaxTiles ( ) : TileIndex + 1 ;
const int32 StartingTile = TileIndex = = INDEX_NONE ? 0 : TileIndex ;
int32 NumVertsToReserve = 0 ;
int32 NumIndicesToReserve = 0 ;
2019-04-09 17:07:46 -04:00
int32 ForbiddenFlags = OutGeometry . bMarkForbiddenPolys
? GetFilterForbiddenFlags ( ( const FRecastQueryFilter * ) NavMeshOwner - > GetDefaultQueryFilterImpl ( ) )
: 0 ;
2015-03-04 08:31:40 -05:00
const FRecastNavMeshGenerator * Generator = static_cast < const FRecastNavMeshGenerator * > ( NavMeshOwner - > GetGenerator ( ) ) ;
2019-03-18 11:17:25 -04:00
if ( Generator & & Generator - > IsBuildingRestrictedToActiveTiles ( )
// if not active tiles try drawing all tiles
& & NavMeshOwner - > GetActiveTiles ( ) . Num ( ) > 0 )
2015-03-04 08:31:40 -05:00
{
const TArray < FIntPoint > & ActiveTiles = NavMeshOwner - > GetActiveTiles ( ) ;
for ( const FIntPoint & TileLocation : ActiveTiles )
{
const int32 LayersCount = ConstNavMesh - > getTileCountAt ( TileLocation . X , TileLocation . Y ) ;
for ( int32 Layer = 0 ; Layer < LayersCount ; + + Layer )
{
dtMeshTile const * const Tile = ConstNavMesh - > getTileAt ( TileLocation . X , TileLocation . Y , Layer ) ;
if ( Tile ! = nullptr & & Tile - > header ! = nullptr )
{
NumVertsToReserve + = Tile - > header - > vertCount + Tile - > header - > detailVertCount ;
for ( int32 PolyIdx = 0 ; PolyIdx < Tile - > header - > polyCount ; + + PolyIdx )
{
dtPolyDetail const * const DetailPoly = & Tile - > detailMeshes [ PolyIdx ] ;
NumIndicesToReserve + = ( DetailPoly - > triCount * 3 ) ;
}
}
}
}
2016-02-24 14:23:53 -05:00
OutGeometry . MeshVerts . Reserve ( OutGeometry . MeshVerts . Num ( ) + NumVertsToReserve ) ;
OutGeometry . AreaIndices [ 0 ] . Reserve ( OutGeometry . AreaIndices [ 0 ] . Num ( ) + NumIndicesToReserve ) ;
OutGeometry . BuiltMeshIndices . Reserve ( OutGeometry . BuiltMeshIndices . Num ( ) + NumIndicesToReserve ) ;
2015-03-04 08:31:40 -05:00
2016-02-24 14:23:53 -05:00
uint32 VertBase = OutGeometry . MeshVerts . Num ( ) ;
2015-03-04 08:31:40 -05:00
for ( const FIntPoint & TileLocation : ActiveTiles )
{
const int32 LayersCount = ConstNavMesh - > getTileCountAt ( TileLocation . X , TileLocation . Y ) ;
for ( int32 Layer = 0 ; Layer < LayersCount ; + + Layer )
{
dtMeshTile const * const Tile = ConstNavMesh - > getTileAt ( TileLocation . X , TileLocation . Y , Layer ) ;
if ( Tile ! = nullptr & & Tile - > header ! = nullptr )
{
2019-04-09 17:07:46 -04:00
VertBase + = GetTilesDebugGeometry ( Generator , * Tile , VertBase , OutGeometry , INDEX_NONE , ForbiddenFlags ) ;
2015-03-04 08:31:40 -05:00
}
}
}
2022-05-05 16:48:48 -04:00
bDone = true ;
2015-03-04 08:31:40 -05:00
}
else
{
for ( int32 TileIdx = StartingTile ; TileIdx < NumTiles ; + + TileIdx )
2014-03-14 14:13:41 -04:00
{
dtMeshTile const * const Tile = ConstNavMesh - > getTile ( TileIdx ) ;
dtMeshHeader const * const Header = Tile - > header ;
if ( Header ! = NULL )
{
NumVertsToReserve + = Header - > vertCount + Header - > detailVertCount ;
2015-03-04 08:31:40 -05:00
for ( int32 PolyIdx = 0 ; PolyIdx < Header - > polyCount ; + + PolyIdx )
2014-03-14 14:13:41 -04:00
{
dtPolyDetail const * const DetailPoly = & Tile - > detailMeshes [ PolyIdx ] ;
NumIndicesToReserve + = ( DetailPoly - > triCount * 3 ) ;
}
}
}
2016-02-24 14:23:53 -05:00
OutGeometry . MeshVerts . Reserve ( OutGeometry . MeshVerts . Num ( ) + NumVertsToReserve ) ;
OutGeometry . AreaIndices [ 0 ] . Reserve ( OutGeometry . AreaIndices [ 0 ] . Num ( ) + NumIndicesToReserve ) ;
OutGeometry . BuiltMeshIndices . Reserve ( OutGeometry . BuiltMeshIndices . Num ( ) + NumIndicesToReserve ) ;
2014-03-14 14:13:41 -04:00
2016-02-24 14:23:53 -05:00
uint32 VertBase = OutGeometry . MeshVerts . Num ( ) ;
2015-03-04 08:31:40 -05:00
for ( int32 TileIdx = StartingTile ; TileIdx < NumTiles ; + + TileIdx )
2014-03-14 14:13:41 -04:00
{
dtMeshTile const * const Tile = ConstNavMesh - > getTile ( TileIdx ) ;
2015-03-04 08:31:40 -05:00
if ( Tile = = nullptr | | Tile - > header = = nullptr )
2014-03-14 14:13:41 -04:00
{
continue ;
}
2019-04-09 17:07:46 -04:00
VertBase + = GetTilesDebugGeometry ( Generator , * Tile , VertBase , OutGeometry , TileIdx , ForbiddenFlags ) ;
2014-03-14 14:13:41 -04:00
}
2022-05-05 16:48:48 -04:00
if ( TileIndex = = INDEX_NONE )
{
bDone = true ;
}
2015-03-04 08:31:40 -05:00
}
2022-05-05 16:48:48 -04:00
return bDone ;
2015-03-04 08:31:40 -05:00
}
2019-04-09 17:07:46 -04:00
int32 FPImplRecastNavMesh : : GetTilesDebugGeometry ( const FRecastNavMeshGenerator * Generator , const dtMeshTile & Tile , int32 VertBase , FRecastDebugGeometry & OutGeometry , int32 TileIdx , uint16 ForbiddenFlags ) const
2015-03-04 08:31:40 -05:00
{
check ( NavMeshOwner & & DetourNavMesh ) ;
dtMeshHeader const * const Header = Tile . header ;
check ( Header ) ;
2020-02-06 14:37:16 -05:00
# if RECAST_INTERNAL_DEBUG_DATA
OutGeometry . TilesToDisplayInternalData . Push ( FIntPoint ( Header - > x , Header - > y ) ) ;
# endif
2015-03-04 08:31:40 -05:00
const bool bIsBeingBuilt = Generator ! = nullptr & & ! ! NavMeshOwner - > bDistinctlyDrawTilesBeingBuilt
& & Generator - > IsTileChanged ( TileIdx = = INDEX_NONE ? DetourNavMesh - > decodePolyIdTile ( DetourNavMesh - > getTileRef ( & Tile ) ) : TileIdx ) ;
// add all the poly verts
2021-10-25 20:05:28 -04:00
FVector : : FReal * F = Tile . verts ;
2015-03-04 08:31:40 -05:00
for ( int32 VertIdx = 0 ; VertIdx < Header - > vertCount ; + + VertIdx )
{
FVector const VertPos = Recast2UnrVector ( F ) ;
OutGeometry . MeshVerts . Add ( VertPos ) ;
F + = 3 ;
}
2020-02-06 14:37:16 -05:00
2015-03-04 08:31:40 -05:00
int32 const DetailVertIndexBase = Header - > vertCount ;
// add the detail verts
F = Tile . detailVerts ;
for ( int32 DetailVertIdx = 0 ; DetailVertIdx < Header - > detailVertCount ; + + DetailVertIdx )
{
FVector const VertPos = Recast2UnrVector ( F ) ;
OutGeometry . MeshVerts . Add ( VertPos ) ;
F + = 3 ;
}
// add all the indices
for ( int32 PolyIdx = 0 ; PolyIdx < Header - > polyCount ; + + PolyIdx )
{
dtPoly const * const Poly = & Tile . polys [ PolyIdx ] ;
if ( Poly - > getType ( ) = = DT_POLYTYPE_GROUND )
{
dtPolyDetail const * const DetailPoly = & Tile . detailMeshes [ PolyIdx ] ;
2019-04-09 17:07:46 -04:00
TArray < int32 > * Indices = bIsBeingBuilt ? & OutGeometry . BuiltMeshIndices
: ( ( Poly - > flags & ForbiddenFlags ) ! = 0
? & OutGeometry . ForbiddenIndices
: & OutGeometry . AreaIndices [ Poly - > getArea ( ) ] ) ;
2015-03-04 08:31:40 -05:00
// one triangle at a time
for ( int32 TriIdx = 0 ; TriIdx < DetailPoly - > triCount ; + + TriIdx )
{
int32 DetailTriIdx = ( DetailPoly - > triBase + TriIdx ) * 4 ;
const unsigned char * DetailTri = & Tile . detailTris [ DetailTriIdx ] ;
// calc indices into the vert buffer we just populated
int32 TriVertIndices [ 3 ] ;
for ( int32 TriVertIdx = 0 ; TriVertIdx < 3 ; + + TriVertIdx )
{
if ( DetailTri [ TriVertIdx ] < Poly - > vertCount )
{
TriVertIndices [ TriVertIdx ] = VertBase + Poly - > verts [ DetailTri [ TriVertIdx ] ] ;
}
else
{
TriVertIndices [ TriVertIdx ] = VertBase + DetailVertIndexBase + ( DetailPoly - > vertBase + DetailTri [ TriVertIdx ] - Poly - > vertCount ) ;
}
}
Indices - > Add ( TriVertIndices [ 0 ] ) ;
Indices - > Add ( TriVertIndices [ 1 ] ) ;
Indices - > Add ( TriVertIndices [ 2 ] ) ;
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2016-03-21 21:11:39 -04:00
if ( Tile . polyClusters )
2015-03-04 08:31:40 -05:00
{
2016-03-21 21:11:39 -04:00
const uint16 ClusterId = Tile . polyClusters [ PolyIdx ] ;
if ( ClusterId < MAX_uint8 )
{
if ( ClusterId > = OutGeometry . Clusters . Num ( ) )
{
OutGeometry . Clusters . AddDefaulted ( ClusterId - OutGeometry . Clusters . Num ( ) + 1 ) ;
}
TArray < int32 > & ClusterIndices = OutGeometry . Clusters [ ClusterId ] . MeshIndices ;
ClusterIndices . Add ( TriVertIndices [ 0 ] ) ;
ClusterIndices . Add ( TriVertIndices [ 1 ] ) ;
ClusterIndices . Add ( TriVertIndices [ 2 ] ) ;
}
2015-03-04 08:31:40 -05:00
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2015-03-04 08:31:40 -05:00
}
}
}
for ( int32 i = 0 ; i < Header - > offMeshConCount ; + + i )
{
const dtOffMeshConnection * OffMeshConnection = & Tile . offMeshCons [ i ] ;
if ( OffMeshConnection ! = NULL )
{
dtPoly const * const LinkPoly = & Tile . polys [ OffMeshConnection - > poly ] ;
2021-10-25 20:05:28 -04:00
const FVector : : FReal * va = & Tile . verts [ LinkPoly - > verts [ 0 ] * 3 ] ; //OffMeshConnection->pos;
const FVector : : FReal * vb = & Tile . verts [ LinkPoly - > verts [ 1 ] * 3 ] ; //OffMeshConnection->pos[3];
2015-03-04 08:31:40 -05:00
const FRecastDebugGeometry : : FOffMeshLink Link = {
Recast2UnrVector ( va )
, Recast2UnrVector ( vb )
, LinkPoly - > getArea ( )
, ( uint8 ) OffMeshConnection - > getBiDirectional ( )
, GetValidEnds ( * DetourNavMesh , Tile , * LinkPoly )
2021-10-25 20:05:28 -04:00
, UE_REAL_TO_FLOAT_CLAMPED_MAX ( OffMeshConnection - > rad )
2015-03-04 08:31:40 -05:00
} ;
2019-04-09 17:07:46 -04:00
( LinkPoly - > flags & ForbiddenFlags ) ! = 0
? OutGeometry . ForbiddenLinks . Add ( Link )
: OutGeometry . OffMeshLinks . Add ( Link ) ;
2015-03-04 08:31:40 -05:00
}
}
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_SEGMENT_LINKS
2015-03-04 08:31:40 -05:00
for ( int32 i = 0 ; i < Header - > offMeshSegConCount ; + + i )
{
const dtOffMeshSegmentConnection * OffMeshSeg = & Tile . offMeshSeg [ i ] ;
if ( OffMeshSeg ! = NULL )
{
const int32 polyBase = Header - > offMeshSegPolyBase + OffMeshSeg - > firstPoly ;
for ( int32 j = 0 ; j < OffMeshSeg - > npolys ; j + + )
{
dtPoly const * const LinkPoly = & Tile . polys [ polyBase + j ] ;
FRecastDebugGeometry : : FOffMeshSegment Link ;
Link . LeftStart = Recast2UnrealPoint ( & Tile . verts [ LinkPoly - > verts [ 0 ] * 3 ] ) ;
Link . LeftEnd = Recast2UnrealPoint ( & Tile . verts [ LinkPoly - > verts [ 1 ] * 3 ] ) ;
Link . RightStart = Recast2UnrealPoint ( & Tile . verts [ LinkPoly - > verts [ 2 ] * 3 ] ) ;
Link . RightEnd = Recast2UnrealPoint ( & Tile . verts [ LinkPoly - > verts [ 3 ] * 3 ] ) ;
Link . AreaID = LinkPoly - > getArea ( ) ;
Link . Direction = ( uint8 ) OffMeshSeg - > getBiDirectional ( ) ;
Link . ValidEnds = GetValidEnds ( * DetourNavMesh , Tile , * LinkPoly ) ;
const int LinkIdx = OutGeometry . OffMeshSegments . Add ( Link ) ;
2019-04-09 17:07:46 -04:00
ensureMsgf ( ( LinkPoly - > flags & ForbiddenFlags ) = = 0 , TEXT ( " Not implemented " ) ) ;
2019-04-11 06:56:27 -04:00
OutGeometry . OffMeshSegmentAreas [ Link . AreaID ] . Add ( LinkIdx ) ;
2015-03-04 08:31:40 -05:00
}
}
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_SEGMENT_LINKS
2015-03-04 08:31:40 -05:00
2020-06-23 18:40:00 -04:00
# if WITH_NAVMESH_CLUSTER_LINKS
2015-03-13 08:34:27 -04:00
for ( int32 i = 0 ; i < Header - > clusterCount ; i + + )
2015-03-04 08:31:40 -05:00
{
const dtCluster & c0 = Tile . clusters [ i ] ;
uint32 iLink = c0 . firstLink ;
while ( iLink ! = DT_NULL_LINK )
{
const dtClusterLink & link = DetourNavMesh - > getClusterLink ( & Tile , iLink ) ;
iLink = link . next ;
dtMeshTile const * const OtherTile = DetourNavMesh - > getTileByRef ( link . ref ) ;
if ( OtherTile )
{
int32 linkedIdx = DetourNavMesh - > decodeClusterIdCluster ( link . ref ) ;
const dtCluster & c1 = OtherTile - > clusters [ linkedIdx ] ;
FRecastDebugGeometry : : FClusterLink LinkGeom ;
LinkGeom . FromCluster = Recast2UnrealPoint ( c0 . center ) ;
LinkGeom . ToCluster = Recast2UnrealPoint ( c1 . center ) ;
if ( linkedIdx > i | | TileIdx > ( int32 ) DetourNavMesh - > decodeClusterIdTile ( link . ref ) )
{
FVector UpDir ( 0 , 0 , 1.0f ) ;
FVector LinkDir = ( LinkGeom . ToCluster - LinkGeom . FromCluster ) . GetSafeNormal ( ) ;
FVector SideDir = FVector : : CrossProduct ( LinkDir , UpDir ) ;
LinkGeom . FromCluster + = SideDir * 40.0f ;
LinkGeom . ToCluster + = SideDir * 40.0f ;
}
OutGeometry . ClusterLinks . Add ( LinkGeom ) ;
}
}
2015-03-13 08:34:27 -04:00
}
2020-06-23 18:40:00 -04:00
# endif // WITH_NAVMESH_CLUSTER_LINKS
2015-03-04 08:31:40 -05:00
// Get tile edges and navmesh edges
if ( OutGeometry . bGatherPolyEdges | | OutGeometry . bGatherNavMeshEdges )
{
GetDebugPolyEdges ( Tile , ! ! OutGeometry . bGatherPolyEdges , ! ! OutGeometry . bGatherNavMeshEdges
, OutGeometry . PolyEdges , OutGeometry . NavMeshEdges ) ;
}
return Header - > vertCount + Header - > detailVertCount ;
2014-03-14 14:13:41 -04:00
}
FBox FPImplRecastNavMesh : : GetNavMeshBounds ( ) const
{
2017-02-08 17:53:41 -05:00
FBox Bbox ( ForceInit ) ;
2014-03-14 14:13:41 -04:00
// @todo, calc once and cache it
if ( DetourNavMesh )
{
// workaround for privacy issue in the recast API
dtNavMesh const * const ConstRecastNavMesh = DetourNavMesh ;
// spin through all the tiles and accumulate the bounds
for ( int32 TileIdx = 0 ; TileIdx < DetourNavMesh - > getMaxTiles ( ) ; + + TileIdx )
{
dtMeshTile const * const Tile = ConstRecastNavMesh - > getTile ( TileIdx ) ;
if ( Tile )
{
dtMeshHeader const * const Header = Tile - > header ;
if ( Header )
{
const FBox NodeBox = Recast2UnrealBox ( Header - > bmin , Header - > bmax ) ;
Bbox + = NodeBox ;
}
}
}
}
return Bbox ;
}
FBox FPImplRecastNavMesh : : GetNavMeshTileBounds ( int32 TileIndex ) const
{
2017-02-08 17:53:41 -05:00
FBox Bbox ( ForceInit ) ;
2014-03-14 14:13:41 -04:00
if ( DetourNavMesh & & TileIndex > = 0 & & TileIndex < DetourNavMesh - > getMaxTiles ( ) )
{
// workaround for privacy issue in the recast API
dtNavMesh const * const ConstRecastNavMesh = DetourNavMesh ;
dtMeshTile const * const Tile = ConstRecastNavMesh - > getTile ( TileIndex ) ;
if ( Tile )
{
dtMeshHeader const * const Header = Tile - > header ;
if ( Header )
{
Bbox = Recast2UnrealBox ( Header - > bmin , Header - > bmax ) ;
}
}
}
return Bbox ;
}
/** Retrieves XY coordinates of tile specified by index */
2014-10-21 18:45:47 -04:00
bool FPImplRecastNavMesh : : GetNavMeshTileXY ( int32 TileIndex , int32 & OutX , int32 & OutY , int32 & OutLayer ) const
2014-03-14 14:13:41 -04:00
{
if ( DetourNavMesh & & TileIndex > = 0 & & TileIndex < DetourNavMesh - > getMaxTiles ( ) )
{
// workaround for privacy issue in the recast API
dtNavMesh const * const ConstRecastNavMesh = DetourNavMesh ;
dtMeshTile const * const Tile = ConstRecastNavMesh - > getTile ( TileIndex ) ;
if ( Tile )
{
dtMeshHeader const * const Header = Tile - > header ;
if ( Header )
{
OutX = Header - > x ;
OutY = Header - > y ;
OutLayer = Header - > layer ;
2014-10-21 18:45:47 -04:00
return true ;
2014-03-14 14:13:41 -04:00
}
}
}
2014-10-21 18:45:47 -04:00
return false ;
2014-03-14 14:13:41 -04:00
}
2014-10-21 18:45:47 -04:00
bool FPImplRecastNavMesh : : GetNavMeshTileXY ( const FVector & Point , int32 & OutX , int32 & OutY ) const
2014-03-14 14:13:41 -04:00
{
if ( DetourNavMesh )
{
// workaround for privacy issue in the recast API
dtNavMesh const * const ConstRecastNavMesh = DetourNavMesh ;
2021-10-25 20:05:28 -04:00
const FVector RecastPt = Unreal2RecastPoint ( Point ) ;
2014-03-14 14:13:41 -04:00
int32 TileX = 0 ;
int32 TileY = 0 ;
ConstRecastNavMesh - > calcTileLoc ( & RecastPt . X , & TileX , & TileY ) ;
OutX = TileX ;
OutY = TileY ;
2014-10-21 18:45:47 -04:00
return true ;
2014-03-14 14:13:41 -04:00
}
2014-10-21 18:45:47 -04:00
return false ;
2014-03-14 14:13:41 -04:00
}
void FPImplRecastNavMesh : : GetNavMeshTilesAt ( int32 TileX , int32 TileY , TArray < int32 > & Indices ) const
{
if ( DetourNavMesh )
{
// workaround for privacy issue in the recast API
dtNavMesh const * const ConstRecastNavMesh = DetourNavMesh ;
const int32 MaxTiles = ConstRecastNavMesh - > getTileCountAt ( TileX , TileY ) ;
TArray < const dtMeshTile * > Tiles ;
Tiles . AddZeroed ( MaxTiles ) ;
2014-09-29 04:23:44 -04:00
const int32 NumTiles = ConstRecastNavMesh - > getTilesAt ( TileX , TileY , Tiles . GetData ( ) , MaxTiles ) ;
2014-03-14 14:13:41 -04:00
for ( int32 i = 0 ; i < NumTiles ; i + + )
{
dtTileRef TileRef = ConstRecastNavMesh - > getTileRef ( Tiles [ i ] ) ;
if ( TileRef )
{
const int32 TileIndex = ( int32 ) ConstRecastNavMesh - > decodePolyIdTile ( TileRef ) ;
Indices . Add ( TileIndex ) ;
}
}
}
}
2014-10-30 09:53:10 -04:00
void FPImplRecastNavMesh : : GetNavMeshTilesIn ( const TArray < FBox > & InclusionBounds , TArray < int32 > & Indices ) const
{
if ( DetourNavMesh )
{
2021-10-25 20:05:28 -04:00
const FVector : : FReal * NavMeshOrigin = DetourNavMesh - > getParams ( ) - > orig ;
const FVector : : FReal TileSize = DetourNavMesh - > getParams ( ) - > tileWidth ;
2014-10-30 09:53:10 -04:00
2016-06-13 12:20:22 -04:00
// Generate a set of all possible tile coordinates that belong to requested bounds
2014-10-30 09:53:10 -04:00
TSet < FIntPoint > TileCoords ;
for ( const FBox & Bounds : InclusionBounds )
{
const FBox RcBounds = Unreal2RecastBox ( Bounds ) ;
const int32 XMin = FMath : : FloorToInt ( ( RcBounds . Min . X - NavMeshOrigin [ 0 ] ) / TileSize ) ;
const int32 XMax = FMath : : FloorToInt ( ( RcBounds . Max . X - NavMeshOrigin [ 0 ] ) / TileSize ) ;
const int32 YMin = FMath : : FloorToInt ( ( RcBounds . Min . Z - NavMeshOrigin [ 2 ] ) / TileSize ) ;
const int32 YMax = FMath : : FloorToInt ( ( RcBounds . Max . Z - NavMeshOrigin [ 2 ] ) / TileSize ) ;
for ( int32 y = YMin ; y < = YMax ; + + y )
{
for ( int32 x = XMin ; x < = XMax ; + + x )
{
TileCoords . Add ( FIntPoint ( x , y ) ) ;
}
}
}
// We guess that each tile has 3 layers in average
Indices . Reserve ( TileCoords . Num ( ) * 3 ) ;
2016-06-13 12:20:22 -04:00
TArray < const dtMeshTile * > MeshTiles ;
MeshTiles . Reserve ( 3 ) ;
for ( const FIntPoint & TileCoord : TileCoords )
2014-10-30 09:53:10 -04:00
{
int32 MaxTiles = DetourNavMesh - > getTileCountAt ( TileCoord . X , TileCoord . Y ) ;
2016-06-13 12:20:22 -04:00
if ( MaxTiles > 0 )
2014-10-30 09:53:10 -04:00
{
2016-06-13 12:20:22 -04:00
MeshTiles . SetNumZeroed ( MaxTiles , false ) ;
const int32 MeshTilesCount = DetourNavMesh - > getTilesAt ( TileCoord . X , TileCoord . Y , MeshTiles . GetData ( ) , MaxTiles ) ;
for ( int32 i = 0 ; i < MeshTilesCount ; + + i )
2014-10-30 09:53:10 -04:00
{
2016-06-13 12:20:22 -04:00
const dtMeshTile * MeshTile = MeshTiles [ i ] ;
dtTileRef TileRef = DetourNavMesh - > getTileRef ( MeshTile ) ;
if ( TileRef )
{
// Consider only mesh tiles that actually belong to a requested bounds
FBox TileBounds = Recast2UnrealBox ( MeshTile - > header - > bmin , MeshTile - > header - > bmax ) ;
for ( const FBox & RequestedBounds : InclusionBounds )
{
if ( TileBounds . Intersect ( RequestedBounds ) )
{
int32 TileIndex = ( int32 ) DetourNavMesh - > decodePolyIdTile ( TileRef ) ;
Indices . Add ( TileIndex ) ;
break ;
}
}
}
2014-10-30 09:53:10 -04:00
}
}
}
}
}
2014-03-14 14:13:41 -04:00
float FPImplRecastNavMesh : : GetTotalDataSize ( ) const
{
float TotalBytes = sizeof ( * this ) ;
if ( DetourNavMesh )
{
// iterate all tiles and sum up their DataSize
dtNavMesh const * ConstNavMesh = DetourNavMesh ;
for ( int i = 0 ; i < ConstNavMesh - > getMaxTiles ( ) ; + + i )
{
const dtMeshTile * Tile = ConstNavMesh - > getTile ( i ) ;
if ( Tile ! = NULL & & Tile - > header ! = NULL )
{
TotalBytes + = Tile - > dataSize ;
}
}
}
return TotalBytes / 1024 ;
}
2019-01-08 11:38:48 -05:00
# if !UE_BUILD_SHIPPING
int32 FPImplRecastNavMesh : : GetCompressedTileCacheSize ( )
{
int32 CompressedTileCacheSize = 0 ;
for ( TPair < FIntPoint , TArray < FNavMeshTileData > > & TilePairIter : CompressedTileCacheLayers )
{
TArray < FNavMeshTileData > & NavMeshTileDataArray = TilePairIter . Value ;
for ( FNavMeshTileData & NavMeshTileDataIter : NavMeshTileDataArray )
{
CompressedTileCacheSize + = NavMeshTileDataIter . DataSize ;
}
}
return CompressedTileCacheSize ;
}
# endif
2014-03-14 14:13:41 -04:00
void FPImplRecastNavMesh : : ApplyWorldOffset ( const FVector & InOffset , bool bWorldShift )
{
if ( DetourNavMesh ! = NULL )
{
// transform offset to Recast space
2021-10-25 20:05:28 -04:00
const FVector OffsetRC = Unreal2RecastPoint ( InOffset ) ;
2014-03-14 14:13:41 -04:00
// apply offset
DetourNavMesh - > applyWorldOffset ( & OffsetRC . X ) ;
}
}
uint16 FPImplRecastNavMesh : : GetFilterForbiddenFlags ( const FRecastQueryFilter * Filter )
{
return ( ( const dtQueryFilter * ) Filter ) - > getExcludeFlags ( ) ;
}
void FPImplRecastNavMesh : : SetFilterForbiddenFlags ( FRecastQueryFilter * Filter , uint16 ForbiddenFlags )
{
( ( dtQueryFilter * ) Filter ) - > setExcludeFlags ( ForbiddenFlags ) ;
// include-exclude don't need to be symmetrical, filter will check both conditions
}
2015-03-20 07:37:38 -04:00
void FPImplRecastNavMesh : : OnAreaCostChanged ( )
{
2021-10-25 20:05:28 -04:00
struct FRealntPair
2015-03-20 07:37:38 -04:00
{
2021-10-25 20:05:28 -04:00
FVector : : FReal Score ;
2015-03-20 07:37:38 -04:00
int32 Index ;
2021-10-25 20:05:28 -04:00
FRealntPair ( ) : Score ( MAX_FLT ) , Index ( 0 ) { }
FRealntPair ( int32 AreaId , FVector : : FReal TravelCost , FVector : : FReal EntryCost ) : Score ( TravelCost + EntryCost ) , Index ( AreaId ) { }
2015-03-20 07:37:38 -04:00
2021-10-25 20:05:28 -04:00
bool operator < ( const FRealntPair & Other ) const { return Score < Other . Score ; }
2015-03-20 07:37:38 -04:00
} ;
if ( NavMeshOwner & & DetourNavMesh )
{
const INavigationQueryFilterInterface * NavFilter = NavMeshOwner - > GetDefaultQueryFilterImpl ( ) ;
const dtQueryFilter * DetourFilter = ( ( const FRecastQueryFilter * ) NavFilter ) - > GetAsDetourQueryFilter ( ) ;
2021-10-25 20:05:28 -04:00
TArray < FRealntPair > AreaData ;
2015-03-20 07:37:38 -04:00
AreaData . Reserve ( RECAST_MAX_AREAS ) ;
for ( int32 Idx = 0 ; Idx < RECAST_MAX_AREAS ; Idx + + )
{
2021-10-25 20:05:28 -04:00
AreaData . Add ( FRealntPair ( Idx , DetourFilter - > getAreaCost ( Idx ) , DetourFilter - > getAreaFixedCost ( Idx ) ) ) ;
2015-03-20 07:37:38 -04:00
}
AreaData . Sort ( ) ;
uint8 AreaCostOrder [ RECAST_MAX_AREAS ] ;
for ( int32 Idx = 0 ; Idx < RECAST_MAX_AREAS ; Idx + + )
{
AreaCostOrder [ AreaData [ Idx ] . Index ] = Idx ;
}
DetourNavMesh - > applyAreaCostOrder ( AreaCostOrder ) ;
}
}
2015-01-12 21:18:44 -05:00
void FPImplRecastNavMesh : : RemoveTileCacheLayers ( int32 TileX , int32 TileY )
{
CompressedTileCacheLayers . Remove ( FIntPoint ( TileX , TileY ) ) ;
}
void FPImplRecastNavMesh : : RemoveTileCacheLayer ( int32 TileX , int32 TileY , int32 LayerIdx )
{
TArray < FNavMeshTileData > * ExistingLayersList = CompressedTileCacheLayers . Find ( FIntPoint ( TileX , TileY ) ) ;
if ( ExistingLayersList )
{
if ( ExistingLayersList - > IsValidIndex ( LayerIdx ) )
{
ExistingLayersList - > RemoveAt ( LayerIdx ) ;
2016-03-29 16:33:59 -04:00
for ( int32 Idx = LayerIdx ; Idx < ExistingLayersList - > Num ( ) ; Idx + + )
{
( * ExistingLayersList ) [ Idx ] . LayerIndex = Idx ;
}
2015-01-12 21:18:44 -05:00
}
if ( ExistingLayersList - > Num ( ) = = 0 )
{
2020-02-06 14:37:16 -05:00
RemoveTileCacheLayers ( TileX , TileY ) ;
# if RECAST_INTERNAL_DEBUG_DATA
NavMeshOwner - > RemoveTileDebugData ( TileX , TileY ) ;
# endif
2015-01-12 21:18:44 -05:00
}
}
}
void FPImplRecastNavMesh : : AddTileCacheLayers ( int32 TileX , int32 TileY , const TArray < FNavMeshTileData > & Layers )
{
CompressedTileCacheLayers . Add ( FIntPoint ( TileX , TileY ) , Layers ) ;
}
void FPImplRecastNavMesh : : AddTileCacheLayer ( int32 TileX , int32 TileY , int32 LayerIdx , const FNavMeshTileData & LayerData )
{
TArray < FNavMeshTileData > * ExistingLayersList = CompressedTileCacheLayers . Find ( FIntPoint ( TileX , TileY ) ) ;
if ( ExistingLayersList )
{
ExistingLayersList - > SetNum ( FMath : : Max ( ExistingLayersList - > Num ( ) , LayerIdx + 1 ) ) ;
( * ExistingLayersList ) [ LayerIdx ] = LayerData ;
}
else
{
TArray < FNavMeshTileData > LayersList ;
LayersList . SetNum ( FMath : : Max ( LayersList . Num ( ) , LayerIdx + 1 ) ) ;
LayersList [ LayerIdx ] = LayerData ;
CompressedTileCacheLayers . Add ( FIntPoint ( TileX , TileY ) , LayersList ) ;
}
}
2016-09-13 18:15:38 -04:00
void FPImplRecastNavMesh : : MarkEmptyTileCacheLayers ( int32 TileX , int32 TileY )
{
if ( ! CompressedTileCacheLayers . Contains ( FIntPoint ( TileX , TileY ) ) )
{
TArray < FNavMeshTileData > EmptyLayersList ;
CompressedTileCacheLayers . Add ( FIntPoint ( TileX , TileY ) , EmptyLayersList ) ;
}
}
2015-01-12 21:18:44 -05:00
FNavMeshTileData FPImplRecastNavMesh : : GetTileCacheLayer ( int32 TileX , int32 TileY , int32 LayerIdx ) const
{
const TArray < FNavMeshTileData > * LayersList = CompressedTileCacheLayers . Find ( FIntPoint ( TileX , TileY ) ) ;
if ( LayersList & & LayersList - > IsValidIndex ( LayerIdx ) )
{
return ( * LayersList ) [ LayerIdx ] ;
}
return FNavMeshTileData ( ) ;
}
TArray < FNavMeshTileData > FPImplRecastNavMesh : : GetTileCacheLayers ( int32 TileX , int32 TileY ) const
{
return CompressedTileCacheLayers . FindRef ( FIntPoint ( TileX , TileY ) ) ;
}
2016-09-13 18:15:38 -04:00
bool FPImplRecastNavMesh : : HasTileCacheLayers ( int32 TileX , int32 TileY ) const
{
return CompressedTileCacheLayers . Contains ( FIntPoint ( TileX , TileY ) ) ;
}
2014-03-14 14:13:41 -04:00
# undef INITIALIZE_NAVQUERY
2022-01-13 10:09:07 -05:00
# endif // WITH_RECAST