Working on an editor visualisation helper component

This commit is contained in:
Steve Streeting 2021-09-13 16:57:51 +01:00
parent 1ef851bce4
commit f49df6d20b
4 changed files with 251 additions and 0 deletions

View File

@ -0,0 +1,49 @@
// Copyright 2020 Old Doorways Ltd
#include "StevesDebugRenderSceneProxy.h"
void FStevesDebugRenderSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views,
const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const
{
FDebugRenderSceneProxy::GetDynamicMeshElements(Views, ViewFamily, VisibilityMap, Collector);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
// Draw Circles
for (const auto& C : Circles)
{
DrawCircle(PDI, C.Centre, C.X, C.Y, C.Color, C.Radius, C.NumSegments, SDPG_World, C.Thickness, 0, C.Thickness > 0);
}
// Draw Arcs
for (const auto& C : Arcs)
{
::DrawArc(PDI,
C.Centre,
C.X, C.Y,
C.MinAngle, C.MaxAngle,
C.Radius, C.NumSegments,
C.Color, SDPG_Foreground);
}
}
}
}
FPrimitiveViewRelevance FStevesDebugRenderSceneProxy::GetViewRelevance(const FSceneView* View) const
{
// More useful defaults than FDebugRenderSceneProxy
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View);
Result.bDynamicRelevance = true;
Result.bShadowRelevance = false;
Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
return Result;
}

View File

@ -0,0 +1,52 @@
// Copyright 2020 Old Doorways Ltd
#include "StevesEditorVisComponent.h"
#include "StevesDebugRenderSceneProxy.h"
void UStevesEditorVisComponent::OnRegister()
{
Super::OnRegister();
// set up some constants
SetCollisionEnabled(ECollisionEnabled::NoCollision);
SetCastShadow(false);
SetIsVisualizationComponent(true);
SetHiddenInGame(true);
AlwaysLoadOnClient = false;
}
FPrimitiveSceneProxy* UStevesEditorVisComponent::CreateSceneProxy()
{
auto Ret = new FStevesDebugRenderSceneProxy(this);
const FTransform& XForm = GetComponentTransform();
for (auto& C : Circles)
{
FQuat WorldRot = XForm.TransformRotation(C.Rotation.Quaternion());
Ret->Circles.Add(FStevesDebugRenderSceneProxy::FDebugCircle(
XForm.TransformPosition(C.Location),
WorldRot.GetForwardVector(), WorldRot.GetRightVector(),
XForm.GetMaximumAxisScale() * C.Radius,
C.NumSegments, C.Colour
));
}
return Ret;
}
FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalToWorld) const
{
FBoxSphereBounds B = Super::CalcBounds(LocalToWorld);
// Now we need to merge in all components
for (auto& C : Circles)
{
B = B + FBoxSphereBounds(C.Location, FVector(C.Radius), C.Radius);
}
return B;
}

View File

@ -0,0 +1,75 @@
// Copyright 2020 Old Doorways Ltd
#pragma once
#include "CoreMinimal.h"
#include "DebugRenderSceneProxy.h"
/**
* An extension to FDebugRenderSceneProxy to support other shapes, e.g. circles and arcs
*/
class FStevesDebugRenderSceneProxy : public FDebugRenderSceneProxy
{
public:
FStevesDebugRenderSceneProxy(const UPrimitiveComponent* InComponent)
: FDebugRenderSceneProxy(InComponent)
{
}
STEVESUEHELPERS_API virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily,
uint32 VisibilityMap, FMeshElementCollector& Collector) const override;
STEVESUEHELPERS_API virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override;
struct FDebugCircle
{
FDebugCircle(const FVector& InCentre, const FVector& InX, const FVector& InY, float InRadius, int InNumSegments,
const FColor& InColor, float InThickness = 0) :
Centre(InCentre),
X(InX),
Y(InY),
Radius(InRadius),
NumSegments(InNumSegments),
Color(InColor),
Thickness(InThickness)
{
}
FVector Centre;
FVector X;
FVector Y;
float Radius;
int NumSegments;
FColor Color;
float Thickness;
};
/// An arc which is a section of a circle
struct FDebugArc
{
FDebugArc(const FVector& InCentre, const FVector& InX, const FVector& InY, float InMinAngle, float InMaxAngle,
float InRadius, int InNumSegments, const FColor& InColor) :
Centre(InCentre),
X(InX),
Y(InY),
MinAngle(InMinAngle),
MaxAngle(InMaxAngle),
Radius(InRadius),
NumSegments(InNumSegments),
Color(InColor)
{
}
FVector Centre;
FVector X;
FVector Y;
float MinAngle;
float MaxAngle;
float Radius;
int NumSegments;
FColor Color;
};
TArray<FDebugCircle> Circles;
TArray<FDebugArc> Arcs;
};

View File

@ -0,0 +1,75 @@
// Copyright 2020 Old Doorways Ltd
#pragma once
#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "StevesEditorVisComponent.generated.h"
USTRUCT(BlueprintType)
struct FStevesEditorVisCircle
{
GENERATED_BODY()
/// Location relative to component
UPROPERTY(EditAnywhere)
FVector Location;
/// Rotation relative to component; circles will be rendered in the X/Y plane
UPROPERTY(EditAnywhere)
FRotator Rotation;
/// Circle radius
UPROPERTY(EditAnywhere)
float Radius;
/// The number of line segments to render the circle with
UPROPERTY(EditAnywhere)
int NumSegments;
/// The colour of the line render
UPROPERTY(EditAnywhere)
FColor Colour;
FStevesEditorVisCircle(const FVector& Location, const FRotator& Rotation, float Radius, int NumSegments,
const FColor& Colour)
: Location(Location),
Rotation(Rotation),
Radius(Radius),
NumSegments(NumSegments),
Colour(Colour)
{
}
FStevesEditorVisCircle():
Location(FVector::ZeroVector),
Rotation(FRotator::ZeroRotator),
Radius(50), NumSegments(12),
Colour(FColor::White)
{
}
};
/**
* A component that solely exists to provide arbitrary editor visualisation when not selected.
* FComponentVisualizer can only display visualisation when selected.
* To display vis on an unselected object, you need a UPrimitiveComponent, and sometimes you don't want/need one of those
* in your actor. Instead, add UStevesEditorVisComponent at construction of your actor, or registration of another component,
* but only in a WITH_EDITOR block. Then, get nice visualisation of your actor/component without making more invasive changes
* to your code.
*
* If you want to, you can add this to your Blueprints too. This component automatically marks itself as "visualisation
* only" so shouldn't have a runtime impact.
*/
UCLASS(ClassGroup="Debug", hidecategories=(Collision,Object,LOD,Lighting,TextureStreaming), meta=(DisplayName="Steves Editor Visualisation", BlueprintSpawnableComponent))
class STEVESUEHELPERS_API UStevesEditorVisComponent : public UPrimitiveComponent
{
GENERATED_BODY()
public:
/// Circles to render
UPROPERTY(EditAnywhere, BlueprintReadWrite)
TArray<FStevesEditorVisCircle> Circles;
virtual void OnRegister() override;
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override;
/// Needs to update on transform since proxy is detached
virtual bool ShouldRecreateProxyOnUpdateTransform() const override { return true; }
};