Files
UnrealEngineUWP/Engine/Source/Developer/GameplayDebugger/Private/GameplayDebuggingComponent.cpp
Marc Audy d3e1006533 Copying //UE4/Dev-Framework to Dev-Main (//UE4/Dev-Main) @ 2926658
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2821607 on 2016/01/08 by Mieszko.Zielinski

	Added a way to limit amount of information logged by vlog by discarding logs from classes from outside of class whitelist #UE4

	This feature was followed by refactoring of functions taking FVisualLogEntry pointers to use references instead.

	#rb Lukasz.Furman
	#codereview John.Abercrombie

Change 2828384 on 2016/01/14 by Mieszko.Zielinski

	Back out of visual log refactor done as part of CL#2821607 #UE4

Change 2910454 on 2016/03/15 by Zak.Middleton

	#ue4 - Properly exclude zero-distance MTD results in ComponentEncroachesBlockingGeometry_WithAdjustment() in the presense of multiple overlaps.

	#rb Jeff.Farris
	#jira UE-24327
	UDN: https://udn.unrealengine.com/questions/270574/jeff-farris-hack-for-physx-mtd.html

Change 2910548 on 2016/03/15 by Zak.Middleton

	#ue4 - Handle MTD computation returning NaN direction when there is a "contact" with zero distance.

Change 2912311 on 2016/03/16 by Marc.Audy

	Properly handle overlaps in C++ in documentation code and UE4 to Unity doc
	#rb Martin.Wilson

Change 2913086 on 2016/03/17 by Marc.Audy

	Adding ability to have 9 parameters to a dynamic delegate

Change 2913101 on 2016/03/17 by Marc.Audy

	Fix some of the loctext error messages

