From f49df6d20b4cd30b8585a710d268c8b9def6f357 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Mon, 13 Sep 2021 16:57:51 +0100 Subject: [PATCH 1/9] Working on an editor visualisation helper component --- .../Private/StevesDebugRenderSceneProxy.cpp | 49 ++++++++++++ .../Private/StevesEditorVisComponent.cpp | 52 +++++++++++++ .../Public/StevesDebugRenderSceneProxy.h | 75 +++++++++++++++++++ .../Public/StevesEditorVisComponent.h | 75 +++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 Source/StevesUEHelpers/Private/StevesDebugRenderSceneProxy.cpp create mode 100644 Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp create mode 100644 Source/StevesUEHelpers/Public/StevesDebugRenderSceneProxy.h create mode 100644 Source/StevesUEHelpers/Public/StevesEditorVisComponent.h diff --git a/Source/StevesUEHelpers/Private/StevesDebugRenderSceneProxy.cpp b/Source/StevesUEHelpers/Private/StevesDebugRenderSceneProxy.cpp new file mode 100644 index 0000000..e50a319 --- /dev/null +++ b/Source/StevesUEHelpers/Private/StevesDebugRenderSceneProxy.cpp @@ -0,0 +1,49 @@ +// Copyright 2020 Old Doorways Ltd + + +#include "StevesDebugRenderSceneProxy.h" + + +void FStevesDebugRenderSceneProxy::GetDynamicMeshElements(const TArray& 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; +} diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp new file mode 100644 index 0000000..a695383 --- /dev/null +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -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; +} + diff --git a/Source/StevesUEHelpers/Public/StevesDebugRenderSceneProxy.h b/Source/StevesUEHelpers/Public/StevesDebugRenderSceneProxy.h new file mode 100644 index 0000000..c802841 --- /dev/null +++ b/Source/StevesUEHelpers/Public/StevesDebugRenderSceneProxy.h @@ -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& 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 Circles; + TArray Arcs; +}; diff --git a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h new file mode 100644 index 0000000..7654f09 --- /dev/null +++ b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h @@ -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 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; } +}; From 44dad3b5050431b207d2edf8ddc1d6fbd78ba0db Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Mon, 13 Sep 2021 17:30:15 +0100 Subject: [PATCH 2/9] Should transform bounds to world at end --- Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index a695383..a1c9a29 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -47,6 +47,6 @@ FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalTo B = B + FBoxSphereBounds(C.Location, FVector(C.Radius), C.Radius); } - return B; + return B.TransformBy(LocalToWorld); } From 1a1b66c3e9c24c508024a1cc3426b6c9776d5537 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Mon, 13 Sep 2021 18:09:25 +0100 Subject: [PATCH 3/9] Move defaults change to constructor instead of OnRegister, fix SetIsVizualisationComponent only being available in editor builds --- .../Private/StevesEditorVisComponent.cpp | 10 ++++++---- .../StevesUEHelpers/Public/StevesEditorVisComponent.h | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index a1c9a29..18cdb72 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -4,16 +4,18 @@ #include "StevesEditorVisComponent.h" #include "StevesDebugRenderSceneProxy.h" -void UStevesEditorVisComponent::OnRegister() +UStevesEditorVisComponent::UStevesEditorVisComponent(const FObjectInitializer& ObjectInitializer) + : UPrimitiveComponent(ObjectInitializer) { - Super::OnRegister(); - // set up some constants - SetCollisionEnabled(ECollisionEnabled::NoCollision); + PrimaryComponentTick.bCanEverTick = false; SetCastShadow(false); +#if WITH_EDITORONLY_DATA SetIsVisualizationComponent(true); +#endif SetHiddenInGame(true); AlwaysLoadOnClient = false; + bIsEditorOnly = true; } diff --git a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h index 7654f09..1e25f28 100644 --- a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h +++ b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h @@ -58,7 +58,7 @@ struct FStevesEditorVisCircle * 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)) +UCLASS(ClassGroup="Debug", hidecategories=(Collision,Physics,Object,LOD,Lighting,TextureStreaming), meta=(DisplayName="Steves Editor Visualisation", BlueprintSpawnableComponent)) class STEVESUEHELPERS_API UStevesEditorVisComponent : public UPrimitiveComponent { GENERATED_BODY() @@ -67,7 +67,8 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Circles; - virtual void OnRegister() override; + UStevesEditorVisComponent(const FObjectInitializer& ObjectInitializer); + virtual FPrimitiveSceneProxy* CreateSceneProxy() override; virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; /// Needs to update on transform since proxy is detached From 94a783e0d4d654b25c1085f33421c43f1a77b7ea Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 14 Sep 2021 11:18:12 +0100 Subject: [PATCH 4/9] Disable visibility in reflection captures etc --- Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index 18cdb72..0e26477 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -14,6 +14,9 @@ UStevesEditorVisComponent::UStevesEditorVisComponent(const FObjectInitializer& O SetIsVisualizationComponent(true); #endif SetHiddenInGame(true); + bVisibleInReflectionCaptures = false; + bVisibleInRayTracing = false; + bVisibleInRealTimeSkyCaptures = false; AlwaysLoadOnClient = false; bIsEditorOnly = true; From 056bae4719ace5b488f290b0fc03ac420c8d4ffe Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 14 Sep 2021 11:53:23 +0100 Subject: [PATCH 5/9] Add line support to vis component --- .../Private/StevesEditorVisComponent.cpp | 11 ++++ .../Public/StevesEditorVisComponent.h | 50 ++++++++++++++++--- 2 files changed, 53 insertions(+), 8 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index 0e26477..b0e0b29 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -27,6 +27,11 @@ FPrimitiveSceneProxy* UStevesEditorVisComponent::CreateSceneProxy() auto Ret = new FStevesDebugRenderSceneProxy(this); const FTransform& XForm = GetComponentTransform(); + for (auto& L : Lines) + { + Ret->Lines.Add(FDebugRenderSceneProxy::FDebugLine(XForm.TransformPosition(L.Start), + XForm.TransformPosition(L.End), L.Colour)); + } for (auto& C : Circles) { FQuat WorldRot = XForm.TransformRotation(C.Rotation.Quaternion()); @@ -47,6 +52,12 @@ FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalTo FBoxSphereBounds B = Super::CalcBounds(LocalToWorld); // Now we need to merge in all components + for (auto& L : Lines) + { + // Re-centre the origin of the line to make box extents + FVector Extents = L.Start.GetAbs().ComponentMax(L.End.GetAbs()); + B = B + FBoxSphereBounds(FVector::ZeroVector, Extents, Extents.GetMax()); + } for (auto& C : Circles) { B = B + FBoxSphereBounds(C.Location, FVector(C.Radius), C.Radius); diff --git a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h index 1e25f28..c3a923b 100644 --- a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h +++ b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h @@ -7,6 +7,37 @@ #include "StevesEditorVisComponent.generated.h" +USTRUCT(BlueprintType) +struct FStevesEditorVisLine +{ + GENERATED_BODY() + + /// Start location relative to component + UPROPERTY(EditAnywhere) + FVector Start; + /// End location relative to component + UPROPERTY(EditAnywhere) + FVector End; + /// The colour of the line render + UPROPERTY(EditAnywhere) + FColor Colour; + + FStevesEditorVisLine(const FVector& InStart, const FVector& InEnd, + const FColor& InColour) + : Start(InStart), + End(InEnd), + Colour(Colour) + { + } + + FStevesEditorVisLine(): + Start(FVector::ZeroVector), + End(FVector(100,0,0)), + Colour(FColor::White) + { + } +}; + USTRUCT(BlueprintType) struct FStevesEditorVisCircle { @@ -28,13 +59,13 @@ struct FStevesEditorVisCircle 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(const FVector& InLocation, const FRotator& InRotation, float InRadius, int InNumSegments, + const FColor& InColour) + : Location(InLocation), + Rotation(InRotation), + Radius(InRadius), + NumSegments(InNumSegments), + Colour(InColour) { } @@ -58,13 +89,16 @@ struct FStevesEditorVisCircle * 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,Physics,Object,LOD,Lighting,TextureStreaming), meta=(DisplayName="Steves Editor Visualisation", BlueprintSpawnableComponent)) +UCLASS(Blueprintable, ClassGroup="Utility", hidecategories=(Collision,Physics,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 Lines; + UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Circles; UStevesEditorVisComponent(const FObjectInitializer& ObjectInitializer); From c67a0793bb318cd796d9ba1dbbddea4ed16bf6e1 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 14 Sep 2021 12:13:12 +0100 Subject: [PATCH 6/9] Make a note of the reason why component isn't editable on level instances --- Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index b0e0b29..6c533ef 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -11,6 +11,7 @@ UStevesEditorVisComponent::UStevesEditorVisComponent(const FObjectInitializer& O PrimaryComponentTick.bCanEverTick = false; SetCastShadow(false); #if WITH_EDITORONLY_DATA + // Note: this makes this component invisible on level instances, not sure why SetIsVisualizationComponent(true); #endif SetHiddenInGame(true); From 618fc84c3272e19a9e985ca68bd63c4ca6a87a7a Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 14 Sep 2021 13:10:32 +0100 Subject: [PATCH 7/9] Add Arc support --- .../Private/StevesEditorVisComponent.cpp | 16 +++++ .../Public/StevesEditorVisComponent.h | 58 ++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index 6c533ef..013b0f5 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -43,6 +43,17 @@ FPrimitiveSceneProxy* UStevesEditorVisComponent::CreateSceneProxy() C.NumSegments, C.Colour )); } + for (auto& Arc : Arcs) + { + FQuat WorldRot = XForm.TransformRotation(Arc.Rotation.Quaternion()); + Ret->Arcs.Add(FStevesDebugRenderSceneProxy::FDebugArc( + XForm.TransformPosition(Arc.Location), + WorldRot.GetForwardVector(), WorldRot.GetRightVector(), + Arc.MinAngle, Arc.MaxAngle, + XForm.GetMaximumAxisScale() * Arc.Radius, + Arc.NumSegments, Arc.Colour + )); + } return Ret; @@ -63,6 +74,11 @@ FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalTo { B = B + FBoxSphereBounds(C.Location, FVector(C.Radius), C.Radius); } + for (auto& Arc : Arcs) + { + // Just use the entire circle for simplicity + B = B + FBoxSphereBounds(Arc.Location, FVector(Arc.Radius), Arc.Radius); + } return B.TransformBy(LocalToWorld); } diff --git a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h index c3a923b..74a805b 100644 --- a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h +++ b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h @@ -78,7 +78,59 @@ struct FStevesEditorVisCircle } }; +USTRUCT(BlueprintType) +struct FStevesEditorVisArc +{ + GENERATED_BODY() + + /// Location relative to component + UPROPERTY(EditAnywhere) + FVector Location; + /// Rotation relative to component; arcs will be rendered in the X/Y plane + UPROPERTY(EditAnywhere) + FRotator Rotation; + /// Minimum angle to render arc from + UPROPERTY(EditAnywhere) + float MinAngle; + /// Maximum angle to render arc to + UPROPERTY(EditAnywhere) + float MaxAngle; + /// 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; + + FStevesEditorVisArc(const FVector& InLocation, const FRotator& InRotation, float InMinAngle, float InMaxAngle, + float InRadius, int InNumSegments, + const FColor& InColour, float InThickness) + : Location(InLocation), + Rotation(InRotation), + MinAngle(InMinAngle), + MaxAngle(InMaxAngle), + Radius(InRadius), + NumSegments(InNumSegments), + Colour(InColour) + { + } + + FStevesEditorVisArc(): + Location(FVector::ZeroVector), + Rotation(FRotator::ZeroRotator), + MinAngle(0), + MaxAngle(180), + 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 @@ -89,17 +141,19 @@ struct FStevesEditorVisCircle * 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(Blueprintable, ClassGroup="Utility", hidecategories=(Collision,Physics,Object,LOD,Lighting,TextureStreaming), meta=(DisplayName="Steves Editor Visualisation", BlueprintSpawnableComponent)) +UCLASS(Blueprintable, ClassGroup="Utility", hidecategories=(Collision,Physics,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 Lines; UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Circles; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray Arcs; UStevesEditorVisComponent(const FObjectInitializer& ObjectInitializer); From 1b8a5a805c18ae664fd34a3fcd1da6cdc6c48004 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 14 Sep 2021 14:29:07 +0100 Subject: [PATCH 8/9] Add arrows and spheres --- .../Private/StevesEditorVisComponent.cpp | 23 ++++++++++++ .../Public/StevesEditorVisComponent.h | 36 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index 013b0f5..be9dee5 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -33,6 +33,11 @@ FPrimitiveSceneProxy* UStevesEditorVisComponent::CreateSceneProxy() Ret->Lines.Add(FDebugRenderSceneProxy::FDebugLine(XForm.TransformPosition(L.Start), XForm.TransformPosition(L.End), L.Colour)); } + for (auto& A : Arrows) + { + Ret->ArrowLines.Add(FDebugRenderSceneProxy::FArrowLine(XForm.TransformPosition(A.Start), + XForm.TransformPosition(A.End), A.Colour)); + } for (auto& C : Circles) { FQuat WorldRot = XForm.TransformRotation(C.Rotation.Quaternion()); @@ -54,6 +59,14 @@ FPrimitiveSceneProxy* UStevesEditorVisComponent::CreateSceneProxy() Arc.NumSegments, Arc.Colour )); } + for (auto& S : Spheres) + { + Ret->Spheres.Add(FStevesDebugRenderSceneProxy::FSphere( + XForm.GetMaximumAxisScale() * S.Radius, + XForm.TransformPosition(S.Location), + S.Colour + )); + } return Ret; @@ -70,6 +83,12 @@ FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalTo FVector Extents = L.Start.GetAbs().ComponentMax(L.End.GetAbs()); B = B + FBoxSphereBounds(FVector::ZeroVector, Extents, Extents.GetMax()); } + for (auto& A : Arrows) + { + // Re-centre the origin of the line to make box extents + FVector Extents = A.Start.GetAbs().ComponentMax(A.End.GetAbs()); + B = B + FBoxSphereBounds(FVector::ZeroVector, Extents, Extents.GetMax()); + } for (auto& C : Circles) { B = B + FBoxSphereBounds(C.Location, FVector(C.Radius), C.Radius); @@ -79,6 +98,10 @@ FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalTo // Just use the entire circle for simplicity B = B + FBoxSphereBounds(Arc.Location, FVector(Arc.Radius), Arc.Radius); } + for (auto& S : Spheres) + { + B = B + FBoxSphereBounds(S.Location, FVector(S.Radius), S.Radius); + } return B.TransformBy(LocalToWorld); } diff --git a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h index 74a805b..c674020 100644 --- a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h +++ b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h @@ -129,6 +129,38 @@ struct FStevesEditorVisArc } }; +USTRUCT(BlueprintType) +struct FStevesEditorVisSphere +{ + GENERATED_BODY() + + /// Location relative to component + UPROPERTY(EditAnywhere) + FVector Location; + /// Sphere radius + UPROPERTY(EditAnywhere) + float Radius; + /// The colour of the line render + UPROPERTY(EditAnywhere) + FColor Colour; + + FStevesEditorVisSphere(const FVector& InLocation, float InRadius, const FColor& InColour) : + Location(InLocation), + Radius(InRadius), + Colour(InColour) + { + } + + FStevesEditorVisSphere(): + Location(FVector::ZeroVector), + Radius(50), + Colour(FColor::White) + { + } +}; + + + /** * * A component that solely exists to provide arbitrary editor visualisation when not selected. @@ -151,9 +183,13 @@ public: UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Lines; UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray Arrows; + UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Circles; UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Arcs; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray Spheres; UStevesEditorVisComponent(const FObjectInitializer& ObjectInitializer); From 266c8cc1b856e46cc293680a3309caf463df65f9 Mon Sep 17 00:00:00 2001 From: Steve Streeting Date: Tue, 14 Sep 2021 14:48:39 +0100 Subject: [PATCH 9/9] Add boxes --- .../Private/StevesEditorVisComponent.cpp | 19 ++++++- .../Public/StevesEditorVisComponent.h | 49 ++++++++++++++++--- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp index be9dee5..3c0ee83 100644 --- a/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp +++ b/Source/StevesUEHelpers/Private/StevesEditorVisComponent.cpp @@ -67,6 +67,15 @@ FPrimitiveSceneProxy* UStevesEditorVisComponent::CreateSceneProxy() S.Colour )); } + for (auto& Box : Boxes) + { + FVector HalfSize = Box.Size * 0.5f; + FBox DBox(-HalfSize, HalfSize); + // Apply local rotation first then parent transform + FTransform CombinedXForm = FTransform(Box.Rotation, Box.Location) * XForm; + Ret->Boxes.Add(FStevesDebugRenderSceneProxy::FDebugBox( + DBox, Box.Colour, CombinedXForm)); + } return Ret; @@ -102,7 +111,15 @@ FBoxSphereBounds UStevesEditorVisComponent::CalcBounds(const FTransform& LocalTo { B = B + FBoxSphereBounds(S.Location, FVector(S.Radius), S.Radius); } - + for (auto& Box : Boxes) + { + FVector HalfSize = Box.Size * 0.5f; + FBox DBox(-HalfSize, HalfSize); + // Apply local rotation only, world is done later + FTransform BoxXForm = FTransform(Box.Rotation, Box.Location); + DBox = DBox.TransformBy(BoxXForm); + B = B + FBoxSphereBounds(DBox); + } return B.TransformBy(LocalToWorld); } diff --git a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h index c674020..297863a 100644 --- a/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h +++ b/Source/StevesUEHelpers/Public/StevesEditorVisComponent.h @@ -22,17 +22,17 @@ struct FStevesEditorVisLine UPROPERTY(EditAnywhere) FColor Colour; - FStevesEditorVisLine(const FVector& InStart, const FVector& InEnd, - const FColor& InColour) + FStevesEditorVisLine(const FVector& InStart, const FVector& InEnd, + const FColor& InColour) : Start(InStart), - End(InEnd), - Colour(Colour) + End(InEnd), + Colour(Colour) { } FStevesEditorVisLine(): Start(FVector::ZeroVector), - End(FVector(100,0,0)), + End(FVector(100, 0, 0)), Colour(FColor::White) { } @@ -60,7 +60,7 @@ struct FStevesEditorVisCircle FColor Colour; FStevesEditorVisCircle(const FVector& InLocation, const FRotator& InRotation, float InRadius, int InNumSegments, - const FColor& InColour) + const FColor& InColour) : Location(InLocation), Rotation(InRotation), Radius(InRadius), @@ -159,6 +159,41 @@ struct FStevesEditorVisSphere } }; +USTRUCT(BlueprintType) +struct FStevesEditorVisBox +{ + GENERATED_BODY() + + /// Location relative to component + UPROPERTY(EditAnywhere) + FVector Location; + /// Size of box in each axis + UPROPERTY(EditAnywhere) + FVector Size; + /// Rotation relative to component + UPROPERTY(EditAnywhere) + FRotator Rotation; + /// The colour of the line render + UPROPERTY(EditAnywhere) + FColor Colour; + + FStevesEditorVisBox(const FVector& InLocation, const FVector& InSize, const FRotator& InRot, + const FColor& InColour) : + Location(InLocation), + Size(InSize), + Rotation(InRot), + Colour(InColour) + { + } + + FStevesEditorVisBox(): + Location(FVector::ZeroVector), + Size(FVector(50, 50, 50)), + Rotation(FRotator::ZeroRotator), + Colour(FColor::White) + { + } +}; /** @@ -190,6 +225,8 @@ public: TArray Arcs; UPROPERTY(EditAnywhere, BlueprintReadWrite) TArray Spheres; + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray Boxes; UStevesEditorVisComponent(const FObjectInitializer& ObjectInitializer);