Files
UnrealEngineUWP/Engine/Source/Developer/GameplayDebugger/Private/GameplayDebuggingComponent.cpp
Robert Manuszewski 97cc1e1be1 Copying //UE4/Dev-Core to //UE4/Main
==========================
MAJOR FEATURES + CHANGES
==========================

Change 2799478 on 2015/12/11 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Added Dev Stream custom versions.

	- Each stream now has its own custom version
	- Developers working in a stream should only modify their respective version

Change 2789867 on 2015/12/04 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	UnrealHeaderTool plugins no longer need to be a separate plugin and don't require UnrealHeaderTool source code changes to work.

	- Merged ScriptGeneratorPlugin with ScriptPlugin
	- Introduced the concept of UHT plugin support for plugins so that UHT's source files don't need to be modified to make it work with external plugins
	- Added RuntimeNoProgram module type (module that can be used at runtime but not by program targets).
	- Fixed logic with project file path setting in the engine. It will no longer try to crate a full path from an already rooted path.

Change 2796114 on 2015/12/09 by Steve.Robb@Dev-Core

	TEnumRange - enabled ranged-based for iteration over enum values.

Change 2789843 on 2015/12/04 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	More thorough way of verifying GC cluster assumptions.

Change 2794221 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	Don't merge GC clusters by default

Change 2797824 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Added the option to load all symbols for stack walking in non-monolithic builds.

Change 2790539 on 2015/12/04 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	Stats/Profiler - Better handling for live connection, using the same path as file profiles, added FStatsLoadedState to separate runtime stats state from the loaded one

Change 2794183 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Always reset events when returning them to pool.

Change 2794406 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	Fixing -unversioned flag being completely ignored when cooking

Change 2794563 on 2015/12/08 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream2

	Making sure string referenced assets don't get cooked if referenced by editor-only properties.

Change 2795124 on 2015/12/08 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	Profiler - Fixed bad data in min/max/avg inclusive times, added min/max/avg num calls, fixed event graph tooltip not displaying correct values

Change 2796208 on 2015/12/09 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	Profiler - Remove support for multiple instances in the profiler (game thread is always visible, a few crash fixes, cleaned code a bit)

Change 2797658 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Allocation verification helpers. Helps with tracking down memory stomps, freeing the same pointers multiple times.

Change 2797699 on 2015/12/10 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Fix incorrect asset loading in Cooked game data (by bozaro)

	PR #1844

Change 2798173 on 2015/12/10 by Steve.Robb@Dev-Core

	Migration of Fortnite to use engine's TEnumRange.

Change 2798217 on 2015/12/10 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	PR #1331
	[Core] Added a stomp allocator that allows finding memory overruns, underruns, and read/write after free. (Contributed by Pablo Zurita)

Change 2799605 on 2015/12/11 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Fixing a crash when cancelling async loading caused by detaching linker from objects that had RF_NeedLoad flag set.

Change 2799849 on 2015/12/11 by Steve.Robb@Dev-Core

	Migration of Ocean to use engine's TEnumRange.

Change 2803144 on 2015/12/15 by Robert.Manuszewski@Robert.Manuszewski_NCL_Stream1

	Changed export tagging archive to also serialize class default objects using the normal Serialize path so that it can collect all custom versions used by exports.

Change 2803206 on 2015/12/15 by Jaroslaw.Surowiec@Stream.1.JarekSurowiec

	#jira UE-24177
	Audit ôshippingö defines in engine

Change 2804868 on 2015/12/16 by Steve.Robb@Dev-Core

	Removal of stats from MallocBinned2, to be readded later.

#lockdown Nick.Penwarden

[CL 2805158 by Robert Manuszewski in Main branch]
2015-12-16 11:52:36 -05:00