Change 2913102 on 2016/03/17 by Thomas.Sarkanen

	Console usability improvements

	Display console autocompletion commands from the lexicographically first element up to either the total number of commands or MAX_AUTOCOMPLETION_LINES, whichever is least. The previous behaviour started the list "in the middle" and hid the first elements if there were too many matches. Thus "[ab ac ad]" with "aa" hidden now becomes "[aa ab ac]" with "ad" hidden.
	To make scrolling work as expected, the input handling of the up and down arrow keys has been reversed so that the cursor iterates forward starting from the top with the down arrow key, and goes back up with the up arrow key. Command history is still accessed with the up arrow key.
	This commit also undoes one of the most evil uses of operator overloading I've ever seen, on par with "#define true false" but more subtle

	Color console autocomplete entries to denote their type: command, CVar or other (manual autocompletion entries). CVars are further divided into writeable and read-only variables.
	Assume that manual console autocompletion entries are commands. This makes the autocompletion list colors more consistent and less noisy

	Automatically select (but don't complete) a command on console character input. To prevent the autocomplete from becoming too trigger happy, no longer automatically complete commands for arbitrary key inputs that we happen to have a match for

	Allow cycling through console commands with the tab key
	Discriminate between first time and repeated tab presses and only scroll through autocomplete entries on the latter

	Fix off-by-one error in console: "x more matches" line was being shown when the number of autocomplete elements was equal to MAX_AUTOCOMPLETION_LINES
	Fix an off-by-one error that was causing the topmost console command to not be shown if there was an autocomplete scroll region

	Show the currently selected autocomplete entry faded out behind the user's typed input
	Slightly increase brightness of the normal input text colour to better distinguish between the typed and autocompleted parts of the input line

	Left-justify command descriptions in the console autocompletion box
	Detect overflow of console autocomplete lines on low resolutions and decrease the space used for description justification to compensate

	Make the console input, history and autocomplete colours user configurable

	Add console background transparency. Configurable, set to 15% by default

	Add missing closing quote to the console dump HTML template

	#github #2061: Console usability improvements from Mattiwatti

Change 2913104 on 2016/03/17 by Thomas.Sarkanen

	Added indicator displayed on animation nodes when they use the 'fast path'

	Added checkbox that can be used to audit Blueprint fast-path usage.
	Switched almost all animation node widgets to derive from new SAnimationGraphNode. This creates the overlay widget that indicates whether this node is using a more optimal path.

	#doc Also added documentation tooltips and UDN doc files/images for the fast path systems.
	#jira UE-24698 - Add icon to pins in anim graph to indicate 'fast mode' access
	#rb Martin.Wilson

Change 2913306 on 2016/03/17 by Marc.Audy

	Cleaning up GetResourceSize
	- Made many call Super::GetResourceSize
	- Removed trivial implementations
	- Fixed HierarchicalInstanceStaticMeshComponent double counting an array

Change 2913535 on 2016/03/17 by Lukasz.Furman

	fixed broken behavior tree graph data after subnode undo
	#ue4 UE-28198

Change 2913608 on 2016/03/17 by Lukasz.Furman

	fixed behavior tree execution indices after undoing move in editor
	#ue4 UE-26705

Change 2913847 on 2016/03/17 by Lukasz.Furman

	added new automation test for UE-28309
	#ue4

Change 2913849 on 2016/03/17 by Lukasz.Furman

	fixed behavior tree skipping over branch when restart request comes during AbortCurrentTask call
	#ue4 UE-28309

Change 2913895 on 2016/03/17 by Marc.Audy

	Added 'self' argument to Actor and PrimitiveComponent delegates that didn't already supply one
	Fixed up all C++ uses of these delegates
	#jira UE-23122
	#rb Zak.Middleton

Change 2914743 on 2016/03/18 by Thomas.Sarkanen

	Editing of primitive data in PhAT

[CL 2926677 by Marc Audy in Main branch]
2016-03-29 16:33:59 -04:00

1887 lines
60 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
//////////////////////////////////////////////////////////////////////////
// THIS CLASS IS NOW DEPRECATED AND WILL BE REMOVED IN NEXT VERSION
// Please check GameplayDebugger.h for details.
#include "GameplayDebuggerPrivatePCH.h"
#include "Net/UnrealNetwork.h"
#include "DebugRenderSceneProxy.h"
#include "AI/Navigation/NavigationSystem.h"
#include "AI/Navigation/NavMeshRenderingComponent.h"
#include "AI/Navigation/RecastNavMesh.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 "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 "GameplayTasksComponent.h"
#include "Perception/AIPerceptionComponent.h"
#if WITH_EDITOR
#include "Editor/EditorEngine.h"
#include "GeomTools.h"
#endif // WITH_EDITOR
#include "GameplayDebuggingComponent.h"
#include "GameplayDebuggingControllerComponent.h"
#include "GameplayDebuggingReplicator.h"
DEFINE_LOG_CATEGORY(LogGameplayDebugger);
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakePoint(const FVector& Location, const float Radius, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points.Add(Location);
NewElement.ThicknesOrRadius = Radius;
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::SinglePoint;
return NewElement;
}
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakeSegment(const FVector& StartLocation, const FVector& EndLocation, const float Thickness, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points.Add(StartLocation);
NewElement.Points.Add(EndLocation);
NewElement.ThicknesOrRadius = Thickness;
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::Segment;
return NewElement;
}
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakeBox(const FVector& Center, const FVector& Extent, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points.Add(Center);
NewElement.Points.Add(Extent);
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::Box;
return NewElement;
}
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakeCone(const FVector& Location, const FVector& Direction, const float Length, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points.Add(Location);
NewElement.Points.Add(Direction);
NewElement.ThicknesOrRadius = Length;
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::Cone;
return NewElement;
}
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakeCylinder(const FVector& Center, const float Radius, const float HalfHeight, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points.Add(Center - FVector(0, 0, HalfHeight));
NewElement.Points.Add(Center + FVector(0, 0, HalfHeight));
NewElement.ThicknesOrRadius = Radius;
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::Cylinder;
return NewElement;
}
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakeCapsule(const FVector& Center, const float Radius, const float HalfHeight, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points.Add(Center - FVector(0, 0, HalfHeight));
NewElement.Points.Add(Center + FVector(0, 0, HalfHeight));
NewElement.ThicknesOrRadius = Radius;
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::Capsule;
return NewElement;
}
FGameplayDebuggerShapeElement FGameplayDebuggerShapeElement::MakePolygon(const TArray<FVector>& Verts, const FColor& Color, const FString& Description)
{
FGameplayDebuggerShapeElement NewElement;
NewElement.Points = Verts;
NewElement.Color = Color;
NewElement.Description = Description;
NewElement.Type = EGameplayDebuggerShapeElement::Polygon;
return NewElement;
}
//----------------------------------------------------------------------//
// 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("");
}
GameplayTasksState = TEXT("");
UGameplayTasksComponent* GTComponent = MyPawn->FindComponentByClass<UGameplayTasksComponent>();
if (GTComponent)
{
for (FConstGameplayTaskIterator It = GTComponent->GetPriorityQueueIterator(); It; ++It)
{
const UGameplayTask* QueueTask = *It;
if (QueueTask)
{
const UObject* OwnerOb = Cast<const UObject>(QueueTask->GetTaskOwner());
GameplayTasksState += FString::Printf(TEXT("{white}%s%s {%s}%s {white}Owner:{yellow}%s {white}Res:{yellow}%s\n"),
*QueueTask->GetName(),
QueueTask->GetInstanceName() != NAME_None ? *FString::Printf(TEXT(" {yellow}[%s]"), *QueueTask->GetInstanceName().ToString()) : TEXT(""),
QueueTask->IsActive() ? TEXT("green") : TEXT("orange"),
*QueueTask->GetTaskStateName(),
(OwnerOb == GTComponent) ? TEXT("default") : *GetNameSafe(OwnerOb),
*QueueTask->GetRequiredResources().GetDebugDescription());
}
}
}
#endif //!(UE_BUILD_SHIPPING || UE_BUILD_TEST)
}
void UGameplayDebuggingComponent::CollectBasicAbilityData(APawn* MyPawn)
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
// DEPRECATED and not supported anymore, use new category based GameplayDebugger instead
// IGameplayAbilitiesModule& AbilitiesModule = FModuleManager::LoadModuleChecked<IGameplayAbilitiesModule>("GameplayAbilities");
// AbilitiesModule.GetActiveAbilitiesDebugDataForActor(MyPawn, AbilityInfo, bUsingAbilities);
// bIsUsingAbilities = bUsingAbilities;
#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
static const FColor NavMeshRenderColor_Recast_TriangleEdges(255, 255, 255);
static const FColor NavMeshRenderColor_Recast_TileEdges(16, 16, 16, 32);
static const FColor NavMeshRenderColor_Recast_NavMeshEdges(32, 63, 0, 220);
static const FColor NavMeshRenderColor_RecastMesh(140, 255, 0, 164);
static const FColor NavMeshRenderColor_TileBounds(255, 255, 64, 255);
static const FColor NavMeshRenderColor_PathCollidingGeom(255, 255, 255, 40);
static const FColor NavMeshRenderColor_RecastTileBeingRebuilt(255, 0, 0, 64);
static const FColor NavMeshRenderColor_OffMeshConnectionInvalid(64, 64, 64);
static const float DefaultEdges_LineThickness = 0.0f;
static const float PolyEdges_LineThickness = 1.5f;
static const float NavMeshEdges_LineThickness = 3.5f;
static const float LinkLines_LineThickness = 2.0f;
static const float ClusterLinkLines_LineThickness = 2.0f;
namespace FNavMeshRenderingHelpers_DEPRECATEDSUPPORT
{
bool LineInView(const FVector& Start, const FVector& End, const FSceneView* View, bool bUseDistanceCheck)
{
if (FVector::DistSquared(Start, View->ViewMatrices.ViewOrigin) > ARecastNavMesh::GetDrawDistanceSq() ||
FVector::DistSquared(End, View->ViewMatrices.ViewOrigin) > ARecastNavMesh::GetDrawDistanceSq())
{
return false;
}
for (int32 PlaneIdx = 0; PlaneIdx < View->ViewFrustum.Planes.Num(); ++PlaneIdx)
{
const FPlane& CurPlane = View->ViewFrustum.Planes[PlaneIdx];
if (CurPlane.PlaneDot(Start) > 0.f && CurPlane.PlaneDot(End) > 0.f)
{
return false;
}
}
return true;
}
bool LineInCorrectDistance(const FVector& Start, const FVector& End, const FSceneView* View, float CorrectDistance = -1)
{
const float MaxDistanceSq = (CorrectDistance > 0) ? FMath::Square(CorrectDistance) : ARecastNavMesh::GetDrawDistanceSq();
return FVector::DistSquared(Start, View->ViewMatrices.ViewOrigin) < MaxDistanceSq &&
FVector::DistSquared(End, View->ViewMatrices.ViewOrigin) < MaxDistanceSq;
}
FVector EvalArc(const FVector& Org, const FVector& Dir, const float h, const float u)
{
FVector Pt = Org + Dir * u;
Pt.Z += h * (1 - (u * 2 - 1)*(u * 2 - 1));
return Pt;
}
void CacheArc(TArray<FDebugRenderSceneProxy::FDebugLine>& DebugLines, const FVector& Start, const FVector& End, const float Height, const uint32 Segments, const FLinearColor& Color, float LineThickness = 0)
{
if (Segments == 0)
{
return;
}
const float ArcPtsScale = 1.0f / (float)Segments;
const FVector Dir = End - Start;
const float Length = Dir.Size();
FVector Prev = Start;
for (uint32 i = 1; i <= Segments; ++i)
{
const float u = i * ArcPtsScale;
const FVector Pt = EvalArc(Start, Dir, Length*Height, u);
DebugLines.Add(FDebugRenderSceneProxy::FDebugLine(Prev, Pt, Color.ToFColor(true)));
Prev = Pt;
}
}
void CacheArrowHead(TArray<FDebugRenderSceneProxy::FDebugLine>& DebugLines, const FVector& Tip, const FVector& Origin, const float Size, const FLinearColor& Color, float LineThickness = 0)
{
FVector Ax, Ay, Az(0, 1, 0);
Ay = Origin - Tip;
Ay.Normalize();
Ax = FVector::CrossProduct(Az, Ay);
FHitProxyId HitProxyId;
DebugLines.Add(FDebugRenderSceneProxy::FDebugLine(Tip, FVector(Tip.X + Ay.X*Size + Ax.X*Size / 3, Tip.Y + Ay.Y*Size + Ax.Y*Size / 3, Tip.Z + Ay.Z*Size + Ax.Z*Size / 3), Color.ToFColor(true)));
DebugLines.Add(FDebugRenderSceneProxy::FDebugLine(Tip, FVector(Tip.X + Ay.X*Size - Ax.X*Size / 3, Tip.Y + Ay.Y*Size - Ax.Y*Size / 3, Tip.Z + Ay.Z*Size - Ax.Z*Size / 3), Color.ToFColor(true)));
}
void DrawWireCylinder(TArray<FDebugRenderSceneProxy::FDebugLine>& DebugLines, const FVector& Base, const FVector& X, const FVector& Y, const FVector& Z, FColor Color, float Radius, float HalfHeight, int32 NumSides, uint8 DepthPriority, float LineThickness = 0)
{
const float AngleDelta = 2.0f * PI / NumSides;
FVector LastVertex = Base + X * Radius;
FHitProxyId HitProxyId;
for (int32 SideIndex = 0; SideIndex < NumSides; SideIndex++)
{
const FVector Vertex = Base + (X * FMath::Cos(AngleDelta * (SideIndex + 1)) + Y * FMath::Sin(AngleDelta * (SideIndex + 1))) * Radius;
DebugLines.Add(FDebugRenderSceneProxy::FDebugLine(LastVertex - Z * HalfHeight, Vertex - Z * HalfHeight, Color));
DebugLines.Add(FDebugRenderSceneProxy::FDebugLine(LastVertex + Z * HalfHeight, Vertex + Z * HalfHeight, Color));
DebugLines.Add(FDebugRenderSceneProxy::FDebugLine(LastVertex - Z * HalfHeight, LastVertex + Z * HalfHeight, Color));
LastVertex = Vertex;
}
}
inline uint8 GetBit(int32 v, uint8 bit)
{
return (v & (1 << bit)) >> bit;
}
FColor GetClusterColor(int32 Idx)
{
uint8 r = 1 + GetBit(Idx, 1) + GetBit(Idx, 3) * 2;
uint8 g = 1 + GetBit(Idx, 2) + GetBit(Idx, 4) * 2;
uint8 b = 1 + GetBit(Idx, 0) + GetBit(Idx, 5) * 2;
return FColor(r * 63, g * 63, b * 63, 164);
}
FColor DarkenColor(const FColor& Base)
{
const uint32 Col = Base.DWColor();
return FColor(((Col >> 1) & 0x007f7f7f) | (Col & 0xff000000));
}
void AddVertex(FNavMeshSceneProxyData::FDebugMeshData& MeshData, const FVector& Pos, const FColor Color = FColor::White)
{
const int32 VertexIndex = MeshData.Vertices.Num();
FDynamicMeshVertex* Vertex = new(MeshData.Vertices) FDynamicMeshVertex;
Vertex->Position = Pos;
Vertex->TextureCoordinate = FVector2D::ZeroVector;
Vertex->TangentX = FVector(1.0f, 0.0f, 0.0f);
Vertex->TangentZ = FVector(0.0f, 1.0f, 0.0f);
// store the sign of the determinant in TangentZ.W (-1=0,+1=255)
Vertex->TangentZ.Vector.W = 255;
Vertex->Color = Color;
}
void AddTriangle(FNavMeshSceneProxyData::FDebugMeshData& MeshData, int32 V0, int32 V1, int32 V2)
{
MeshData.Indices.Add(V0);
MeshData.Indices.Add(V1);
MeshData.Indices.Add(V2);
}
FVector Recast2UnrealPoint(const float* RecastPoint)
{
return FVector(-RecastPoint[0], -RecastPoint[2], RecastPoint[1]);
}
void AddRecastGeometry(TArray<FVector>& OutVertexBuffer, TArray<int32>& OutIndexBuffer, const float* Coords, int32 NumVerts, const int32* Faces, int32 NumFaces)
{
const int32 VertIndexBase = OutVertexBuffer.Num();
for (int32 VertIdx = 0; VertIdx < NumVerts * 3; VertIdx += 3)
{
OutVertexBuffer.Add(Recast2UnrealPoint(&Coords[VertIdx]));
}
const int32 FirstNewFaceVertexIndex = OutIndexBuffer.Num();
const uint32 NumIndices = NumFaces * 3;
OutIndexBuffer.AddUninitialized(NumIndices);
for (uint32 Index = 0; Index < NumIndices; ++Index)
{
OutIndexBuffer[FirstNewFaceVertexIndex + Index] = VertIndexBase + Faces[Index];
}
}
}
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)) ? 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
if (UncompressedBuffer.Num() == 0)
{
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));
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::AddTriangle(DebugMeshData, Index0 + IndexesOffset, Index1 + IndexesOffset, Index2 + IndexesOffset);
}
for (int32 iVert = 0; iVert < Verts.Num(); iVert++)
{
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::AddVertex(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;
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::CacheArc(CurrentData->NavLinkLines, V0, V1, 0.4f, 4, LinkColor, LinkLines_LineThickness);
const FVector VOffset(0, 0, FVector::Dist(V0, V1) * 1.333f);
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::CacheArrowHead(CurrentData->NavLinkLines, V1, V0+VOffset, 30.f, LinkColor, LinkLines_LineThickness);
if (SrcLink.PackedFlags.Direction)
{
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::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
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::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)
{
FNavMeshRenderingHelpers_DEPRECATEDSUPPORT::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;
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 FNavMeshSceneProxy(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;
}
#if 0
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
#endif
}