Files
yoan stamant 05babfc50a [NavigationSystem] deprecated methods/properties cleanup
#rb Mieszko.Zielinski

[CL 34671795 by yoan stamant in ue5-main branch]
2024-06-26 07:24:40 -04:00

601 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NavMesh/RecastNavMeshDataChunk.h"
#include "Engine/World.h"
#include "NavigationSystem.h"
#include "NavMesh/RecastNavMesh.h"
#include "NavMesh/PImplRecastNavMesh.h"
#include "NavMesh/RecastHelpers.h"
#include "NavMesh/RecastVersion.h"
#include "NavMesh/RecastNavMeshGenerator.h"
#if WITH_RECAST
#include "Detour/DetourNavMeshBuilder.h"
#endif // WITH_RECAST
#include UE_INLINE_GENERATED_CPP_BY_NAME(RecastNavMeshDataChunk)
//----------------------------------------------------------------------//
// FRecastTileData
//----------------------------------------------------------------------//
FRecastTileData::FRawData::FRawData(uint8* InData)
: RawData(InData)
{
}
FRecastTileData::FRawData::~FRawData()
{
#if WITH_RECAST
dtFree(RawData, DT_ALLOC_PERM_TILE_DATA);
#else
FMemory::Free(RawData);
#endif
}
FRecastTileData::FRecastTileData()
: OriginalX(0)
, OriginalY(0)
, X(0)
, Y(0)
, Layer(0)
, TileDataSize(0)
, TileCacheDataSize(0)
, bAttached(false)
{
}
FRecastTileData::FRecastTileData(int32 DataSize, uint8* RawData, int32 CacheDataSize, uint8* CacheRawData)
: OriginalX(0)
, OriginalY(0)
, X(0)
, Y(0)
, Layer(0)
, TileDataSize(DataSize)
, TileCacheDataSize(CacheDataSize)
, bAttached(false)
{
TileRawData = MakeShareable(new FRawData(RawData));
TileCacheRawData = MakeShareable(new FRawData(CacheRawData));
}
// Helper to duplicate recast raw data
static uint8* DuplicateRecastRawData(const uint8* Src, int32 SrcSize)
{
#if WITH_RECAST
uint8* DupData = (uint8*)dtAlloc(SrcSize, DT_ALLOC_PERM_TILE_DATA);
#else
uint8* DupData = (uint8*)FMemory::Malloc(SrcSize);
#endif
FMemory::Memcpy(DupData, Src, SrcSize);
return DupData;
}
namespace UE::NavMesh::Private
{
bool IsUsingActiveTileGeneration(const ARecastNavMesh& NavMesh)
{
#if WITH_RECAST
const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(NavMesh.GetWorld());
if (NavSys)
{
return NavMesh.IsUsingActiveTilesGeneration(*NavSys);
}
#endif // WITH_RECAST
return false;
}
} // namespace UE::NavMesh::Private
//----------------------------------------------------------------------//
// URecastNavMeshDataChunk
//----------------------------------------------------------------------//
URecastNavMeshDataChunk::URecastNavMeshDataChunk(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void URecastNavMeshDataChunk::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
int32 NavMeshVersion = NAVMESHVER_LATEST;
Ar << NavMeshVersion;
// when writing, write a zero here for now. will come back and fill it in later.
int64 RecastNavMeshSizeBytes = 0;
int64 RecastNavMeshSizePos = Ar.Tell();
Ar << RecastNavMeshSizeBytes;
if (Ar.IsLoading())
{
auto CleanUpBadVersion = [&Ar, RecastNavMeshSizePos, RecastNavMeshSizeBytes]()
{
// incompatible, just skip over this data. Navmesh needs rebuilt.
Ar.Seek(RecastNavMeshSizePos + RecastNavMeshSizeBytes);
};
if (NavMeshVersion < NAVMESHVER_MIN_COMPATIBLE)
{
UE_LOG(LogNavigation, Warning, TEXT("%s: URecastNavMeshDataChunk: Nav mesh version %d < Min compatible %d. Nav mesh needs to be rebuilt. \n"), *GetFullName(), NavMeshVersion, NAVMESHVER_MIN_COMPATIBLE);
CleanUpBadVersion();
}
else if (NavMeshVersion > NAVMESHVER_LATEST)
{
UE_LOG(LogNavigation, Warning, TEXT("%s: URecastNavMeshDataChunk: Nav mesh version %d > NAVMESHVER_LATEST %d. Newer nav mesh should not be loaded by older versioned code. At a minimum the nav mesh needs to be rebuilt. \n"), *GetFullName(), NavMeshVersion, NAVMESHVER_LATEST);
CleanUpBadVersion();
}
#if WITH_RECAST
else if (RecastNavMeshSizeBytes > 4)
{
SerializeRecastData(Ar, NavMeshVersion);
}
#endif// WITH_RECAST
else
{
// empty, just skip over this data
Ar.Seek(RecastNavMeshSizePos + RecastNavMeshSizeBytes);
}
}
else if (Ar.IsSaving())
{
#if WITH_RECAST
SerializeRecastData(Ar, NavMeshVersion);
#endif// WITH_RECAST
int64 CurPos = Ar.Tell();
RecastNavMeshSizeBytes = CurPos - RecastNavMeshSizePos;
Ar.Seek(RecastNavMeshSizePos);
Ar << RecastNavMeshSizeBytes;
Ar.Seek(CurPos);
}
}
#if WITH_RECAST
void URecastNavMeshDataChunk::SerializeRecastData(FArchive& Ar, int32 NavMeshVersion)
{
int32 TileNum = Tiles.Num();
Ar << TileNum;
if (Ar.IsLoading())
{
Tiles.Empty(TileNum);
for (int32 TileIdx = 0; TileIdx < TileNum; TileIdx++)
{
int32 TileDataSize = 0;
Ar << TileDataSize;
// Load tile data
uint8* TileRawData = nullptr;
FPImplRecastNavMesh::SerializeRecastMeshTile(Ar, NavMeshVersion, TileRawData, TileDataSize); //allocates TileRawData on load
if (TileRawData != nullptr)
{
// Load compressed tile cache layer
int32 TileCacheDataSize = 0;
uint8* TileCacheRawData = nullptr;
FPImplRecastNavMesh::SerializeCompressedTileCacheData(Ar, NavMeshVersion, TileCacheRawData, TileCacheDataSize); //allocates TileCacheRawData on load
// We are owner of tile raw data
FRecastTileData TileData(TileDataSize, TileRawData, TileCacheDataSize, TileCacheRawData);
Tiles.Add(TileData);
}
}
}
else if (Ar.IsSaving())
{
for (FRecastTileData& TileData : Tiles)
{
if (TileData.TileRawData.IsValid())
{
// Save tile itself
Ar << TileData.TileDataSize;
FPImplRecastNavMesh::SerializeRecastMeshTile(Ar, NavMeshVersion, TileData.TileRawData->RawData, TileData.TileDataSize);
// Save compressed tile cache layer
FPImplRecastNavMesh::SerializeCompressedTileCacheData(Ar, NavMeshVersion, TileData.TileCacheRawData->RawData, TileData.TileCacheDataSize);
}
}
}
}
#endif// WITH_RECAST
#if WITH_RECAST
TArray<FNavTileRef> URecastNavMeshDataChunk::AttachTiles(ARecastNavMesh& NavMesh)
{
check(NavMesh.GetWorld());
const bool bIsGameWorld = NavMesh.GetWorld()->IsGameWorld();
// In editor we still need to own the data so a copy will be made.
const bool bKeepCopyOfData = !bIsGameWorld;
const bool bKeepCopyOfCacheData = !bIsGameWorld;
return AttachTiles(NavMesh, bKeepCopyOfData, bKeepCopyOfCacheData);
}
TArray<FNavTileRef> URecastNavMeshDataChunk::AttachTiles(ARecastNavMesh& NavMesh, const bool bKeepCopyOfData, const bool bKeepCopyOfCacheData)
{
UE_LOG(LogNavigation, Verbose, TEXT("%s Attaching to NavMesh - %s"), ANSI_TO_TCHAR(__FUNCTION__), *NavigationDataName.ToString());
TArray<FNavTileRef> Result;
Result.Reserve(Tiles.Num());
dtNavMesh* DetourNavMesh = NavMesh.GetRecastMesh();
if (DetourNavMesh != nullptr)
{
TSet<FIntPoint>* ActiveTiles = nullptr;
if (UE::NavMesh::Private::IsUsingActiveTileGeneration(NavMesh))
{
ActiveTiles = &NavMesh.GetActiveTileSet();
ActiveTiles->Reserve(ActiveTiles->Num() + Tiles.Num());
}
for (FRecastTileData& TileData : Tiles)
{
if (!TileData.bAttached && TileData.TileRawData.IsValid())
{
if (TileData.TileRawData->RawData == nullptr)
{
UE_LOG(LogNavigation, Warning, TEXT("Null rawdata. This can be caused by the reuse of unloaded sublevels. 's.ForceGCAfterLevelStreamedOut 1' can be used until this gets fixed."));
continue;
}
const dtMeshHeader* Header = (dtMeshHeader*)TileData.TileRawData->RawData;
if (Header->version != DT_NAVMESH_VERSION)
{
continue;
}
// If there was a previous tile at the location remove it
if (const dtMeshTile* PreExistingTile = DetourNavMesh->getTileAt(Header->x, Header->y, Header->layer))
{
if (const dtTileRef PreExistingTileRef = DetourNavMesh->getTileRef(PreExistingTile))
{
NavMesh.LogRecastTile(ANSI_TO_TCHAR(__FUNCTION__), FName(" "), FName("removing"), *DetourNavMesh, Header->x, Header->y, Header->layer, PreExistingTileRef);
DetourNavMesh->removeTile(PreExistingTileRef, nullptr, nullptr);
}
}
// Attach mesh tile to target nav mesh
dtTileRef TileRef = 0;
const dtMeshTile* MeshTile = nullptr;
dtStatus status = DetourNavMesh->addTile(TileData.TileRawData->RawData, TileData.TileDataSize, DT_TILE_FREE_DATA, 0, &TileRef);
if (dtStatusFailed(status))
{
if (dtStatusDetail(status, DT_OUT_OF_MEMORY))
{
UE_LOG(LogNavigation, Warning, TEXT("%s> Failed to add tile (%d,%d:%d), %d tile limit reached! (from: %s). If using FixedTilePoolSize, try increasing the TilePoolSize or using bigger tiles."),
*NavMesh.GetName(), Header->x, Header->y, Header->layer, DetourNavMesh->getMaxTiles(), ANSI_TO_TCHAR(__FUNCTION__));
}
continue;
}
else
{
MeshTile = DetourNavMesh->getTileByRef(TileRef);
check(MeshTile);
TileData.X = MeshTile->header->x;
TileData.Y = MeshTile->header->y;
TileData.Layer = MeshTile->header->layer;
TileData.bAttached = true;
}
NavMesh.LogRecastTile(ANSI_TO_TCHAR(__FUNCTION__), FName(" "), FName("added"), *DetourNavMesh, TileData.X, TileData.Y, TileData.Layer, TileRef);
if (ActiveTiles)
{
ActiveTiles->FindOrAdd(FIntPoint(TileData.X, TileData.Y));
}
if (bKeepCopyOfData == false)
{
// We don't own tile data anymore it will be released by recast navmesh
TileData.TileDataSize = 0;
TileData.TileRawData->RawData = nullptr;
}
else
{
// In the editor we still need to own data, so make a copy of it
TileData.TileRawData->RawData = DuplicateRecastRawData(TileData.TileRawData->RawData, TileData.TileDataSize);
}
// Attach tile cache layer to target nav mesh
if (TileData.TileCacheDataSize > 0)
{
FBox TileBBox = Recast2UnrealBox(MeshTile->header->bmin, MeshTile->header->bmax);
FNavMeshTileData LayerData(TileData.TileCacheRawData->RawData, TileData.TileCacheDataSize, TileData.Layer, TileBBox);
NavMesh.GetRecastNavMeshImpl()->AddTileCacheLayer(TileData.X, TileData.Y, TileData.Layer, LayerData);
if (bKeepCopyOfCacheData == false)
{
// We don't own tile cache data anymore it will be released by navmesh
TileData.TileCacheDataSize = 0;
TileData.TileCacheRawData->RawData = nullptr;
}
else
{
// In the editor we still need to own data, so make a copy of it
TileData.TileCacheRawData->RawData = DuplicateRecastRawData(TileData.TileCacheRawData->RawData, TileData.TileCacheDataSize);
}
}
Result.Add(FNavTileRef(TileRef));
}
}
}
UE_LOG(LogNavigation, Verbose, TEXT("Attached %d tiles to NavMesh - %s"), Result.Num(), *NavigationDataName.ToString());
return Result;
}
TArray<FNavTileRef> URecastNavMeshDataChunk::DetachTiles(ARecastNavMesh& NavMesh)
{
check(NavMesh.GetWorld());
const bool bIsGameWorld = NavMesh.GetWorld()->IsGameWorld();
// Keep data in game worlds (in editor we have a copy of the data so we don't keep it).
const bool bTakeDataOwnership = bIsGameWorld;
const bool bTakeCacheDataOwnership = bIsGameWorld;
return DetachTiles(NavMesh, bTakeDataOwnership, bTakeCacheDataOwnership);
}
TArray<FNavTileRef> URecastNavMeshDataChunk::DetachTiles(ARecastNavMesh& NavMesh, const bool bTakeDataOwnership, const bool bTakeCacheDataOwnership)
{
UE_LOG(LogNavigation, Verbose, TEXT("%s Detaching from %s"), ANSI_TO_TCHAR(__FUNCTION__), *NavigationDataName.ToString());
TArray<FNavTileRef> Result;
Result.Reserve(Tiles.Num());
dtNavMesh* DetourNavMesh = NavMesh.GetRecastMesh();
if (DetourNavMesh != nullptr)
{
TSet<FIntPoint>* ActiveTiles = nullptr;
if (UE::NavMesh::Private::IsUsingActiveTileGeneration(NavMesh))
{
ActiveTiles = &NavMesh.GetActiveTileSet();
}
TArray<const dtMeshTile*> ExtraMeshTiles;
const bool bIsDynamic = NavMesh.SupportsRuntimeGeneration();
for (FRecastTileData& TileData : Tiles)
{
if (TileData.bAttached)
{
// Detach tile cache layer and take ownership over compressed data
dtTileRef TileRef = 0;
const dtMeshTile* MeshTile = DetourNavMesh->getTileAt(TileData.X, TileData.Y, TileData.Layer);
if (MeshTile)
{
TileRef = DetourNavMesh->getTileRef(MeshTile);
if (bTakeCacheDataOwnership)
{
FNavMeshTileData TileCacheData = NavMesh.GetRecastNavMeshImpl()->GetTileCacheLayer(TileData.X, TileData.Y, TileData.Layer);
if (TileCacheData.IsValid())
{
TileData.TileCacheDataSize = TileCacheData.DataSize;
TileData.TileCacheRawData->RawData = TileCacheData.Release();
}
}
NavMesh.LogRecastTile(ANSI_TO_TCHAR(__FUNCTION__), FName(" "), FName("removing"), *DetourNavMesh, TileData.X, TileData.Y, TileData.Layer, TileRef);
NavMesh.GetRecastNavMeshImpl()->RemoveTileCacheLayer(TileData.X, TileData.Y, TileData.Layer);
if (bTakeDataOwnership)
{
// Remove tile from navmesh and take ownership of tile raw data
DetourNavMesh->removeTile(TileRef, &TileData.TileRawData->RawData, &TileData.TileDataSize);
}
else
{
// In the editor we have a copy of tile data so just release tile in navmesh
DetourNavMesh->removeTile(TileRef, nullptr, nullptr);
}
if (ActiveTiles)
{
ActiveTiles->Remove(FIntPoint(TileData.X, TileData.Y));
}
Result.Add(FNavTileRef(TileRef));
}
if (bIsDynamic)
{
// Remove any tile remaining
const int32 MaxTiles = DetourNavMesh->getTileCountAt(TileData.X, TileData.Y);
if (MaxTiles > 0)
{
ExtraMeshTiles.SetNumZeroed(MaxTiles, EAllowShrinking::No);
const int32 MeshTilesCount = DetourNavMesh->getTilesAt(TileData.X, TileData.Y, ExtraMeshTiles.GetData(), MaxTiles);
for (int32 i = 0; i < MeshTilesCount; ++i)
{
const dtMeshTile* ExtraMeshTile = ExtraMeshTiles[i];
dtTileRef ExtraTileRef = DetourNavMesh->getTileRef(ExtraMeshTile);
if (ExtraTileRef)
{
DetourNavMesh->removeTile(ExtraTileRef, nullptr, nullptr);
Result.Add(FNavTileRef(ExtraTileRef));
}
}
}
}
}
TileData.bAttached = false;
TileData.X = 0;
TileData.Y = 0;
TileData.Layer = 0;
}
}
UE_LOG(LogNavigation, Verbose, TEXT("Detached %d tiles from NavMesh - %s"), Result.Num(), *NavigationDataName.ToString());
return Result;
}
#endif // WITH_RECAST
void URecastNavMeshDataChunk::MoveTiles(FPImplRecastNavMesh& NavMeshImpl, const FIntPoint& Offset, const FVector::FReal RotationDeg, const FVector2D& RotationCenter)
{
#if WITH_RECAST
UE_LOG(LogNavigation, Verbose, TEXT("%s Moving %i tiles on navmesh %s."), ANSI_TO_TCHAR(__FUNCTION__), Tiles.Num(), *NavigationDataName.ToString());
dtNavMesh* NavMesh = NavMeshImpl.DetourNavMesh;
if (NavMesh != nullptr)
{
for (FRecastTileData& TileData : Tiles)
{
if (TileData.TileCacheDataSize != 0)
{
UE_LOG(LogNavigation, Error, TEXT(" TileCacheRawData is expected to be empty. No support for moving the cache data yet."));
continue;
}
if ((TileData.bAttached == false) && TileData.TileRawData.IsValid())
{
const FVector RcRotationCenter = Unreal2RecastPoint(FVector(RotationCenter.X, RotationCenter.Y, 0.f));
const FVector::FReal TileWidth = NavMesh->getParams()->tileWidth;
const FVector::FReal TileHeight = NavMesh->getParams()->tileHeight;
const dtMeshHeader* Header = (dtMeshHeader*)TileData.TileRawData->RawData;
if (Header->version != DT_NAVMESH_VERSION)
{
continue;
}
// Apply rotation to tile coordinates
int DeltaX = 0;
int DeltaY = 0;
FBox TileBox(Recast2UnrealPoint(Header->bmin), Recast2UnrealPoint(Header->bmax));
FVector RcTileCenter = Unreal2RecastPoint(TileBox.GetCenter());
dtComputeTileOffsetFromRotation(&RcTileCenter.X, &RcRotationCenter.X, RotationDeg, TileWidth, TileHeight, DeltaX, DeltaY);
const int OffsetWithRotX = Offset.X + DeltaX;
const int OffsetWithRotY = Offset.Y + DeltaY;
const bool bSuccess = dtTransformTileData(TileData.TileRawData->RawData, TileData.TileDataSize, OffsetWithRotX, OffsetWithRotY, TileWidth, TileHeight, RotationDeg, NavMesh->getBVQuantFactor(Header->resolution));
UE_CLOG(bSuccess, LogNavigation, Verbose, TEXT(" Moved tile from (%i,%i) to (%i,%i)."), TileData.OriginalX, TileData.OriginalY, (TileData.OriginalX + OffsetWithRotX), (TileData.OriginalY + OffsetWithRotY));
}
}
}
UE_LOG(LogNavigation, Verbose, TEXT("%s Moving done."), ANSI_TO_TCHAR(__FUNCTION__));
#endif// WITH_RECAST
}
int32 URecastNavMeshDataChunk::GetNumTiles() const
{
return Tiles.Num();
}
void URecastNavMeshDataChunk::ReleaseTiles()
{
Tiles.Reset();
}
// Deprecated
void URecastNavMeshDataChunk::GetTiles(const FPImplRecastNavMesh* NavMeshImpl, const TArray<int32>& TileIndices, const EGatherTilesCopyMode CopyMode, const bool bMarkAsAttached /*= true*/)
{
Tiles.Empty(TileIndices.Num());
#if WITH_RECAST
if (NavMeshImpl)
{
TArray<uint32> TileUnsignedIndices;
TileUnsignedIndices.Append(TileIndices);
TArray<FNavTileRef> TileRefs;
FNavTileRef::DeprecatedMakeTileRefsFromTileIds(NavMeshImpl, TileUnsignedIndices, TileRefs);
GetTiles(NavMeshImpl, TileRefs, CopyMode, bMarkAsAttached);
}
#endif // WITH_RECAST
}
#if WITH_RECAST
void URecastNavMeshDataChunk::GetTiles(const FPImplRecastNavMesh* NavMeshImpl, const TArray<FNavTileRef>& TileRefs, const EGatherTilesCopyMode CopyMode, const bool bMarkAsAttached /*= true*/)
{
Tiles.Empty(TileRefs.Num());
const dtNavMesh* NavMesh = NavMeshImpl->DetourNavMesh;
for (const FNavTileRef TileRef : TileRefs)
{
const dtMeshTile* Tile = NavMesh->getTileByRef(static_cast<dtTileRef>(TileRef));
if (Tile && Tile->header)
{
// Make our own copy of tile data
uint8* RawTileData = nullptr;
if (CopyMode & EGatherTilesCopyMode::CopyData)
{
RawTileData = DuplicateRecastRawData(Tile->data, Tile->dataSize);
}
// We need tile cache data only if navmesh supports any kind of runtime generation
FNavMeshTileData TileCacheData;
uint8* RawTileCacheData = nullptr;
if (CopyMode & EGatherTilesCopyMode::CopyCacheData)
{
TileCacheData = NavMeshImpl->GetTileCacheLayer(Tile->header->x, Tile->header->y, Tile->header->layer);
if (TileCacheData.IsValid())
{
// Make our own copy of tile cache data
RawTileCacheData = DuplicateRecastRawData(TileCacheData.GetData(), TileCacheData.DataSize);
}
}
FRecastTileData RecastTileData(Tile->dataSize, RawTileData, TileCacheData.DataSize, RawTileCacheData);
RecastTileData.OriginalX = Tile->header->x;
RecastTileData.OriginalY = Tile->header->y;
RecastTileData.X = Tile->header->x;
RecastTileData.Y = Tile->header->y;
RecastTileData.Layer = Tile->header->layer;
RecastTileData.bAttached = bMarkAsAttached;
Tiles.Add(RecastTileData);
}
}
}
#endif // WITH_RECAST
// Deprecated
void URecastNavMeshDataChunk::GetTilesBounds(const FPImplRecastNavMesh& NavMeshImpl, const TArray<int32>& TileIndices, FBox& OutBounds) const
{
OutBounds.Init();
#if WITH_RECAST
TArray<uint32> TileUnsignedIndices;
TileUnsignedIndices.Append(TileIndices);
TArray<FNavTileRef> TileRefs;
FNavTileRef::DeprecatedMakeTileRefsFromTileIds(&NavMeshImpl, TileUnsignedIndices, TileRefs);
GetTilesBounds(NavMeshImpl, TileRefs, OutBounds);
#endif // WITH_RECAST
}
#if WITH_RECAST
void URecastNavMeshDataChunk::GetTilesBounds(const FPImplRecastNavMesh& NavMeshImpl, const TArray<FNavTileRef>& TileRefs, FBox& OutBounds) const
{
OutBounds.Init();
const dtNavMesh* NavMesh = NavMeshImpl.DetourNavMesh;
for (const FNavTileRef TileRef : TileRefs)
{
const dtMeshTile* Tile = NavMesh->getTileByRef(static_cast<dtTileRef>(TileRef));
if (Tile && Tile->header)
{
OutBounds += Recast2UnrealBox(Tile->header->bmin, Tile->header->bmax);
}
}
}
#endif // WITH_RECAST