Files
UnrealEngine/Engine/Source/Runtime/Experimental/GeometryCollectionEngine/Public/GeometryCollection/GeometryCollectionISMPoolDebugDrawComponent.cpp
2025-05-18 13:04:45 +08:00

372 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "GeometryCollectionISMPoolDebugDrawComponent.h"
#include "CollisionQueryParams.h"
#include "ComponentRecreateRenderStateContext.h"
#include "Components/HierarchicalInstancedStaticMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/PlayerController.h"
#include "GeometryCollectionISMPoolActor.h"
#include "GeometryCollectionISMPoolComponent.h"
#include "Misc/CoreDelegates.h"
#if WITH_EDITOR
#include "Editor.h"
#include "EditorViewportClient.h"
#include "LevelEditorViewport.h"
#endif
#include UE_INLINE_GENERATED_CPP_BY_NAME(GeometryCollectionISMPoolDebugDrawComponent)
static TAutoConsoleVariable<int32> CVarISMPoolStats(
TEXT("p.Chaos.GC.ISMPoolDebugStats"),
0,
TEXT("Show stats for the ISM pools"),
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable) { UGeometryCollectionISMPoolDebugDrawComponent::UpdateAllTickEnabled(); }),
PRAGMA_ENABLE_DEPRECATION_WARNINGS
ECVF_Default);
static TAutoConsoleVariable<int32> CVarISMPoolDebugDraw(
TEXT("p.Chaos.GC.ISMPoolDebugDraw"),
0,
TEXT("Show debug drawing for the ISM pools"),
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FConsoleVariableDelegate::CreateLambda([](IConsoleVariable* InVariable) { UGeometryCollectionISMPoolDebugDrawComponent::UpdateAllTickEnabled(); FGlobalComponentRecreateRenderStateContext Context; }),
PRAGMA_ENABLE_DEPRECATION_WARNINGS
ECVF_Default);
class FGeometryCollectionISMPoolDebugDrawSceneProxy final : public FDebugRenderSceneProxy
{
public:
SIZE_T GetTypeHash() const override
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FGeometryCollectionISMPoolDebugDrawSceneProxy(UGeometryCollectionISMPoolDebugDrawComponent const* InComponent)
: FDebugRenderSceneProxy(InComponent)
{
DrawType = FDebugRenderSceneProxy::SolidAndWireMeshes;
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = true;
Result.bDynamicRelevance = true;
Result.bSeparateTranslucency = Result.bNormalTranslucency = true;
return Result;
}
};
UGeometryCollectionISMPoolDebugDrawComponent::UGeometryCollectionISMPoolDebugDrawComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
bIsEditorOnly = false;
bSelectable = false;
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bAllowTickOnDedicatedServer = false;
}
void UGeometryCollectionISMPoolDebugDrawComponent::UpdateTickEnabled()
{
const bool bForceShowStats = CVarISMPoolStats.GetValueOnAnyThread() != 0;
const bool bForceShowBounds = CVarISMPoolDebugDraw.GetValueOnAnyThread() != 0;
PrimaryComponentTick.SetTickFunctionEnable(bShowStats || bForceShowStats || bShowBounds || bForceShowBounds);
}
void UGeometryCollectionISMPoolDebugDrawComponent::UpdateAllTickEnabled()
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
for (TObjectIterator<UGeometryCollectionISMPoolDebugDrawComponent> It; It; ++It)
{
It->UpdateTickEnabled();
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
void UGeometryCollectionISMPoolDebugDrawComponent::BeginPlay()
{
Super::BeginPlay();
#if UE_ENABLE_DEBUG_DRAWING
OnScreenMessagesHandle = FCoreDelegates::OnGetOnScreenMessages.AddUObject(this, &UGeometryCollectionISMPoolDebugDrawComponent::GetOnScreenMessages);
#endif
UpdateTickEnabled();
}
void UGeometryCollectionISMPoolDebugDrawComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
#if UE_ENABLE_DEBUG_DRAWING
FCoreDelegates::OnGetOnScreenMessages.Remove(OnScreenMessagesHandle);
#endif
}
FBoxSphereBounds UGeometryCollectionISMPoolDebugDrawComponent::CalcBounds(const FTransform& LocalToWorld) const
{
return SelectedComponent ? SelectedComponent->CalcBounds(LocalToWorld) : FBox(ForceInitToZero);
}
#if WITH_EDITOR
void UGeometryCollectionISMPoolDebugDrawComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if (PropertyChangedEvent.Property)
{
if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGeometryCollectionISMPoolDebugDrawComponent, bShowStats) ||
PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UGeometryCollectionISMPoolDebugDrawComponent, bShowBounds))
{
UpdateTickEnabled();
}
}
}
#endif
void UGeometryCollectionISMPoolDebugDrawComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
#if UE_ENABLE_DEBUG_DRAWING
UInstancedStaticMeshComponent const* FoundISM = nullptr;
const bool bForceShowStats = CVarISMPoolStats.GetValueOnAnyThread() != 0;
const bool bForceShowBounds = CVarISMPoolDebugDraw.GetValueOnAnyThread() != 0;
if (bShowStats || bForceShowStats || bShowBounds || bForceShowBounds)
{
#if WITH_EDITOR
if (GCurrentLevelEditingViewportClient)
{
FViewport* Viewport = GCurrentLevelEditingViewportClient->Viewport;
HHitProxy* HitResult = Viewport->GetHitProxy(Viewport->GetMouseX(), Viewport->GetMouseY());
if (HActor* HitActor = HitProxyCast<HActor>(HitResult))
{
FoundISM = Cast<UInstancedStaticMeshComponent>(HitActor->PrimComponent);
}
}
#endif
if (FoundISM == nullptr)
{
if (UWorld* World = GetWorld())
{
APlayerController const* Controller = nullptr;
for (FConstPlayerControllerIterator Iterator = World->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
APlayerController* PC = Iterator->Get();
if (PC && PC->IsLocalController())
{
Controller = PC;
}
}
if (Controller != nullptr)
{
FVector CamLoc;
FRotator CamRot;
Controller->GetPlayerViewPoint(CamLoc, CamRot);
const FVector CamForward = CamRot.Vector();
const FVector TraceStart = CamLoc;
const FVector TraceEnd = TraceStart + CamForward * 20000;
FHitResult HitResult(ForceInit);
FCollisionQueryParams TraceParams(SCENE_QUERY_STAT(ChaosDebugVisibilityTrace), true, Controller->GetPawn());
const bool bHit = World->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECC_Visibility, TraceParams);
if (bHit && HitResult.GetComponent() != nullptr)
{
if (UInstancedStaticMeshComponent* ISMComponent = Cast<UInstancedStaticMeshComponent>(HitResult.GetComponent()))
{
FoundISM = ISMComponent;
}
}
}
}
}
}
if (FoundISM != nullptr && FoundISM->GetOuter() != GetOuter())
{
FoundISM = nullptr;
}
if (SelectedComponent != FoundISM)
{
bool bChangeSelection = (SelectedComponent == nullptr) || SelectTimer < 0.f;
if (bChangeSelection)
{
#if WITH_EDITOR
if (SelectedComponent)
{
((UInstancedStaticMeshComponent*)SelectedComponent)->PushHoveredToProxy(false);
}
#endif
SelectedComponent = FoundISM;
#if WITH_EDITOR
if (SelectedComponent)
{
((UInstancedStaticMeshComponent*)SelectedComponent)->PushHoveredToProxy(true);
}
#endif
SelectTimer = 0.3f;
MarkRenderStateDirty();
}
else
{
SelectTimer -= DeltaTime;
}
}
#endif // UE_ENABLE_DEBUG_DRAWING
}
#if UE_ENABLE_DEBUG_DRAWING
static void DrawBoxAndChildren(FDebugRenderSceneProxy* DebugProxy, FTransform const& LocalToWorldTransform, TArray<FClusterNode> const& Nodes, int32 NodeIndex, int32 LevelIndex)
{
FClusterNode const& Node = Nodes[NodeIndex];
const FBox LocalBounds(FBox(Node.BoundMin, Node.BoundMax));
static TArray<FColor> Colors = { FColorList::VeryLightGrey, FColorList::CoolCopper, FColorList::GreenYellow, FColorList::CornFlowerBlue, FColorList::DustyRose, FColorList::Red, FColorList::Magenta };
const FColor LevelColor = Colors[FMath::Min(LevelIndex, Colors.Num() - 1)];
DebugProxy->Boxes.Add(FDebugRenderSceneProxy::FDebugBox(LocalBounds, LevelColor, LocalToWorldTransform));
if (Node.FirstChild > 0)
{
for (int32 ChildIndex = Node.FirstChild; ChildIndex <= Node.LastChild; ChildIndex++)
{
DrawBoxAndChildren(DebugProxy, LocalToWorldTransform, Nodes, ChildIndex, LevelIndex + 1);
}
}
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FDebugRenderSceneProxy* UGeometryCollectionISMPoolDebugDrawComponent::CreateDebugSceneProxy()
PRAGMA_ENABLE_DEPRECATION_WARNINGS
{
const bool bForceShowBounds = CVarISMPoolDebugDraw.GetValueOnAnyThread() != 0;
if (!(SelectedComponent && (bShowBounds || bForceShowBounds)))
{
return nullptr;
}
FDebugRenderSceneProxy* DebugProxy = new FGeometryCollectionISMPoolDebugDrawSceneProxy(this);
if (const UHierarchicalInstancedStaticMeshComponent* HISMComponent = Cast<UHierarchicalInstancedStaticMeshComponent>(SelectedComponent))
{
const FTransform LocalToWorldTransform = HISMComponent->GetComponentTransform();
TArray<FClusterNode> TreeNodes;
HISMComponent->GetTree(TreeNodes);
DrawBoxAndChildren(DebugProxy, LocalToWorldTransform, TreeNodes, 0, 0);
}
else if(const UInstancedStaticMeshComponent* ISMComponent = Cast<UInstancedStaticMeshComponent>(SelectedComponent))
{
const FBox LocalBounds = ISMComponent->CalcBounds(FTransform::Identity).GetBox();
const FTransform LocalToWorldTransform = ISMComponent->GetComponentTransform();
DebugProxy->Boxes.Add(FDebugRenderSceneProxy::FDebugBox(LocalBounds, FColorList::CornFlowerBlue, LocalToWorldTransform));
}
return DebugProxy;
}
void UGeometryCollectionISMPoolDebugDrawComponent::GetOnScreenMessages(TMultiMap<FCoreDelegates::EOnScreenMessageSeverity, FText>& OutMessages)
{
const bool bForceShowStats = CVarISMPoolStats.GetValueOnAnyThread() != 0;
if (!bShowStats && !bShowGlobalStats && !bForceShowStats)
{
return;
}
if (bShowGlobalStats || bForceShowStats)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
AGeometryCollectionISMPoolActor const* ISMPoolActor = Cast<AGeometryCollectionISMPoolActor>(GetOuter());
UGeometryCollectionISMPoolComponent const* ISMPoolComponent = ISMPoolActor ? ISMPoolActor->GetISMPoolComp() : nullptr;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
if (ISMPoolComponent)
{
int32 NumISMs = 0;
int32 NumHISMs = 0;
int32 NumInstancesTotal = 0;
TArray<int32> NumInstancesPerISM;
PRAGMA_DISABLE_DEPRECATION_WARNINGS
NumInstancesPerISM.Reserve(ISMPoolComponent->Pool.ISMs.Num());
PRAGMA_ENABLE_DEPRECATION_WARNINGS
for (FGeometryCollectionISM const& ISM : ISMPoolComponent->Pool.ISMs)
{
if (ISM.ISMComponent != nullptr)
{
const bool bIsHISM = ISM.ISMComponent->IsA(UHierarchicalInstancedStaticMeshComponent::StaticClass());
NumISMs += bIsHISM ? 0 : 1;
NumHISMs += bIsHISM ? 1 : 0;
const int32 NumInstances = ISM.ISMComponent->GetNumRenderInstances();
NumInstancesTotal += NumInstances;
NumInstancesPerISM.Add(NumInstances);
}
}
if (NumInstancesTotal > 0)
{
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Warning, FText::FromString(*GetOwner()->GetPathName()));
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Info, FText::FromString(FString::Printf(TEXT("Num ISMS %d"), NumISMs)));
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Info, FText::FromString(FString::Printf(TEXT("Num HISMS %d"), NumHISMs)));
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Info, FText::FromString(FString::Printf(TEXT("Num Instances %d"), NumInstancesTotal)));
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Info, FText::FromString(FString::Printf(TEXT("Average Instances Per ISM %f"), (float)NumInstancesTotal / (float)(NumISMs + NumHISMs))));
NumInstancesPerISM.Sort();
for (int32 Index = 0; Index < 10; ++Index)
{
const int32 Percentile = (Index + 1) * 10;
const int32 PercentileIndex = FMath::Min(NumInstancesPerISM.Num() * (Index + 1) / 10, NumInstancesPerISM.Num() - 1);
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Info, FText::FromString(FString::Printf(TEXT("Num Instances at %d percentile: %d"), Percentile, NumInstancesPerISM[PercentileIndex])));
}
}
}
}
if ((bShowStats || bForceShowStats) && SelectedComponent)
{
TCHAR const* ISMType = SelectedComponent->IsA(UHierarchicalInstancedStaticMeshComponent::StaticClass()) ? TEXT("HISM") : TEXT("ISM");
FVector BoundsMin, BoundsMax;
SelectedComponent->GetLocalBounds(BoundsMin, BoundsMax);
const FVector BoundsSize = BoundsMax - BoundsMin;
const int32 NumInstances = SelectedComponent->GetNumRenderInstances();
const int32 NumCustomDataFloats = SelectedComponent->NumCustomDataFloats;
const int32 StartCullDistance = SelectedComponent->InstanceStartCullDistance;
const int32 EndCullDistance = SelectedComponent->InstanceEndCullDistance;
const int32 NumMaterials = SelectedComponent->GetNumMaterials();
UStaticMesh const* StaticMesh = SelectedComponent->GetStaticMesh();
const int32 NumLods = StaticMesh ? StaticMesh->GetNumLODs() : 0;
FString ISMDescription = FString::Printf(TEXT("Type=%s Count=%d Bounds=(%f., %f., %f.) CullDistance=(%d - %d) NumLods= %d NumMaterials=%d CustomDataSize=%d"),
ISMType, NumInstances, BoundsSize.X, BoundsSize.Z, BoundsSize.Z, StartCullDistance, EndCullDistance, NumLods, NumMaterials, NumCustomDataFloats);
for (int32 LodIndex = 0; LodIndex < NumLods; ++LodIndex)
{
const int32 NumVertices = StaticMesh->GetNumVertices(LodIndex);
const int32 NumTriangles = StaticMesh->GetNumTriangles(LodIndex);
ISMDescription += FString::Printf(TEXT("\nLod %d: Vertices=%d, Triangles=%d."), LodIndex, NumVertices, NumTriangles);
}
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Warning, FText::FromString(*GetOwner()->GetPathName()));
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Warning, FText::FromString(*SelectedComponent->GetName()));
OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Info, FText::FromString(ISMDescription));
}
}
#endif