Files
UnrealEngineUWP/Engine/Source/Developer/LogVisualizer/Private/VisualLoggerRenderingActorBase.cpp
yoan stamant 42299b520f [DebugDrawDelegateHelper] rollback of delegate unregistration issue previous fix and implemented a different approach for cases where proxy is not created:
In some code paths for loaded actors the call order might be different since primitive registration gets deferred (i.e. FRegisterComponentContext != nullptr).

Case 1 (normal flow): FRegisterComponentContext == nullptr
 > UPrimitiveComponent::CreateRenderState_Concurrent : calls CreateSceneProxy
 > RegisterDebugDrawDelegate
 > UnregisterDebugDrawDelegate  ==> works fine

Case 2 (deferred AddPrimitive): FRegisterComponentContext != nullptr
 > UPrimitiveComponent::CreateRenderState_Concurrent : defers CreateSceneProxy
 > RegisterDebugDrawDelegate (skip register since not init)
 > CreateSceneProxy
 > UnregisterDebugDrawDelegate  ==>  ensures in UnregisterDebugDrawDelegate

With this new version `CreateRenderState_Concurrent` calls `RequestRegisterDebugDrawDelegate` that will take care of registering the delegate immediately or mark it for deferred execution. An explicit call to `ProcessDeferredRegister` is then required from `CreateSceneProxy` to implement proper flow:

Case 2 (fixed) (deferred AddPrimitive + deferred Register): FRegisterComponentContext != nullptr
 > UPrimitiveComponent::CreateRenderState_Concurrent: defers CreateSceneProxy
 > RequestRegisterDebugDrawDelegate: defers RegisterDebugDrawDelegate
 > CreateSceneProxy: calls ProcessDeferredRegister & RegisterDebugDrawDelegate
 > UnregisterDebugDrawDelegate  ==> works fine

Bonus:
- New DebugDrawComponent abstract class that is now used to share common functionalities between existing debug related components. Takes care of DelegateHelper registration flow to render text on screen from different sources.
- FDebugDrawDelegateHelper::InitDelegateHelper is no longer virtual and derived classes requiring extra data from their associated scene proxy should set it up from their overriden CreateSceneProxy
- FDebugDrawDelegateHelper derived classes should not override RegisterDebugDrawDelegate/UnregisterDebugDrawDelegate to use their `DrawDebugLabels` method since this is already a virtual method that would get called from the base class.
- Fixed a few ViewFlag members in the SceneProxy so the DelegateHelper base class can behave as expected

#jira FORT-419154
#rb mieszko.zielinski
#preflight 61703f8766ed7f0001c0faf1

#ROBOMERGE-AUTHOR: yoan.stamant
#ROBOMERGE-COMMAND: FnMain
#ROBOMERGE-SOURCE: CL 17875336 in //UE5/Main/...
#ROBOMERGE-BOT: STARSHIP (Main -> Release-Engine-Test) (v883-17842818)
#ROBOMERGE[STARSHIP]: UE5-Release-Engine-Staging Release-5.0
#ROBOMERGE[bot1]: Main

[CL 17875370 by yoan stamant in ue5-release-engine-test branch]
2021-10-20 13:23:33 -04:00

