Files
UnrealEngineUWP/Engine/Source/Runtime/NavigationSystem/Private/NavigationDirtyAreasController.cpp
Aris Theophanidis 5c4115f532 [WP Dynamic Navmesh] First pass on WP dynamic navmesh
- Dynamic navmesh in a world partitioned map is now allowed to build a base navmesh and stream cells from it
- Ignore navigation dirtiness coming from objects loading/unloading that are part of the base navmesh
- Addition of vlog boxes on addition and removal of navigation data chunk actors
#rb Yoan.StAmant
#jira UE-150793
#preflight 626fe108220f89f0ad3fdb14

[CL 20008063 by Aris Theophanidis in ue5-main branch]
2022-05-02 10:10:57 -04:00

168 lines
5.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "NavigationDirtyAreasController.h"
#include "NavigationData.h"
#include "VisualLogger/VisualLogger.h"
#include "AI/Navigation/NavigationDirtyElement.h"
DEFINE_LOG_CATEGORY(LogNavigationDirtyArea);
//----------------------------------------------------------------------//
// FNavigationDirtyAreasController
//----------------------------------------------------------------------//
FNavigationDirtyAreasController::FNavigationDirtyAreasController()
: bCanAccumulateDirtyAreas(true)
, bUseWorldPartitionedDynamicMode(false)
#if !UE_BUILD_SHIPPING
, bDirtyAreasReportedWhileAccumulationLocked(false)
, bCanReportOversizedDirtyArea(false)
, bNavigationBuildLocked(false)
#endif // !UE_BUILD_SHIPPING
{
}
void FNavigationDirtyAreasController::ForceRebuildOnNextTick()
{
float MinTimeForUpdate = (DirtyAreasUpdateFreq != 0.f ? (1.0f / DirtyAreasUpdateFreq) : 0.f);
DirtyAreasUpdateTime = FMath::Max(DirtyAreasUpdateTime, MinTimeForUpdate);
}
void FNavigationDirtyAreasController::Tick(const float DeltaSeconds, const TArray<ANavigationData*>& NavDataSet, bool bForceRebuilding)
{
DirtyAreasUpdateTime += DeltaSeconds;
const bool bCanRebuildNow = bForceRebuilding || (DirtyAreasUpdateFreq != 0.f && DirtyAreasUpdateTime >= (1.0f / DirtyAreasUpdateFreq));
if (DirtyAreas.Num() > 0 && bCanRebuildNow)
{
for (ANavigationData* NavData : NavDataSet)
{
if (NavData)
{
NavData->RebuildDirtyAreas(DirtyAreas);
}
}
DirtyAreasUpdateTime = 0.f;
DirtyAreas.Reset();
}
}
void FNavigationDirtyAreasController::AddArea(const FBox& NewArea, const int32 Flags, const TFunction<UObject*()>& ObjectProviderFunc /*= nullptr*/, const FNavigationDirtyElement* DirtyElement /*= nullptr*/)
{
#if !UE_BUILD_SHIPPING
// always keep track of reported areas even when filtered out by invalid area as long as flags are valid
bDirtyAreasReportedWhileAccumulationLocked = bDirtyAreasReportedWhileAccumulationLocked || (Flags > 0 && !bCanAccumulateDirtyAreas);
#endif // !UE_BUILD_SHIPPING
if (!NewArea.IsValid)
{
UE_LOG(LogNavigationDirtyArea, Warning, TEXT("Skipping dirty area creation because of invalid bounds (object: %s)"), *GetFullNameSafe(ObjectProviderFunc ? ObjectProviderFunc() : nullptr));
return;
}
const FVector2D BoundsSize(NewArea.GetSize());
if (BoundsSize.IsNearlyZero())
{
UE_LOG(LogNavigationDirtyArea, Warning, TEXT("Skipping dirty area creation because of empty bounds (object: %s)"), *GetFullNameSafe(ObjectProviderFunc ? ObjectProviderFunc() : nullptr));
return;
}
if (bUseWorldPartitionedDynamicMode)
{
// ObjectProviderFunc() is not always providing a valid object.
if (const bool bIsFromVisibilityChange = (DirtyElement && DirtyElement->bIsFromVisibilityChange) || FNavigationSystem::IsLevelVisibilityChanging(ObjectProviderFunc()))
{
// If the area is from the addition or removal of objects caused by level loading/unloading and it's already in the base navmesh ignore the dirtiness.
if (const bool bIsIncludedInBaseNavmesh = (DirtyElement && DirtyElement->bIsInBaseNavmesh) || FNavigationSystem::IsInBaseNavmesh(ObjectProviderFunc()))
{
UE_LOG(LogNavigationDirtyArea, VeryVerbose, TEXT("Ignoring dirtyness (visibility changed and in base navmesh). (object: %s)"), *GetFullNameSafe(ObjectProviderFunc ? ObjectProviderFunc() : nullptr));
return;
}
}
}
#if !UE_BUILD_SHIPPING
auto DumpExtraInfo = [ObjectProviderFunc, BoundsSize, NewArea]() {
const UObject* ObjectProvider = nullptr;
if (ObjectProviderFunc)
{
ObjectProvider = ObjectProviderFunc();
}
const UActorComponent* ObjectAsComponent = Cast<UActorComponent>(ObjectProvider);
const AActor* ComponentOwner = ObjectAsComponent ? ObjectAsComponent->GetOwner() : nullptr;
if (ComponentOwner)
{
UE_VLOG_BOX(ComponentOwner, LogNavigationDirtyArea, Log, NewArea, FColor::Red, TEXT(""));
}
return FString::Printf(TEXT("Adding dirty area object: %s | Potential component's owner: %s | Bounds size: %s)"), *GetFullNameSafe(ObjectProvider), *GetFullNameSafe(ComponentOwner), *BoundsSize.ToString());
};
UE_LOG(LogNavigationDirtyArea, VeryVerbose, TEXT("%s"), *DumpExtraInfo());
if (ShouldReportOversizedDirtyArea() && BoundsSize.GetMax() > DirtyAreaWarningSizeThreshold)
{
UE_LOG(LogNavigationDirtyArea, Warning, TEXT("Adding an oversized dirty area (object:%s size:%s threshold:%.2f)"),
*GetFullNameSafe(ObjectProviderFunc ? ObjectProviderFunc() : nullptr),
*BoundsSize.ToString(),
DirtyAreaWarningSizeThreshold);
}
#endif // !UE_BUILD_SHIPPING
if (Flags > 0 && bCanAccumulateDirtyAreas)
{
DirtyAreas.Add(FNavigationDirtyArea(NewArea, Flags, ObjectProviderFunc ? ObjectProviderFunc() : nullptr));
}
}
void FNavigationDirtyAreasController::OnNavigationBuildLocked()
{
#if !UE_BUILD_SHIPPING
bNavigationBuildLocked = true;
#endif // !UE_BUILD_SHIPPING
}
void FNavigationDirtyAreasController::OnNavigationBuildUnlocked()
{
#if !UE_BUILD_SHIPPING
bNavigationBuildLocked = false;
#endif // !UE_BUILD_SHIPPING
}
void FNavigationDirtyAreasController::SetDirtyAreaWarningSizeThreshold(const float Threshold)
{
#if !UE_BUILD_SHIPPING
DirtyAreaWarningSizeThreshold = Threshold;
#endif // !UE_BUILD_SHIPPING
}
void FNavigationDirtyAreasController::SetUseWorldPartitionedDynamicMode(bool bIsWPDynamic)
{
bUseWorldPartitionedDynamicMode = bIsWPDynamic;
}
void FNavigationDirtyAreasController::SetCanReportOversizedDirtyArea(bool bCanReport)
{
#if !UE_BUILD_SHIPPING
bCanReportOversizedDirtyArea = bCanReport;
#endif // !UE_BUILD_SHIPPING
}
#if !UE_BUILD_SHIPPING
bool FNavigationDirtyAreasController::ShouldReportOversizedDirtyArea() const
{
return bNavigationBuildLocked == false && bCanReportOversizedDirtyArea && DirtyAreaWarningSizeThreshold >= 0.0f;
}
#endif // !UE_BUILD_SHIPPING
void FNavigationDirtyAreasController::Reset()
{
// discard all pending dirty areas, we are going to rebuild navmesh anyway
DirtyAreas.Reset();
#if !UE_BUILD_SHIPPING
bDirtyAreasReportedWhileAccumulationLocked = false;
#endif // !UE_BUILD_SHIPPING
}