1621 lines
50 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "GameplayDebuggerPrivate.h"
#include "Net/UnrealNetwork.h"
#include "DebugRenderSceneProxy.h"
#include "AI/Navigation/RecastNavMeshGenerator.h"
#include "NavMeshRenderingHelpers.h"
#include "AI/Navigation/NavigationSystem.h"
#include "EnvironmentQuery/EnvQueryTypes.h"
#include "EnvironmentQuery/EnvQueryManager.h"
#include "EnvironmentQuery/EQSRenderingComponent.h"
#include "EnvironmentQuery/EnvQueryTest.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "AbilitySystemComponent.h"
#include "AIController.h"
#include "BrainComponent.h"
#include "BehaviorTreeDelegates.h"
#include "GameFramework/PlayerState.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Engine/Channel.h"
#include "Animation/AnimMontage.h"
#include "GameplayAbilitiesModule.h"
#include "Perception/AIPerceptionComponent.h"
#if WITH_EDITOR
#include "Editor/EditorEngine.h"
#include "GeomTools.h"
#endif // WITH_EDITOR
#include "GameplayDebuggingComponent.h"
//----------------------------------------------------------------------//
// Composite Scene proxy
//----------------------------------------------------------------------//
class FDebugRenderSceneCompositeProxy : public FDebugRenderSceneProxy
{
public:
FDebugRenderSceneCompositeProxy(const UPrimitiveComponent* InComponent)
: FDebugRenderSceneProxy(InComponent)
{
}
virtual ~FDebugRenderSceneCompositeProxy()
{
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
delete ChildProxies[Index];
}
}
virtual void DrawStaticElements(FStaticPrimitiveDrawInterface* PDI) override
{
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
ChildProxies[Index]->DrawStaticElements(PDI);
}
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
ChildProxies[Index]->GetDynamicMeshElements(Views, ViewFamily, VisibilityMap, Collector);
}
}
virtual void RegisterDebugDrawDelgate() override
{
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
ChildProxies[Index]->RegisterDebugDrawDelgate();
}
}
virtual void UnregisterDebugDrawDelgate() override
{
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
ChildProxies[Index]->UnregisterDebugDrawDelgate();
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
Result |= ChildProxies[Index]->GetViewRelevance(View);
}
return Result;
}
virtual uint32 GetMemoryFootprint( void ) const override
{
return sizeof( *this ) + GetAllocatedSize();
}
uint32 GetAllocatedSize( void ) const
{
uint32 Size = 0;
for (int32 Index = 0; Index < ChildProxies.Num(); ++Index)
{
ChildProxies[Index]->GetMemoryFootprint();
}
return Size + ChildProxies.GetAllocatedSize();
}
void AddChild(FDebugRenderSceneProxy* NewChild)
{
ChildProxies.AddUnique(NewChild);
}
protected:
TArray<FDebugRenderSceneProxy*> ChildProxies;
};
//----------------------------------------------------------------------//
// UGameplayDebuggingComponent
//----------------------------------------------------------------------//
FName UGameplayDebuggingComponent::DefaultComponentName = TEXT("GameplayDebuggingComponent");
FOnDebuggingTargetChanged UGameplayDebuggingComponent::OnDebuggingTargetChangedDelegate;
UGameplayDebuggingComponent::UGameplayDebuggingComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, bDrawEQSLabels(true)
, bDrawEQSFailedItems(true)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
DebugComponentClassName = TEXT("/Script/GameplayDebugger.GameplayDebuggingComponent");
PrimaryComponentTick.bCanEverTick = true;
bWantsInitializeComponent = true;
bAutoActivate = false;
PrimaryComponentTick.bStartWithTickEnabled = false;
LastStoredPathTimeStamp = -1.f;
ShowExtendedInformatiomCounter = 0;
#if WITH_EDITOR
bWasSelectedInEditor = false;
#endif
bHiddenInGame = false;
bEnabledTargetSelection = false;
bEnableClientEQSSceneProxy = false;
NextTargrtSelectionTime = 0;
ReplicateViewDataCounters.Init(0, EAIDebugDrawDataView::MAX);
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
if (Replicator)
{
Replicator->OnCycleDetailsView.AddUObject(this, &UGameplayDebuggingComponent::OnCycleDetailsView);
FGameplayDebuggerSettings Settings = GameplayDebuggerSettings(Replicator);
for (EAIDebugDrawDataView::Type View : TEnumRange<EAIDebugDrawDataView::Type>())
{
ReplicateViewDataCounters[View] = Settings.CheckFlag(View);
}
}
#endif
}
void UGameplayDebuggingComponent::Activate(bool bReset)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
Super::Activate(bReset);
SetComponentTickEnabled(true);
SetIsReplicated(true);
ActivationCounter++;
#else
Super::Activate(bReset);
#endif
}
void UGameplayDebuggingComponent::Deactivate()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
Super::Deactivate();
SetComponentTickEnabled(false);
SetIsReplicated(false);
#else
Super::Deactivate();
#endif
}
void UGameplayDebuggingComponent::OnRep_ActivationCounter()
{
// make sure that tick function is running, can't select actor to debug without it
// if first activation (UActorComponent) comes before tick function is registered, state will be overridden by bStartWithTickEnabled (false)
SetComponentTickEnabled(true);
}
void UGameplayDebuggingComponent::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
Super::GetLifetimeReplicatedProps( OutLifetimeProps );
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
DOREPLIFETIME(UGameplayDebuggingComponent, ReplicateViewDataCounters);
DOREPLIFETIME(UGameplayDebuggingComponent, ShowExtendedInformatiomCounter);
DOREPLIFETIME(UGameplayDebuggingComponent, ControllerName)
DOREPLIFETIME(UGameplayDebuggingComponent, PawnName);
DOREPLIFETIME(UGameplayDebuggingComponent, DebugIcon);
DOREPLIFETIME(UGameplayDebuggingComponent, PawnClass);
DOREPLIFETIME(UGameplayDebuggingComponent, ActivationCounter);
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingCharacter);
DOREPLIFETIME(UGameplayDebuggingComponent, MovementBaseInfo);
DOREPLIFETIME(UGameplayDebuggingComponent, MovementModeInfo);
DOREPLIFETIME(UGameplayDebuggingComponent, MontageInfo);
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingPathFollowing);
DOREPLIFETIME(UGameplayDebuggingComponent, PathFollowingInfo);
DOREPLIFETIME(UGameplayDebuggingComponent, NavDataInfo);
DOREPLIFETIME(UGameplayDebuggingComponent, PathPoints);
DOREPLIFETIME(UGameplayDebuggingComponent, PathCorridorData);
DOREPLIFETIME(UGameplayDebuggingComponent, NextPathPointIndex);
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingBehaviorTree);
DOREPLIFETIME(UGameplayDebuggingComponent, CurrentAITask);
DOREPLIFETIME(UGameplayDebuggingComponent, CurrentAIState);
DOREPLIFETIME(UGameplayDebuggingComponent, CurrentAIAssets);
DOREPLIFETIME(UGameplayDebuggingComponent, GameplayTasksState);
DOREPLIFETIME(UGameplayDebuggingComponent, bIsUsingAbilities);
DOREPLIFETIME(UGameplayDebuggingComponent, AbilityInfo);
DOREPLIFETIME(UGameplayDebuggingComponent, BrainComponentName);
DOREPLIFETIME(UGameplayDebuggingComponent, BrainComponentString);
DOREPLIFETIME(UGameplayDebuggingComponent, BlackboardRepData);
DOREPLIFETIME(UGameplayDebuggingComponent, NavmeshRepData);
DOREPLIFETIME(UGameplayDebuggingComponent, TargetActor);
DOREPLIFETIME(UGameplayDebuggingComponent, EQSRepData);
DOREPLIFETIME(UGameplayDebuggingComponent, SensingComponentLocation);
DOREPLIFETIME(UGameplayDebuggingComponent, PerceptionLegend)
DOREPLIFETIME(UGameplayDebuggingComponent, DistanceFromPlayer);
DOREPLIFETIME(UGameplayDebuggingComponent, DistanceFromSensor);
DOREPLIFETIME(UGameplayDebuggingComponent, SensingComponentLocation);
DOREPLIFETIME(UGameplayDebuggingComponent, PerceptionShapeElements);
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
bool UGameplayDebuggingComponent::ClientEnableTargetSelection_Validate(bool bEnable)
{
return true;
}
void UGameplayDebuggingComponent::ClientEnableTargetSelection_Implementation(bool bEnable)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
bEnabledTargetSelection = bEnable;
if (bEnabledTargetSelection && World && World->GetNetMode() != NM_DedicatedServer)
{
NextTargrtSelectionTime = 0;
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::SetActorToDebug(AActor* Actor)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (TargetActor != Actor)
{
MarkRenderStateDirty();
}
TargetActor = Actor;
EQSLocalData.Reset();
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (World && World->GetNetMode() != NM_DedicatedServer)
{
if (bEnabledTargetSelection)
{
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
if (Replicator)
{
if (Replicator->GetLocalPlayerOwner())
{
SelectTargetToDebug();
}
}
}
}
if (World && World->GetNetMode() < NM_Client)
{
CollectDataToReplicate(true);
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::SelectTargetToDebug()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
UGameplayDebuggingControllerComponent* GDC = Replicator->FindComponentByClass<UGameplayDebuggingControllerComponent>();
APlayerController* MyPC = GDC && GDC->GetDebugCameraController().IsValid() ? GDC->GetDebugCameraController().Get() : Replicator->GetLocalPlayerOwner();
if (MyPC )
{
float bestAim = 0;
FVector CamLocation;
FRotator CamRotation;
check(MyPC->PlayerCameraManager != NULL);
MyPC->PlayerCameraManager->GetCameraViewPoint(CamLocation, CamRotation);
FVector FireDir = CamRotation.Vector();
UWorld* World = MyPC->GetWorld();
check( World );
APawn* PossibleTarget = NULL;
for (FConstPawnIterator Iterator = World->GetPawnIterator(); Iterator; ++Iterator )
{
APawn* NewTarget = *Iterator;
if (NewTarget == NULL || NewTarget == MyPC->GetPawn()
|| (NewTarget->PlayerState != NULL && NewTarget->PlayerState->bIsABot == false)
|| NewTarget->GetActorEnableCollision() == false)
{
continue;
}
if (NewTarget != MyPC->GetPawn())
{
// look for best controlled pawn target
const FVector AimDir = NewTarget->GetActorLocation() - CamLocation;
float FireDist = AimDir.SizeSquared();
// only find targets which are < 25000 units away
if (FireDist < 625000000.f)
{
FireDist = FMath::Sqrt(FireDist);
float newAim = FVector::DotProduct(FireDir, AimDir);
newAim = newAim / FireDist;
if (newAim > bestAim)
{
PossibleTarget = NewTarget;
bestAim = newAim;
}
}
}
}
if (PossibleTarget != NULL && PossibleTarget != GetSelectedActor())
{
if (AGameplayDebuggingReplicator* DebuggingReplicator = Cast<AGameplayDebuggingReplicator>(GetOwner()))
{
DebuggingReplicator->ServerSetActorToDebug(Cast<AActor>(PossibleTarget));
MarkRenderStateDirty();
}
ServerReplicateData(EDebugComponentMessage::ActivateReplication, EAIDebugDrawDataView::Empty);
}
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectDataToReplicate(bool bCollectExtendedData)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
const AActor *SelectedActor = GetSelectedActor();
if (!SelectedActor || SelectedActor->IsPendingKill())
{
return;
}
if (ShouldReplicateData(EAIDebugDrawDataView::Basic) || ShouldReplicateData(EAIDebugDrawDataView::OverHead))
{
CollectBasicData();
}
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
const bool bDrawFullData = Replicator->GetSelectedActorToDebug() == GetSelectedActor();
if (bDrawFullData && ShouldReplicateData(EAIDebugDrawDataView::Basic))
{
CollectPathData();
}
if (bCollectExtendedData && bDrawFullData)
{
if (ShouldReplicateData(EAIDebugDrawDataView::BehaviorTree))
{
CollectBehaviorTreeData();
}
#if WITH_EQS
if (ShouldReplicateData(EAIDebugDrawDataView::EQS))
{
bool bEnabledEnvironmentQueryEd = true;
if (GConfig)
{
GConfig->GetBool(TEXT("EnvironmentQueryEd"), TEXT("EnableEnvironmentQueryEd"), bEnabledEnvironmentQueryEd, GEngineIni);
}
if (bEnabledEnvironmentQueryEd)
{
CollectEQSData();
}
}
#endif // WITH_EQS
CollectPerceptionData();
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicData()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
PawnName = MyPawn->GetHumanReadableName();
PawnClass = MyPawn->GetClass()->GetName();
AAIController* MyController = Cast<AAIController>(MyPawn->Controller);
bIsUsingCharacter = MyPawn->IsA(ACharacter::StaticClass());
if (MyController)
{
if (MyController->IsPendingKill() == false)
{
ControllerName = MyController->GetName();
DebugIcon = MyController->GetDebugIcon();
CollectBasicMovementData(MyPawn);
CollectBasicPathData(MyPawn);
CollectBasicBehaviorData(MyPawn);
CollectBasicAbilityData(MyPawn);
CollectBasicAnimationData(MyPawn);
}
else
{
ControllerName = TEXT("Controller PENDING KILL");
}
}
else
{
ControllerName = TEXT("No Controller");
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicMovementData(APawn* MyPawn)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UCharacterMovementComponent* CharMovement = Cast<UCharacterMovementComponent>(MyPawn->GetMovementComponent());
if (CharMovement)
{
UPrimitiveComponent* FloorComponent = MyPawn->GetMovementBase();
AActor* FloorActor = FloorComponent ? FloorComponent->GetOwner() : nullptr;
MovementBaseInfo = FloorComponent ? FString::Printf(TEXT("%s.%s"), *GetNameSafe(FloorActor), *FloorComponent->GetName()) : TEXT("None");
MovementModeInfo = CharMovement->GetMovementName();
}
else
{
MovementBaseInfo = TEXT("");
MovementModeInfo = TEXT("");
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicPathData(APawn* MyPawn)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(MyPawn->GetWorld());
AAIController* MyAIController = Cast<AAIController>(MyPawn->GetController());
const ANavigationData* NavData = NavSys ? NavSys->GetNavDataForProps(MyAIController->GetNavAgentPropertiesRef()) : nullptr;
if (NavData)
{
NavDataInfo = NavData->GetConfig().Name.ToString();
}
UPathFollowingComponent* PFC = MyAIController->GetPathFollowingComponent();
bIsUsingPathFollowing = (PFC != nullptr);
if (PFC)
{
TArray<FString> Tokens;
TArray<EPathFollowingDebugTokens::Type> Flags;
if (PFC)
{
PFC->GetDebugStringTokens(Tokens, Flags);
}
PathFollowingInfo = FString();
for (int32 Idx = 0; Idx < Tokens.Num(); Idx++)
{
switch (Flags[Idx])
{
case EPathFollowingDebugTokens::Description:
PathFollowingInfo += Tokens[Idx];
break;
case EPathFollowingDebugTokens::ParamName:
PathFollowingInfo += TEXT(", {yellow}");
PathFollowingInfo += Tokens[Idx];
PathFollowingInfo += TEXT(":");
break;
case EPathFollowingDebugTokens::PassedValue:
PathFollowingInfo += TEXT("{yellow}");
PathFollowingInfo += Tokens[Idx];
break;
case EPathFollowingDebugTokens::FailedValue:
PathFollowingInfo += TEXT("{orange}");
PathFollowingInfo += Tokens[Idx];
break;
default:
break;
}
}
}
else
{
PathFollowingInfo = TEXT("");
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicBehaviorData(APawn* MyPawn)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && ENABLE_VISUAL_LOG
AAIController* MyAIController = Cast<AAIController>(MyPawn->GetController());
UBehaviorTreeComponent* BTC = MyAIController ? Cast<UBehaviorTreeComponent>(MyAIController->BrainComponent) : nullptr;
bIsUsingBehaviorTree = (BTC != nullptr);
if (BTC)
{
CurrentAITask = BTC->DescribeActiveTasks();
CurrentAIState = BTC->IsRunning() ? TEXT("Running") : BTC->IsPaused() ? TEXT("Paused") : TEXT("Inactive");
CurrentAIAssets = BTC->DescribeActiveTrees();
}
else
{
CurrentAITask = TEXT("");
CurrentAIState = TEXT("");
CurrentAIAssets = TEXT("");
}
UGameplayTasksComponent* GTComponent = MyPawn->FindComponentByClass<UGameplayTasksComponent>();
if (GTComponent)
{
GameplayTasksState = FString::Printf(TEXT("Ticking Tasks: %s\nTask Queue: %s"), *GTComponent->GetTickingTasksDescription(), *GTComponent->GetTasksPriorityQueueDescription());
}
else
{
GameplayTasksState = TEXT("");
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicAbilityData(APawn* MyPawn)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (IGameplayAbilitiesModule::IsAvailable())
{
bool bUsingAbilities;
IGameplayAbilitiesModule& AbilitiesModule = FModuleManager::LoadModuleChecked<IGameplayAbilitiesModule>("GameplayAbilities");
AbilitiesModule.GetActiveAbilitiesDebugDataForActor(MyPawn, AbilityInfo, bUsingAbilities);
bIsUsingAbilities = bUsingAbilities;
}
else
{
bIsUsingAbilities = false;
AbilityInfo = TEXT("None");
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicAnimationData(APawn* MyPawn)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
ACharacter* MyChar = Cast<ACharacter>(MyPawn);
MontageInfo = MyChar ? GetNameSafe(MyChar->GetCurrentMontage()) : TEXT("");
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBehaviorTreeData()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (!ShouldReplicateData(EAIDebugDrawDataView::BehaviorTree))
{
return;
}
BrainComponentName = TEXT("");
BrainComponentString = TEXT("");
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
AAIController* MyController = Cast<AAIController>(MyPawn->Controller);
if (MyController != NULL && MyController->BrainComponent != NULL && MyController->IsPendingKillPending() == false)
{
BrainComponentName = MyController->BrainComponent != NULL ? MyController->BrainComponent->GetName() : TEXT("");
BrainComponentString = MyController->BrainComponent != NULL ? MyController->BrainComponent->GetDebugInfoString() : TEXT("");
BlackboardString = MyController->BrainComponent->GetBlackboardComponent() ? MyController->BrainComponent->GetBlackboardComponent()->GetDebugInfoString(EBlackboardDescription::KeyWithValue) : TEXT("");
if (World && World->GetNetMode() != NM_Standalone)
{
TArray<uint8> UncompressedBuffer;
FMemoryWriter ArWriter(UncompressedBuffer);
ArWriter << BlackboardString;
const int32 HeaderSize = sizeof(int32);
BlackboardRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedBuffer.Num()));
const int32 UncompressedSize = UncompressedBuffer.Num();
int32 CompressedSize = BlackboardRepData.Num() - HeaderSize;
uint8* DestBuffer = BlackboardRepData.GetData();
FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
DestBuffer += HeaderSize;
FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory),
(void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);
BlackboardRepData.SetNum(CompressedSize + HeaderSize, false);
}
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::OnRep_UpdateBlackboard()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (World && World->GetNetMode() != NM_Standalone)
{
TArray<uint8> UncompressedBuffer;
int32 UncompressedSize = 0;
const int32 HeaderSize = sizeof(int32);
uint8* SrcBuffer = (uint8*)BlackboardRepData.GetData();
FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
SrcBuffer += HeaderSize;
const int32 CompressedSize = BlackboardRepData.Num() - HeaderSize;
UncompressedBuffer.AddZeroed(UncompressedSize);
FCompression::UncompressMemory((ECompressionFlags)(COMPRESS_ZLIB), (void*)UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
FMemoryReader ArReader(UncompressedBuffer);
ArReader << BlackboardString;
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectPathData()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) && ENABLE_VISUAL_LOG
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
bool bRefreshRendering = false;
if (AAIController *MyController = Cast<AAIController>(MyPawn->Controller))
{
if (MyController->PathFollowingComponent && MyController->PathFollowingComponent->GetPath().IsValid())
{
NextPathPointIndex = MyController->PathFollowingComponent->GetNextPathIndex();
const FNavPathSharedPtr& NewPath = MyController->PathFollowingComponent->GetPath();
if (CurrentPath.HasSameObject(NewPath.Get()) == false || NewPath->GetTimeStamp() > LastStoredPathTimeStamp)
{
LastStoredPathTimeStamp = NewPath->GetTimeStamp();
FVisualLogEntry Snapshot;
NewPath->DescribeSelfToVisLog(&Snapshot);
PathCorridorPolygons.Reset(Snapshot.ElementsToDraw.Num());
FPathCorridorPolygons Polygon;
for (FVisualLogShapeElement& CurrentShape : Snapshot.ElementsToDraw)
{
if (CurrentShape.GetType() == EVisualLoggerShapeElement::Polygon)
{
Polygon.Color = CurrentShape.GetFColor();
Polygon.Points.Reset();
Polygon.Points.Append(CurrentShape.Points);
PathCorridorPolygons.Add(Polygon);
}
}
PathPoints.Reset();
for (int32 Index=0; Index < NewPath->GetPathPoints().Num(); ++Index)
{
PathPoints.Add(NewPath->GetPathPoints()[Index].Location);
}
CurrentPath = NewPath;
if (PathCorridorPolygons.Num() && World && World->GetNetMode() != NM_Client)
{
PathCorridorData.Reset();
TArray<uint8> HelpBuffer;
FMemoryWriter ArWriter(HelpBuffer);
int32 NumPolygons = PathCorridorPolygons.Num();
ArWriter << NumPolygons;
for (const FPathCorridorPolygons& CurrentPoly : PathCorridorPolygons)
{
FColor Color = CurrentPoly.Color;
ArWriter << Color;
int32 NumVerts = CurrentPoly.Points.Num();
ArWriter << NumVerts;
for (auto Vert : CurrentPoly.Points)
{
ArWriter << Vert;
}
}
PathCorridorData = HelpBuffer;
}
bRefreshRendering = true;
}
}
else
{
NextPathPointIndex = INDEX_NONE;
CurrentPath = NULL;
PathPoints.Reset();
PathCorridorPolygons.Reset();
}
}
if (bRefreshRendering && World && World->GetNetMode() != NM_DedicatedServer)
{
UpdateBounds();
MarkRenderStateDirty();
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::OnRep_PathCorridorData()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (World && World->GetNetMode() != NM_DedicatedServer)
{
FMemoryReader ArReader(PathCorridorData);
int32 NumPolygons = 0;
ArReader << NumPolygons;
PathCorridorPolygons.Reset(NumPolygons);
for (int32 PolyIndex = 0; PolyIndex < NumPolygons; ++PolyIndex)
{
FPathCorridorPolygons Polygon;
FColor Color;
ArReader << Polygon.Color;
int32 NumVerts = 0;
ArReader << NumVerts;
for (int32 VertIndex = 0; VertIndex < NumVerts; ++VertIndex)
{
FVector CurrentVertex;
ArReader << CurrentVertex;
Polygon.Points.Add(CurrentVertex);
}
if (NumVerts > 2)
{
PathCorridorPolygons.Add(Polygon);
}
}
UpdateBounds();
MarkRenderStateDirty();
}
#endif
}
void UGameplayDebuggingComponent::EnableDebugDraw(bool bEnable, bool InFocusedComponent)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
#if WITH_EQS
EnableClientEQSSceneProxy(bEnable);
#endif // WITH_EQS
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::ServerReplicateData(uint32 InMessage, uint32 InDataView)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
EDebugComponentMessage::Type Message = (EDebugComponentMessage::Type)InMessage;
EAIDebugDrawDataView::Type DataView = (EAIDebugDrawDataView::Type) InDataView;
switch (Message)
{
case EDebugComponentMessage::EnableExtendedView:
ShowExtendedInformatiomCounter++;
break;
case EDebugComponentMessage::DisableExtendedView:
ShowExtendedInformatiomCounter = FMath::Max(0, ShowExtendedInformatiomCounter - 1);
break;
case EDebugComponentMessage::ActivateReplication:
Activate();
break;
case EDebugComponentMessage::DeactivateReplilcation:
Deactivate();
break;
case EDebugComponentMessage::ActivateDataView:
if (ReplicateViewDataCounters.IsValidIndex(DataView))
{
ReplicateViewDataCounters[DataView] += 1;
}
break;
case EDebugComponentMessage::DeactivateDataView:
if (ReplicateViewDataCounters.IsValidIndex(DataView))
{
ReplicateViewDataCounters[DataView] = FMath::Max(0, ReplicateViewDataCounters[DataView] - 1);
}
break;
default:
break;
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
//////////////////////////////////////////////////////////////////////////
// EQS Data
//////////////////////////////////////////////////////////////////////////
void UGameplayDebuggingComponent::OnCycleDetailsView()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
if (++CurrentEQSIndex >= EQSLocalData.Num())
{
CurrentEQSIndex = 0;
}
UpdateBounds();
MarkRenderStateDirty();
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
const FEnvQueryResult* UGameplayDebuggingComponent::GetQueryResult() const
{
return CachedQueryInstance.Get();
}
const FEnvQueryInstance* UGameplayDebuggingComponent::GetQueryInstance() const
{
return CachedQueryInstance.Get();
}
void UGameplayDebuggingComponent::OnRep_UpdateEQS()
{
#if USE_EQS_DEBUGGER
// decode scoring data
if (World && World->GetNetMode() == NM_Client)
{
TArray<uint8> UncompressedBuffer;
int32 UncompressedSize = 0;
const int32 HeaderSize = sizeof(int32);
uint8* SrcBuffer = (uint8*)EQSRepData.GetData();
FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
SrcBuffer += HeaderSize;
const int32 CompressedSize = EQSRepData.Num() - HeaderSize;
UncompressedBuffer.AddZeroed(UncompressedSize);
FCompression::UncompressMemory((ECompressionFlags)(COMPRESS_ZLIB), (void*)UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
FMemoryReader ArReader(UncompressedBuffer);
ArReader << EQSLocalData;
}
UpdateBounds();
MarkRenderStateDirty();
#endif //USE_EQS_DEBUGGER
}
void UGameplayDebuggingComponent::CollectEQSData()
{
#if USE_EQS_DEBUGGER
if (!ShouldReplicateData(EAIDebugDrawDataView::EQS))
{
return;
}
UWorld* World = GetWorld();
UEnvQueryManager* QueryManager = World ? UEnvQueryManager::GetCurrent(World) : NULL;
const AActor* Owner = GetSelectedActor();
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
if (QueryManager == NULL || Owner == NULL)
{
return;
}
auto AllQueries = QueryManager->GetDebugger().GetAllQueriesForOwner(Owner);
const class APawn* OwnerAsPawn = Cast<class APawn>(Owner);
if (OwnerAsPawn != NULL && OwnerAsPawn->GetController())
{
const auto& AllControllerQueries = QueryManager->GetDebugger().GetAllQueriesForOwner(OwnerAsPawn->GetController());
AllQueries.Append(AllControllerQueries);
}
struct FEnvQueryInfoSort
{
FORCEINLINE bool operator()(const FEQSDebugger::FEnvQueryInfo& A, const FEQSDebugger::FEnvQueryInfo& B) const
{
return (A.Timestamp < B.Timestamp);
}
};
TArray<FEQSDebugger::FEnvQueryInfo> QueriesToSort = AllQueries;
QueriesToSort.Sort(FEnvQueryInfoSort()); //sort queries by timestamp
QueriesToSort.SetNum(FMath::Min<int32>(Replicator->MaxEQSQueries, AllQueries.Num()));
for (int32 Index = AllQueries.Num() - 1; Index >= 0; --Index)
{
auto &CurrentQuery = AllQueries[Index];
if (QueriesToSort.Find(CurrentQuery) == INDEX_NONE)
{
AllQueries.RemoveAt(Index);
}
}
EQSLocalData.Reset();
for (int32 Index = 0; Index < FMath::Min<int32>(Replicator->MaxEQSQueries, AllQueries.Num()); ++Index)
{
EQSDebug::FQueryData* CurrentLocalData = NULL;
CachedQueryInstance = AllQueries[Index].Instance;
const float CachedTimestamp = AllQueries[Index].Timestamp;
if (!CurrentLocalData)
{
EQSLocalData.AddZeroed();
CurrentLocalData = &EQSLocalData[EQSLocalData.Num()-1];
}
if (CachedQueryInstance.IsValid())
{
UEnvQueryDebugHelpers::QueryToDebugData(*CachedQueryInstance, *CurrentLocalData);
}
CurrentLocalData->Timestamp = AllQueries[Index].Timestamp;
}
TArray<uint8> UncompressedBuffer;
FMemoryWriter ArWriter(UncompressedBuffer);
ArWriter << EQSLocalData;
const int32 UncompressedSize = UncompressedBuffer.Num();
const int32 HeaderSize = sizeof(int32);
EQSRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedSize));
int32 CompressedSize = EQSRepData.Num() - HeaderSize;
uint8* DestBuffer = EQSRepData.GetData();
FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
DestBuffer += HeaderSize;
FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory), (void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);
EQSRepData.SetNum(CompressedSize + HeaderSize, false);
if (World && World->GetNetMode() != NM_DedicatedServer)
{
OnRep_UpdateEQS();
}
#endif
}
//----------------------------------------------------------------------//
// NavMesh rendering
//----------------------------------------------------------------------//
void UGameplayDebuggingComponent::OnRep_UpdateNavmesh()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
NavMeshBounds = FBox(FVector(-HALF_WORLD_MAX1, -HALF_WORLD_MAX1, -HALF_WORLD_MAX1), FVector(HALF_WORLD_MAX1, HALF_WORLD_MAX1, HALF_WORLD_MAX1));
UpdateBounds();
MarkRenderStateDirty();
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
bool UGameplayDebuggingComponent::ServerCollectNavmeshData_Validate(FVector_NetQuantize10 TargetLocation)
{
bool bIsValid = false;
#if WITH_RECAST
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
bIsValid = NavSys && NavSys->GetMainNavData(FNavigationSystem::DontCreate);
#endif
return bIsValid;
}
bool UGameplayDebuggingComponent::ServerDiscardNavmeshData_Validate()
{
return true;
}
namespace NavMeshDebug
{
struct FShortVector
{
int16 X;
int16 Y;
int16 Z;
FShortVector()
:X(0), Y(0), Z(0)
{
}
FShortVector(const FVector& Vec)
: X(Vec.X)
, Y(Vec.Y)
, Z(Vec.Z)
{
}
FShortVector& operator=(const FVector& R)
{
X = R.X;
Y = R.Y;
Z = R.Z;
return *this;
}
FVector ToVector() const
{
return FVector(X, Y, Z);
}
};
struct FOffMeshLinkFlags
{
uint8 Radius : 6;
uint8 Direction : 1;
uint8 ValidEnds : 1;
};
struct FOffMeshLink
{
FShortVector Left;
FShortVector Right;
FColor Color;
union
{
FOffMeshLinkFlags PackedFlags;
uint8 ByteFlags;
};
};
struct FAreaPolys
{
TArray<int32> Indices;
FColor Color;
};
struct FTileData
{
FVector Location;
TArray<FAreaPolys> Areas;
TArray<FShortVector> Verts;
TArray<FOffMeshLink> Links;
};
}
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FShortVector& ShortVector)
{
Ar << ShortVector.X;
Ar << ShortVector.Y;
Ar << ShortVector.Z;
return Ar;
}
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FOffMeshLink& Data)
{
Ar << Data.Left;
Ar << Data.Right;
Ar << Data.Color.R;
Ar << Data.Color.G;
Ar << Data.Color.B;
Ar << Data.ByteFlags;
return Ar;
}
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FAreaPolys& Data)
{
Ar << Data.Indices;
Ar << Data.Color.R;
Ar << Data.Color.G;
Ar << Data.Color.B;
Data.Color.A = 255;
return Ar;
}
FArchive& operator<<(FArchive& Ar, NavMeshDebug::FTileData& Data)
{
Ar << Data.Areas;
Ar << Data.Location;
Ar << Data.Verts;
Ar << Data.Links;
return Ar;
}
void UGameplayDebuggingComponent::ServerDiscardNavmeshData_Implementation()
{
NavmeshRepData.Empty();
}
FORCEINLINE bool LineInCorrectDistance(const FVector& PlayerLoc, const FVector& Start, const FVector& End, float CorrectDistance = -1)
{
const float MaxDistance = CorrectDistance > 0 ? (CorrectDistance*CorrectDistance) : ARecastNavMesh::GetDrawDistanceSq();
if ((FVector::DistSquared(Start, PlayerLoc) > MaxDistance || FVector::DistSquared(End, PlayerLoc) > MaxDistance))
{
return false;
}
return true;
}
#if WITH_RECAST
ARecastNavMesh* UGameplayDebuggingComponent::GetNavData()
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
if (NavSys == NULL)
{
return NULL;
}
// Try to get the correct nav-mesh relative to the selected actor.
APawn* TargetPawn = Cast<APawn>(TargetActor);
if (TargetPawn != NULL)
{
const FNavAgentProperties& NavAgentProperties = TargetPawn->GetNavAgentPropertiesRef();
return Cast<ARecastNavMesh>(NavSys->GetNavDataForProps(NavAgentProperties));
}
// If it wasn't found, just get the main nav-mesh data.
return Cast<ARecastNavMesh>(NavSys->GetMainNavData(FNavigationSystem::DontCreate));
#else
return NULL;
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
#endif
void UGameplayDebuggingComponent::ServerCollectNavmeshData_Implementation(FVector_NetQuantize10 TargetLocation)
{
#if WITH_RECAST
UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld());
ARecastNavMesh* NavData = GetNavData();
if (NavData == NULL)
{
NavmeshRepData.Empty();
return;
}
const double Timer1 = FPlatformTime::Seconds();
TArray<int32> Indices;
int32 TileX = 0;
int32 TileY = 0;
const int32 DeltaX[] = { 0, 1, 1, 0, -1, -1, -1, 0, 1 };
const int32 DeltaY[] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
NavData->BeginBatchQuery();
// add 3x3 neighborhood of target
int32 TargetTileX = 0;
int32 TargetTileY = 0;
NavData->GetNavMeshTileXY(TargetLocation, TargetTileX, TargetTileY);
for (int32 i = 0; i < ARRAY_COUNT(DeltaX); i++)
{
const int32 NeiX = TargetTileX + DeltaX[i];
const int32 NeiY = TargetTileY + DeltaY[i];
NavData->GetNavMeshTilesAt(NeiX, NeiY, Indices);
}
const FNavDataConfig& NavConfig = NavData->GetConfig();
FColor NavMeshColors[RECAST_MAX_AREAS];
NavMeshColors[RECAST_DEFAULT_AREA] = NavConfig.Color.DWColor() > 0 ? NavConfig.Color : NavMeshRenderColor_RecastMesh;
for (uint8 i = 0; i < RECAST_DEFAULT_AREA; ++i)
{
NavMeshColors[i] = NavData->GetAreaIDColor(i);
}
TArray<uint8> UncompressedBuffer;
FMemoryWriter ArWriter(UncompressedBuffer);
int32 NumIndices = Indices.Num();
ArWriter << NumIndices;
for (int32 i = 0; i < NumIndices; i++)
{
FRecastDebugGeometry NavMeshGeometry;
NavMeshGeometry.bGatherPolyEdges = false;
NavMeshGeometry.bGatherNavMeshEdges = false;
NavData->GetDebugGeometry(NavMeshGeometry, Indices[i]);
NavMeshDebug::FTileData TileData;
const FBox TileBoundingBox = NavData->GetNavMeshTileBounds(Indices[i]);
TileData.Location = TileBoundingBox.GetCenter();
for (int32 VertIndex = 0; VertIndex < NavMeshGeometry.MeshVerts.Num(); ++VertIndex)
{
const NavMeshDebug::FShortVector SV = NavMeshGeometry.MeshVerts[VertIndex] - TileData.Location;
TileData.Verts.Add(SV);
}
for (int32 iArea = 0; iArea < RECAST_MAX_AREAS; iArea++)
{
const int32 NumTris = NavMeshGeometry.AreaIndices[iArea].Num();
if (NumTris)
{
NavMeshDebug::FAreaPolys AreaPolys;
for (int32 AreaIndicesIndex = 0; AreaIndicesIndex < NavMeshGeometry.AreaIndices[iArea].Num(); ++AreaIndicesIndex)
{
AreaPolys.Indices.Add(NavMeshGeometry.AreaIndices[iArea][AreaIndicesIndex]);
}
AreaPolys.Color = NavMeshColors[iArea];
TileData.Areas.Add(AreaPolys);
}
}
TileData.Links.Reserve(NavMeshGeometry.OffMeshLinks.Num());
for (int32 iLink = 0; iLink < NavMeshGeometry.OffMeshLinks.Num(); iLink++)
{
const FRecastDebugGeometry::FOffMeshLink& SrcLink = NavMeshGeometry.OffMeshLinks[iLink];
NavMeshDebug::FOffMeshLink Link;
Link.Left = SrcLink.Left - TileData.Location;
Link.Right = SrcLink.Right - TileData.Location;
Link.Color = ((SrcLink.Direction && SrcLink.ValidEnds) || (SrcLink.ValidEnds & FRecastDebugGeometry::OMLE_Left)) ? DarkenColor(NavMeshColors[SrcLink.AreaID]) : NavMeshRenderColor_OffMeshConnectionInvalid;
Link.PackedFlags.Radius = (int8)SrcLink.Radius;
Link.PackedFlags.Direction = SrcLink.Direction;
Link.PackedFlags.ValidEnds = SrcLink.ValidEnds;
TileData.Links.Add(Link);
}
ArWriter << TileData;
}
NavData->FinishBatchQuery();
const double Timer2 = FPlatformTime::Seconds();
const int32 HeaderSize = sizeof(int32);
NavmeshRepData.Init(0, HeaderSize + FMath::TruncToInt(1.1f * UncompressedBuffer.Num()));
const int32 UncompressedSize = UncompressedBuffer.Num();
int32 CompressedSize = NavmeshRepData.Num() - HeaderSize;
uint8* DestBuffer = NavmeshRepData.GetData();
FMemory::Memcpy(DestBuffer, &UncompressedSize, HeaderSize);
DestBuffer += HeaderSize;
FCompression::CompressMemory((ECompressionFlags)(COMPRESS_ZLIB | COMPRESS_BiasMemory),
(void*)DestBuffer, CompressedSize, (void*)UncompressedBuffer.GetData(), UncompressedSize);
NavmeshRepData.SetNum(CompressedSize + HeaderSize, false);
const double Timer3 = FPlatformTime::Seconds();
UE_LOG(LogGameplayDebugger, Log, TEXT("Preparing navmesh data: %.1fkB took %.3fms (collect: %.3fms + compress %d%%: %.3fms)"),
NavmeshRepData.Num() / 1024.0f, 1000.0f * (Timer3 - Timer1),
1000.0f * (Timer2 - Timer1),
FMath::TruncToInt(100.0f * NavmeshRepData.Num() / UncompressedBuffer.Num()), 1000.0f * (Timer3 - Timer2));
#endif
if (World && World->GetNetMode() != NM_DedicatedServer)
{
OnRep_UpdateNavmesh();
}
}
void UGameplayDebuggingComponent::PrepareNavMeshData(struct FNavMeshSceneProxyData* CurrentData) const
{
#if WITH_RECAST
if (CurrentData)
{
CurrentData->Reset();
CurrentData->bNeedsNewData = false;
// uncompress data
TArray<uint8> UncompressedBuffer;
const int32 HeaderSize = sizeof(int32);
if (NavmeshRepData.Num() > HeaderSize)
{
int32 UncompressedSize = 0;
uint8* SrcBuffer = (uint8*)NavmeshRepData.GetData();
FMemory::Memcpy(&UncompressedSize, SrcBuffer, HeaderSize);
SrcBuffer += HeaderSize;
const int32 CompressedSize = NavmeshRepData.Num() - HeaderSize;
UncompressedBuffer.AddZeroed(UncompressedSize);
FCompression::UncompressMemory((ECompressionFlags)(COMPRESS_ZLIB),
(void*)UncompressedBuffer.GetData(), UncompressedSize, SrcBuffer, CompressedSize);
}
// read serialized values
CurrentData->bEnableDrawing = (UncompressedBuffer.Num() > 0);
if (!CurrentData->bEnableDrawing)
{
return;
}
FMemoryReader ArReader(UncompressedBuffer);
int32 NumTiles = 0;
ArReader << NumTiles;
int32 IndexesOffset = 0;
for (int32 iTile = 0; iTile < NumTiles; iTile++)
{
NavMeshDebug::FTileData TileData;
ArReader << TileData;
FVector OffsetLocation = TileData.Location;
TArray<FVector> Verts;
Verts.Reserve(TileData.Verts.Num());
for (int32 VertIndex = 0; VertIndex < TileData.Verts.Num(); ++VertIndex)
{
const FVector Loc = TileData.Verts[VertIndex].ToVector() + OffsetLocation;
Verts.Add(Loc);
}
CurrentData->Bounds += FBox(Verts);
for (int32 iArea = 0; iArea < TileData.Areas.Num(); iArea++)
{
const NavMeshDebug::FAreaPolys& SrcArea = TileData.Areas[iArea];
FNavMeshSceneProxyData::FDebugMeshData DebugMeshData;
DebugMeshData.ClusterColor = SrcArea.Color;
DebugMeshData.ClusterColor.A = 128;
for (int32 iTri = 0; iTri < SrcArea.Indices.Num(); iTri += 3)
{
const int32 Index0 = SrcArea.Indices[iTri + 0];
const int32 Index1 = SrcArea.Indices[iTri + 1];
const int32 Index2 = SrcArea.Indices[iTri + 2];
FVector V0 = Verts[Index0];
FVector V1 = Verts[Index1];
FVector V2 = Verts[Index2];
CurrentData->TileEdgeLines.Add(FDebugRenderSceneProxy::FDebugLine(V0 + CurrentData->NavMeshDrawOffset, V1 + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
CurrentData->TileEdgeLines.Add(FDebugRenderSceneProxy::FDebugLine(V1 + CurrentData->NavMeshDrawOffset, V2 + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
CurrentData->TileEdgeLines.Add(FDebugRenderSceneProxy::FDebugLine(V2 + CurrentData->NavMeshDrawOffset, V0 + CurrentData->NavMeshDrawOffset, NavMeshRenderColor_Recast_TileEdges));
AddTriangleHelper(DebugMeshData, Index0 + IndexesOffset, Index1 + IndexesOffset, Index2 + IndexesOffset);
}
for (int32 iVert = 0; iVert < Verts.Num(); iVert++)
{
AddVertexHelper(DebugMeshData, Verts[iVert] + CurrentData->NavMeshDrawOffset);
}
CurrentData->MeshBuilders.Add(DebugMeshData);
IndexesOffset += Verts.Num();
}
for (int32 i = 0; i < TileData.Links.Num(); i++)
{
const NavMeshDebug::FOffMeshLink& SrcLink = TileData.Links[i];
const FVector V0 = SrcLink.Left.ToVector() + OffsetLocation + CurrentData->NavMeshDrawOffset;
const FVector V1 = SrcLink.Right.ToVector() + OffsetLocation + CurrentData->NavMeshDrawOffset;
const FColor LinkColor = SrcLink.Color;
CacheArc(CurrentData->NavLinkLines, V0, V1, 0.4f, 4, LinkColor, LinkLines_LineThickness);
const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
CacheArrowHead(CurrentData->NavLinkLines, V1, V0+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
if (SrcLink.PackedFlags.Direction)
{
CacheArrowHead(CurrentData->NavLinkLines, V0, V1+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
}
// if the connection as a whole is valid check if there are any of ends is invalid
if (LinkColor != NavMeshRenderColor_OffMeshConnectionInvalid)
{
if (SrcLink.PackedFlags.Direction && (SrcLink.PackedFlags.ValidEnds & FRecastDebugGeometry::OMLE_Left) == 0)
{
// left end invalid - mark it
DrawWireCylinder(CurrentData->NavLinkLines, V0, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), NavMeshRenderColor_OffMeshConnectionInvalid, SrcLink.PackedFlags.Radius, 30 /*NavMesh->AgentMaxStepHeight*/, 16, 0, DefaultEdges_LineThickness);
}
if ((SrcLink.PackedFlags.ValidEnds & FRecastDebugGeometry::OMLE_Right) == 0)
{
DrawWireCylinder(CurrentData->NavLinkLines, V1, FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), NavMeshRenderColor_OffMeshConnectionInvalid, SrcLink.PackedFlags.Radius, 30 /*NavMesh->AgentMaxStepHeight*/, 16, 0, DefaultEdges_LineThickness);
}
}
}
}
}
#endif
}
//----------------------------------------------------------------------//
// rendering
//----------------------------------------------------------------------//
class FPathDebugRenderSceneProxy : public FDebugRenderSceneProxy
{
public:
FPathDebugRenderSceneProxy(const UPrimitiveComponent* InComponent, const FString &InViewFlagName)
: FDebugRenderSceneProxy(InComponent)
{
DrawType = FDebugRenderSceneProxy::SolidAndWireMeshes;
DrawAlpha = 90;
ViewFlagName = InViewFlagName;
ViewFlagIndex = uint32(FEngineShowFlags::FindIndexByName(*ViewFlagName));
bWantsSelectionOutline = false;
}
FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = View->Family->EngineShowFlags.GetSingleFlag(ViewFlagIndex);// IsShown(View);
Result.bDynamicRelevance = true;
// ideally the TranslucencyRelevance should be filled out by the material, here we do it conservative
Result.bSeparateTranslucencyRelevance = Result.bNormalTranslucencyRelevance = IsShown(View);
return Result;
}
};
FPrimitiveSceneProxy* UGameplayDebuggingComponent::CreateSceneProxy()
{
FDebugRenderSceneCompositeProxy* CompositeProxy = NULL;
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
if (!World || World->GetNetMode() == NM_DedicatedServer)
{
return NULL;
}
if (!Replicator || !Replicator->IsDrawEnabled() || Replicator->IsPendingKill() || IsPendingKill())
{
return NULL;
}
#if WITH_RECAST
if (ShouldReplicateData(EAIDebugDrawDataView::NavMesh))
{
FNavMeshSceneProxyData NewNavmeshRenderData;
NewNavmeshRenderData.Reset();
NewNavmeshRenderData.bNeedsNewData = false;
NewNavmeshRenderData.bEnableDrawing = false;
PrepareNavMeshData(&NewNavmeshRenderData);
NavMeshBounds = NewNavmeshRenderData.Bounds.GetCenter().ContainsNaN() || NewNavmeshRenderData.Bounds.GetExtent().ContainsNaN() ? FBox(FVector(-HALF_WORLD_MAX1, -HALF_WORLD_MAX1, -HALF_WORLD_MAX1), FVector(HALF_WORLD_MAX1, HALF_WORLD_MAX1, HALF_WORLD_MAX1)) : NewNavmeshRenderData.Bounds;
CompositeProxy = CompositeProxy ? CompositeProxy : (new FDebugRenderSceneCompositeProxy(this));
CompositeProxy->AddChild(new FRecastRenderingSceneProxy(this, &NewNavmeshRenderData, true));
}
#endif
#if USE_EQS_DEBUGGER
if (ShouldReplicateData(EAIDebugDrawDataView::EQS) && IsClientEQSSceneProxyEnabled() && GetSelectedActor() != NULL)
{
const int32 EQSIndex = EQSLocalData.Num() > 0 ? FMath::Clamp(CurrentEQSIndex, 0, EQSLocalData.Num() - 1) : INDEX_NONE;
if (EQSLocalData.IsValidIndex(EQSIndex))
{
CompositeProxy = CompositeProxy ? CompositeProxy : (new FDebugRenderSceneCompositeProxy(this));
auto& CurrentLocalData = EQSLocalData[EQSIndex];
FString ViewFlagName = TEXT("Game");
#if WITH_EDITOR
UEditorEngine* EEngine = Cast<UEditorEngine>(GEngine);
if (EEngine && EEngine->bIsSimulatingInEditor)
{
ViewFlagName = TEXT("DebugAI");
}
#endif
CompositeProxy->AddChild(new FEQSSceneProxy(this, ViewFlagName, CurrentLocalData.SolidSpheres, CurrentLocalData.Texts));
}
}
#endif // USE_EQS_DEBUGGER
const bool bDrawFullData = Replicator->GetSelectedActorToDebug() == GetSelectedActor() && GetSelectedActor() != NULL;
if (bDrawFullData && ShouldReplicateData(EAIDebugDrawDataView::Basic))
{
CompositeProxy = CompositeProxy ? CompositeProxy : (new FDebugRenderSceneCompositeProxy(this));
TArray<FDebugRenderSceneProxy::FMesh> Meshes;
TArray<FDebugRenderSceneProxy::FDebugLine> Lines;
for (FPathCorridorPolygons& CurrentPoly : PathCorridorPolygons)
{
if (CurrentPoly.Points.Num() > 2)
{
int32 LastIndex = 0;
FVector FirstVertex = CurrentPoly.Points[0];
FDebugRenderSceneProxy::FMesh TestMesh;
TestMesh.Color = CurrentPoly.Color;// FColor::Green;
for (int32 Index = 1; Index < CurrentPoly.Points.Num()-1; Index += 1)
{
TestMesh.Vertices.Add(FDynamicMeshVertex(FirstVertex));
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentPoly.Points[Index + 0]));
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentPoly.Points[Index + 1]));
TestMesh.Indices.Add(LastIndex++);
TestMesh.Indices.Add(LastIndex++);
TestMesh.Indices.Add(LastIndex++);
}
Meshes.Add(TestMesh);
}
for (int32 VIdx = 0; VIdx < CurrentPoly.Points.Num(); VIdx++)
{
Lines.Add(FDebugRenderSceneProxy::FDebugLine(
CurrentPoly.Points[VIdx],
CurrentPoly.Points[(VIdx + 1) % CurrentPoly.Points.Num()],
CurrentPoly.Color,
2)
);
}
}
FString ViewFlagName = TEXT("Game");
#if WITH_EDITOR
UEditorEngine* EEngine = Cast<UEditorEngine>(GEngine);
if (EEngine && EEngine->bIsSimulatingInEditor)
{
ViewFlagName = TEXT("DebugAI");
}
#endif
FPathDebugRenderSceneProxy *DebugSceneProxy = new FPathDebugRenderSceneProxy(this, ViewFlagName);
DebugSceneProxy->Lines = Lines;
DebugSceneProxy->Meshes = Meshes;
CompositeProxy->AddChild(DebugSceneProxy);
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
return CompositeProxy;
}
FBoxSphereBounds UGameplayDebuggingComponent::CalcBounds(const FTransform& LocalToWorld) const
{
FBox MyBounds;
MyBounds.Init();
#if WITH_RECAST
if (ShouldReplicateData(EAIDebugDrawDataView::NavMesh))
{
MyBounds = NavMeshBounds;
}
#endif
#if USE_EQS_DEBUGGER
if ((EQSRepData.Num() && ShouldReplicateData(EAIDebugDrawDataView::EQS)) || PathCorridorPolygons.Num())
{
MyBounds = FBox(FVector(-HALF_WORLD_MAX1, -HALF_WORLD_MAX1, -HALF_WORLD_MAX1), FVector(HALF_WORLD_MAX1, HALF_WORLD_MAX1, HALF_WORLD_MAX1));
}
#endif // USE_EQS_DEBUGGER
return MyBounds;
}
void UGameplayDebuggingComponent::CreateRenderState_Concurrent()
{
Super::CreateRenderState_Concurrent();
#if WITH_EDITOR
if (SceneProxy)
{
static_cast<FDebugRenderSceneCompositeProxy*>(SceneProxy)->RegisterDebugDrawDelgate();
}
#endif
}
void UGameplayDebuggingComponent::DestroyRenderState_Concurrent()
{
#if WITH_EDITOR
if (SceneProxy)
{
static_cast<FDebugRenderSceneCompositeProxy*>(SceneProxy)->UnregisterDebugDrawDelgate();
}
#endif
Super::DestroyRenderState_Concurrent();
}
void UGameplayDebuggingComponent::CollectPerceptionData()
{
#if USE_EQS_DEBUGGER
if (!ShouldReplicateData(EAIDebugDrawDataView::Perception))
{
return;
}
APawn* MyPawn = Cast<APawn>(GetSelectedActor());
if (MyPawn)
{
AAIController* BTAI = Cast<AAIController>(MyPawn->GetController());
if (BTAI)
{
const UAIPerceptionComponent* PerceptionComponent = BTAI->GetAIPerceptionComponent();
if (PerceptionComponent == nullptr)
{
PerceptionComponent = MyPawn->FindComponentByClass<UAIPerceptionComponent>();
}
if (PerceptionComponent)
{
TArray<FString> PerceptionTexts;
PerceptionShapeElements.Reset();
PerceptionComponent->GrabGameplayDebuggerData(PerceptionTexts, PerceptionShapeElements);
DistanceFromPlayer = DistanceFromSensor = -1;
AGameplayDebuggingReplicator* Replicator = Cast<AGameplayDebuggingReplicator>(GetOwner());
UGameplayDebuggingControllerComponent* GDC = Replicator ? Replicator->FindComponentByClass<UGameplayDebuggingControllerComponent>() : nullptr;
APlayerController* MyPC = GDC && GDC->GetDebugCameraController().IsValid() ? GDC->GetDebugCameraController().Get() : Replicator->GetLocalPlayerOwner();
if (MyPC && MyPC->GetPawn())
{
DistanceFromPlayer = (MyPawn->GetActorLocation() - MyPC->GetPawn()->GetActorLocation()).Size();
DistanceFromSensor = SensingComponentLocation != FVector::ZeroVector ? (SensingComponentLocation - MyPC->GetPawn()->GetActorLocation()).Size() : -1;
}
}
UAIPerceptionSystem* PerceptionSys = UAIPerceptionSystem::GetCurrent(MyPawn->GetWorld());
if (PerceptionSys)
{
PerceptionLegend = PerceptionSys->GetPerceptionDebugLegend();
}
}
}
#endif
}