523 lines
18 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "VisualLoggerRenderingActorBase.h"
#include "LogVisualizerSettings.h"
#if WITH_EDITOR
#include "GeomTools.h"
#endif // WITH_EDITOR
#include "VisualLoggerRenderingComponent.h"
#include "VisualLogger/VisualLogger.h"
namespace FDebugDrawing
{
const FVector NavOffset(0, 0, 15);
}
class UVisualLoggerRenderingComponent;
class FVisualLoggerSceneProxy final : public FDebugRenderSceneProxy
{
public:
virtual SIZE_T GetTypeHash() const override
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
FVisualLoggerSceneProxy(const UVisualLoggerRenderingComponent* InComponent)
: FDebugRenderSceneProxy(InComponent)
{
DrawType = SolidAndWireMeshes;
ViewFlagName = TEXT("VisLog");
ViewFlagIndex = uint32(FEngineShowFlags::FindIndexByName(*ViewFlagName));
bWantsSelectionOutline = false;
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View);
Result.bDynamicRelevance = true;
// ideally the TranslucencyRelevance should be filled out by the material, here we do it conservative
Result.bSeparateTranslucency = Result.bNormalTranslucency = IsShown(View) && GIsEditor;
return Result;
}
virtual uint32 GetMemoryFootprint(void) const override { return sizeof(*this) + GetAllocatedSize(); }
};
UVisualLoggerRenderingComponent::UVisualLoggerRenderingComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
}
FDebugRenderSceneProxy* UVisualLoggerRenderingComponent::CreateDebugSceneProxy()
{
AVisualLoggerRenderingActorBase* RenderingActor = Cast<AVisualLoggerRenderingActorBase>(GetOuter());
if (RenderingActor == nullptr)
{
return nullptr;
}
ULogVisualizerSettings *Settings = ULogVisualizerSettings::StaticClass()->GetDefaultObject<ULogVisualizerSettings>();
FVisualLoggerSceneProxy *VLogSceneProxy = new FVisualLoggerSceneProxy(this);
VLogSceneProxy->SolidMeshMaterial = Settings->GetDebugMeshMaterial();
RenderingActor->IterateDebugShapes([VLogSceneProxy] (const AVisualLoggerRenderingActorBase::FTimelineDebugShapes& Shapes)
{
VLogSceneProxy->Spheres.Append(Shapes.Points);
VLogSceneProxy->Lines.Append(Shapes.Lines);
VLogSceneProxy->Boxes.Append(Shapes.Boxes);
VLogSceneProxy->Meshes.Append(Shapes.Meshes);
VLogSceneProxy->Cones.Append(Shapes.Cones);
VLogSceneProxy->Texts.Append(Shapes.Texts);
VLogSceneProxy->Cylinders.Append(Shapes.Cylinders);
VLogSceneProxy->ArrowLines.Append(Shapes.Arrows);
VLogSceneProxy->Capsules.Append(Shapes.Capsules);
});
return VLogSceneProxy;
}
FBoxSphereBounds UVisualLoggerRenderingComponent::CalcBounds(const FTransform& LocalToWorld) const
{
FBox MyBounds;
MyBounds.Init();
MyBounds = FBox(FVector(-HALF_WORLD_MAX, -HALF_WORLD_MAX, -HALF_WORLD_MAX), FVector(HALF_WORLD_MAX, HALF_WORLD_MAX, HALF_WORLD_MAX));
return MyBounds;
}
AVisualLoggerRenderingActorBase::AVisualLoggerRenderingActorBase(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
USceneComponent* SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneComp"));
RootComponent = SceneComponent;
RenderingComponent = CreateDefaultSubobject<UVisualLoggerRenderingComponent>(TEXT("RenderingComponent"));
}
AVisualLoggerRenderingActorBase::~AVisualLoggerRenderingActorBase()
{
}
namespace
{
static bool IsPolygonWindingCorrect(const TArray<FVector>& Verts)
{
// this will work only for convex polys, but we're assuming that all logged polygons are convex in the first place
if (Verts.Num() >= 3)
{
const FVector SurfaceNormal = FVector::CrossProduct(Verts[1] - Verts[0], Verts[2] - Verts[0]);
const float TestDot = FVector::DotProduct(SurfaceNormal, FVector(0, 0, 1));
return TestDot > 0;
}
return false;
}
static void GetPolygonMesh(const FVisualLogShapeElement* ElementToDraw, FDebugRenderSceneProxy::FMesh& TestMesh, const FVector3f& VertexOffset = FVector3f::ZeroVector)
{
TestMesh.Color = ElementToDraw->GetFColor();
FClipSMPolygon InPoly(ElementToDraw->Points.Num());
InPoly.FaceNormal = FVector(0, 0, 1);
const bool bHasCorrectWinding = IsPolygonWindingCorrect(ElementToDraw->Points);
if (bHasCorrectWinding)
{
for (int32 Index = 0; Index < ElementToDraw->Points.Num(); Index++)
{
FClipSMVertex v1;
v1.Pos = ElementToDraw->Points[Index];
InPoly.Vertices.Add(v1);
}
}
else
{
for (int32 Index = ElementToDraw->Points.Num() - 1; Index >= 0; Index--)
{
FClipSMVertex v1;
v1.Pos = ElementToDraw->Points[Index];
InPoly.Vertices.Add(v1);
}
}
TArray<FClipSMTriangle> OutTris;
const bool bTriangulated = FGeomTools::TriangulatePoly(OutTris, InPoly, false);
if (bTriangulated)
{
int32 LastIndex = 0;
FGeomTools::RemoveRedundantTriangles(OutTris);
for (const auto& CurrentTri : OutTris)
{
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentTri.Vertices[0].Pos + VertexOffset));
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentTri.Vertices[1].Pos + VertexOffset));
TestMesh.Vertices.Add(FDynamicMeshVertex(CurrentTri.Vertices[2].Pos + VertexOffset));
TestMesh.Indices.Add(LastIndex++);
TestMesh.Indices.Add(LastIndex++);
TestMesh.Indices.Add(LastIndex++);
}
}
}
}
void AVisualLoggerRenderingActorBase::GetDebugShapes(const FVisualLogEntry& InEntry, bool bAddEntryLocationPointer, AVisualLoggerRenderingActorBase::FTimelineDebugShapes& DebugShapes)
{
const FVisualLogEntry* Entry = &InEntry;
const FVisualLogShapeElement* ElementToDraw = Entry->ElementsToDraw.GetData();
const int32 ElementsCount = Entry->ElementsToDraw.Num();
if (bAddEntryLocationPointer)
{
constexpr float Length = 100;
const FVector DirectionNorm = FVector(0, 0, 1).GetSafeNormal();
FVector YAxis, ZAxis;
DirectionNorm.FindBestAxisVectors(YAxis, ZAxis);
DebugShapes.Cones.Add(FDebugRenderSceneProxy::FCone(FScaleMatrix(FVector(Length)) * FMatrix(DirectionNorm, YAxis, ZAxis, Entry->Location), 5, 5, FColor::Red));
}
if (DebugShapes.LogEntriesPath.Num())
{
FVector Location = DebugShapes.LogEntriesPath[0];
for (int32 Index = 1; Index < DebugShapes.LogEntriesPath.Num(); ++Index)
{
const FVector CurrentLocation = DebugShapes.LogEntriesPath[Index];
DebugShapes.Lines.Add(FDebugRenderSceneProxy::FDebugLine(Location, CurrentLocation, FColor(160, 160, 240), 2.0));
Location = CurrentLocation;
}
}
for (int32 ElementIndex = 0; ElementIndex < ElementsCount; ++ElementIndex, ++ElementToDraw)
{
if (!FVisualLoggerFilters::Get().MatchCategoryFilters(ElementToDraw->Category.ToString(), ElementToDraw->Verbosity))
{
continue;
}
const FVector3f CorridorOffset = FDebugDrawing::NavOffset * 1.25f;
const FColor Color = ElementToDraw->GetFColor();
switch (ElementToDraw->GetType())
{
case EVisualLoggerShapeElement::SinglePoint:
{
const float Radius = float(ElementToDraw->Radius);
const bool bDrawLabel = (ElementToDraw->Description.IsEmpty() == false);
const int32 NumPoints = ElementToDraw->Points.Num();
for (int32 Index = 0; Index < NumPoints; ++Index)
{
const FVector& Point = ElementToDraw->Points[Index];
DebugShapes.Points.Add(FDebugRenderSceneProxy::FSphere(Radius, Point, Color));
if (bDrawLabel)
{
const FString PrintString = NumPoints == 1 ? ElementToDraw->Description : FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index);
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(PrintString, Point, Color));
}
}
}
break;
case EVisualLoggerShapeElement::Polygon:
{
FDebugRenderSceneProxy::FMesh TestMesh;
GetPolygonMesh(ElementToDraw, TestMesh, CorridorOffset);
DebugShapes.Meshes.Add(TestMesh);
for (int32 VIdx = 0; VIdx < ElementToDraw->Points.Num(); VIdx++)
{
DebugShapes.Lines.Add(FDebugRenderSceneProxy::FDebugLine(
ElementToDraw->Points[VIdx] + CorridorOffset,
ElementToDraw->Points[(VIdx + 1) % ElementToDraw->Points.Num()] + CorridorOffset,
FColor::Cyan,
2)
);
}
}
break;
case EVisualLoggerShapeElement::Mesh:
{
struct FHeaderData
{
float VerticesNum, FacesNum;
FHeaderData(const FVector& InVector) : VerticesNum(InVector.X), FacesNum(InVector.Y) {}
};
const FHeaderData HeaderData(ElementToDraw->Points[0]);
FDebugRenderSceneProxy::FMesh TestMesh;
TestMesh.Color = ElementToDraw->GetFColor();
int32 StartIndex = 1;
int32 EndIndex = StartIndex + HeaderData.VerticesNum;
for (int32 VIdx = StartIndex; VIdx < EndIndex; VIdx++)
{
TestMesh.Vertices.Add(FVector3f(ElementToDraw->Points[VIdx]));
}
StartIndex = EndIndex;
EndIndex = StartIndex + HeaderData.FacesNum;
for (int32 VIdx = StartIndex; VIdx < EndIndex; VIdx++)
{
const FVector &CurrentFace = ElementToDraw->Points[VIdx];
TestMesh.Indices.Add(CurrentFace.X);
TestMesh.Indices.Add(CurrentFace.Y);
TestMesh.Indices.Add(CurrentFace.Z);
}
DebugShapes.Meshes.Add(TestMesh);
}
break;
case EVisualLoggerShapeElement::Segment:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = (ElementToDraw->Description.IsEmpty() == false);
const FVector* Location = ElementToDraw->Points.GetData();
const int32 NumPoints = ElementToDraw->Points.Num();
for (int32 Index = 0; Index + 1 < NumPoints; Index += 2, Location += 2)
{
DebugShapes.Lines.Add(FDebugRenderSceneProxy::FDebugLine(*Location, *(Location + 1), Color, Thickness));
if (bDrawLabel)
{
const FString PrintString = NumPoints == 2 ? ElementToDraw->Description : FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index / 2);
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(PrintString, (*Location + (*(Location + 1) - *Location) / 2), Color));
}
}
}
break;
case EVisualLoggerShapeElement::Path:
{
const float Thickness = float(ElementToDraw->Thicknes);
FVector Location = ElementToDraw->Points[0];
for (int32 Index = 1; Index < ElementToDraw->Points.Num(); ++Index)
{
const FVector CurrentLocation = ElementToDraw->Points[Index];
DebugShapes.Lines.Add(FDebugRenderSceneProxy::FDebugLine(Location, CurrentLocation, Color, Thickness));
Location = CurrentLocation;
}
}
break;
case EVisualLoggerShapeElement::Box:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = (ElementToDraw->Description.IsEmpty() == false);
const FVector* BoxExtent = ElementToDraw->Points.GetData();
const int32 NumPoints = ElementToDraw->Points.Num();
for (int32 Index = 0; Index + 1 < NumPoints; Index += 2, BoxExtent += 2)
{
const FBox Box = FBox(*BoxExtent, *(BoxExtent + 1));
DebugShapes.Boxes.Add(FDebugRenderSceneProxy::FDebugBox(Box, Color, FTransform(ElementToDraw->TransformationMatrix)));
if (bDrawLabel)
{
const FString PrintString = NumPoints == 2 ? ElementToDraw->Description : FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index / 2);
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(PrintString, Box.GetCenter(), Color));
}
}
}
break;
case EVisualLoggerShapeElement::Cone:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = ElementToDraw->Description.IsEmpty() == false;
for (int32 Index = 0; Index + 2 < ElementToDraw->Points.Num(); Index += 3)
{
const FVector Origin = ElementToDraw->Points[Index];
const FVector Direction = ElementToDraw->Points[Index + 1].GetSafeNormal();
const FVector Angles = ElementToDraw->Points[Index + 2];
const float Length = Angles.X;
FVector YAxis, ZAxis;
Direction.FindBestAxisVectors(YAxis, ZAxis);
DebugShapes.Cones.Add(FDebugRenderSceneProxy::FCone(FScaleMatrix(FVector(Length)) * FMatrix(Direction, YAxis, ZAxis, Origin), Angles.Y, Angles.Z, Color));
if (bDrawLabel)
{
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(ElementToDraw->Description, Origin, Color));
}
}
}
break;
case EVisualLoggerShapeElement::Cylinder:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = ElementToDraw->Description.IsEmpty() == false;
for (int32 Index = 0; Index + 2 < ElementToDraw->Points.Num(); Index += 3)
{
const FVector Start = ElementToDraw->Points[Index];
const FVector End = ElementToDraw->Points[Index + 1];
const FVector OtherData = ElementToDraw->Points[Index + 2];
const float HalfHeight = 0.5f * (End - Start).Size();
const FVector Center = 0.5f * (Start + End);
DebugShapes.Cylinders.Add(FDebugRenderSceneProxy::FWireCylinder(Center, OtherData.X, HalfHeight, Color)); // Base parameter is the center of the cylinder
if (bDrawLabel)
{
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(ElementToDraw->Description, Center, Color));
}
}
}
break;
case EVisualLoggerShapeElement::Capsule:
{
const float Thickness = float(ElementToDraw->Thicknes);
const bool bDrawLabel = ElementToDraw->Description.IsEmpty() == false;
for (int32 Index = 0; Index + 2 < ElementToDraw->Points.Num(); Index += 3)
{
const FVector Base = ElementToDraw->Points[Index + 0];
const FVector FirstData = ElementToDraw->Points[Index + 1];
const FVector SecondData = ElementToDraw->Points[Index + 2];
const float HalfHeight = FirstData.X;
const float Radius = FirstData.Y;
const FQuat Rotation = FQuat(FirstData.Z, SecondData.X, SecondData.Y, SecondData.Z);
const FMatrix Axes = FQuatRotationTranslationMatrix(Rotation, FVector::ZeroVector);
const FVector XAxis = Axes.GetScaledAxis(EAxis::X);
const FVector YAxis = Axes.GetScaledAxis(EAxis::Y);
const FVector ZAxis = Axes.GetScaledAxis(EAxis::Z);
DebugShapes.Capsules.Add(FDebugRenderSceneProxy::FCapsule(Base, Radius, XAxis, YAxis, ZAxis, HalfHeight, Color));
if (bDrawLabel)
{
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(ElementToDraw->Description, Base + HalfHeight * FVector::UpVector, Color));
}
}
}
break;
case EVisualLoggerShapeElement::NavAreaMesh:
{
if (ElementToDraw->Points.Num() == 0)
{
continue;
}
struct FHeaderData
{
float MinZ, MaxZ;
FHeaderData(const FVector& InVector) : MinZ(InVector.X), MaxZ(InVector.Y) {}
};
const FHeaderData HeaderData(ElementToDraw->Points[0]);
TArray<FVector> AreaMeshPoints = ElementToDraw->Points;
AreaMeshPoints.RemoveAt(0, 1, false);
AreaMeshPoints.Add(ElementToDraw->Points[1]);
TArray<FVector> Vertices;
TNavStatArray<FVector> Faces;
int32 CurrentIndex = 0;
FDebugRenderSceneProxy::FMesh TestMesh;
TestMesh.Color = ElementToDraw->GetFColor();
for (int32 PointIndex = 0; PointIndex < AreaMeshPoints.Num() - 1; PointIndex++)
{
FVector Point = AreaMeshPoints[PointIndex];
FVector NextPoint = AreaMeshPoints[PointIndex + 1];
FVector3f P1(Point.X, Point.Y, HeaderData.MinZ);
FVector3f P2(Point.X, Point.Y, HeaderData.MaxZ);
FVector3f P3(NextPoint.X, NextPoint.Y, HeaderData.MinZ);
FVector3f P4(NextPoint.X, NextPoint.Y, HeaderData.MaxZ);
TestMesh.Vertices.Add(P1); TestMesh.Vertices.Add(P2); TestMesh.Vertices.Add(P3);
TestMesh.Indices.Add(CurrentIndex + 0);
TestMesh.Indices.Add(CurrentIndex + 1);
TestMesh.Indices.Add(CurrentIndex + 2);
CurrentIndex += 3;
TestMesh.Vertices.Add(P3); TestMesh.Vertices.Add(P2); TestMesh.Vertices.Add(P4);
TestMesh.Indices.Add(CurrentIndex + 0);
TestMesh.Indices.Add(CurrentIndex + 1);
TestMesh.Indices.Add(CurrentIndex + 2);
CurrentIndex += 3;
}
DebugShapes.Meshes.Add(TestMesh);
{
FDebugRenderSceneProxy::FMesh PolygonMesh;
FVisualLogShapeElement PolygonToDraw(EVisualLoggerShapeElement::Polygon);
PolygonToDraw.SetColor(ElementToDraw->GetFColor());
PolygonToDraw.Points.Reserve(AreaMeshPoints.Num());
PolygonToDraw.Points = AreaMeshPoints;
GetPolygonMesh(&PolygonToDraw, PolygonMesh, FVector3f(0, 0, HeaderData.MaxZ));
DebugShapes.Meshes.Add(PolygonMesh);
}
for (int32 VIdx = 0; VIdx < AreaMeshPoints.Num(); VIdx++)
{
DebugShapes.Lines.Add(FDebugRenderSceneProxy::FDebugLine(
AreaMeshPoints[VIdx] + FVector(0, 0, HeaderData.MaxZ),
AreaMeshPoints[(VIdx + 1) % AreaMeshPoints.Num()] + FVector(0, 0, HeaderData.MaxZ),
ElementToDraw->GetFColor(),
2)
);
}
}
break;
case EVisualLoggerShapeElement::Arrow:
{
const bool bDrawLabel = (ElementToDraw->Description.IsEmpty() == false);
const FVector* Location = ElementToDraw->Points.GetData();
const int32 NumPoints = ElementToDraw->Points.Num();
for (int32 Index = 0; Index + 1 < NumPoints; Index += 2, Location += 2)
{
DebugShapes.Arrows.Add(FDebugRenderSceneProxy::FArrowLine(*Location, *(Location + 1), Color));
if (bDrawLabel)
{
const FString PrintString = NumPoints == 2 ? ElementToDraw->Description : FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index / 2);
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(PrintString, (*Location + (*(Location + 1) - *Location) / 2), Color));
}
}
}
break;
case EVisualLoggerShapeElement::Circle:
{
const bool bDrawLabel = (ElementToDraw->Description.IsEmpty() == false);
const int32 NumPoints = ElementToDraw->Points.Num();
for (int32 Index = 0; Index + 2 < NumPoints; Index += 3)
{
const FVector Center = ElementToDraw->Points[Index + 0];
const FVector UpAxis = ElementToDraw->Points[Index + 1];
const float Radius = ElementToDraw->Points[Index + 2].X;
const float Thickness = float(ElementToDraw->Thicknes);
const FQuat Rotation = FQuat::FindBetweenNormals(FVector::UpVector, UpAxis);
const FVector XAxis = Rotation.RotateVector(FVector::XAxisVector);
const FVector YAxis = Rotation.RotateVector(FVector::YAxisVector);
static constexpr int32 CircleDivs = 12;
FVector PrevPosition = FVector::ZeroVector;
for (int32 Div = 0; Div <= CircleDivs; Div++)
{
const float Angle = (float)Div / (float)CircleDivs * PI * 2.0f;
const FVector Position = Center + (FMath::Cos(Angle) * XAxis + FMath::Sin(Angle) * YAxis) * Radius;
if (Div > 0)
{
DebugShapes.Lines.Add(FDebugRenderSceneProxy::FDebugLine(PrevPosition, Position, Color, Thickness));
}
PrevPosition = Position;
}
if (bDrawLabel)
{
const FString PrintString = NumPoints == 3 ? ElementToDraw->Description : FString::Printf(TEXT("%s_%d"), *ElementToDraw->Description, Index / 3);
DebugShapes.Texts.Add(FDebugRenderSceneProxy::FText3d(PrintString, Center, Color));
}
}
}
break;
case EVisualLoggerShapeElement::Invalid:
UE_LOG(LogVisual, Warning, TEXT("Invalid element type"));
break;
default:
UE_LOG(LogVisual, Warning, TEXT("Unhandled element type: %s"), ElementToDraw->GetType());
}
}
}