Files
2025-05-18 13:04:45 +08:00

543 lines
22 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Annotations/SmartObjectSlotEntranceAnnotation.h"
#include "Engine/HitResult.h"
#include "Engine/OverlapResult.h"
#include "SmartObjectDefinition.h"
#include "SmartObjectVisualizationContext.h"
#include "PrimitiveDrawingUtils.h" // FPrimitiveDrawInterface
#include "NavigationSystem.h"
#include "NavigationData.h"
#include "NavFilters/NavigationQueryFilter.h"
#include "AI/Navigation/NavAgentInterface.h"
#include "SmartObjectUserComponent.h"
#if WITH_GAMEPLAY_DEBUGGER
#include "GameplayDebuggerCategory.h"
#endif
#include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectSlotEntranceAnnotation)
namespace UE::SmartObject::Annotations
{
const ANavigationData* GetDefaultNavData(const UWorld& World)
{
const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World);
if (!NavSys)
{
return nullptr;
}
return NavSys->GetDefaultNavDataInstance();
}
const ANavigationData* GetNavDataForActor(const UWorld& World, const AActor* UserActor)
{
if (const UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(&World))
{
const INavAgentInterface* NavAgent = Cast<INavAgentInterface>(UserActor);
if (NavAgent)
{
const FNavAgentProperties& NavAgentProps = NavAgent->GetNavAgentPropertiesRef();
return NavSys->GetNavDataForProps(NavAgentProps, NavAgent->GetNavAgentLocation());
}
return NavSys->GetDefaultNavDataInstance();
}
return nullptr;
}
bool ProjectNavigationLocation(const ANavigationData& NavData, const FVector Location, const FBox& SearchBounds, const FSharedConstNavQueryFilter& NavigationFilter, const AActor* InstigatorActor, FNavLocation& OutNavLocation)
{
return NavData.ProjectPoint(Location, OutNavLocation, SearchBounds.GetExtent(), NavigationFilter, InstigatorActor)
&& OutNavLocation.HasNodeRef()
&& SearchBounds.IsInsideOrOn(OutNavLocation.Location);
}
bool TraceGroundLocation(const UWorld& World, const FVector Location, const FBox& SearchBounds, const FSmartObjectTraceParams& TraceParameters, const FCollisionQueryParams& CollisionQueryParams, FVector& OutGroundLocation)
{
const FVector TraceStart(Location.X, Location.Y, SearchBounds.Max.Z);
const FVector TraceEnd(Location.X, Location.Y, SearchBounds.Min.Z);
FHitResult HitResult;
bool bHasHit = false;
constexpr float SphereRadius = 1.f;
if (TraceParameters.Type == ESmartObjectTraceType::ByChannel)
{
bHasHit = World.SweepSingleByChannel(
HitResult,
TraceStart,
TraceEnd,
FQuat::Identity,
UEngineTypes::ConvertToCollisionChannel(TraceParameters.TraceChannel),
FCollisionShape::MakeSphere(SphereRadius),
CollisionQueryParams);
}
else if (TraceParameters.Type == ESmartObjectTraceType::ByProfile)
{
bHasHit = World.SweepSingleByProfile(
HitResult,
TraceStart,
TraceEnd,
FQuat::Identity,
TraceParameters.CollisionProfile.Name,
FCollisionShape::MakeSphere(SphereRadius),
CollisionQueryParams);
}
else if (TraceParameters.Type == ESmartObjectTraceType::ByObjectTypes)
{
const FCollisionObjectQueryParams ObjectQueryParams(TraceParameters.ObjectTypes);
bHasHit = World.SweepSingleByObjectType(
HitResult,
TraceStart,
TraceEnd,
FQuat::Identity,
ObjectQueryParams,
FCollisionShape::MakeSphere(SphereRadius),
CollisionQueryParams);
}
if (bHasHit)
{
OutGroundLocation = HitResult.Location;
return true;
}
return false;
}
bool TestCollidersOverlap(const UWorld& World, TConstArrayView<FSmartObjectAnnotationCollider> Colliders, const FSmartObjectTraceParams& TraceParameters, const FCollisionQueryParams& CollisionQueryParams)
{
bool bHasHit = false;
TArray<FOverlapResult> Overlaps;
for (const FSmartObjectAnnotationCollider& Collider : Colliders)
{
Overlaps.Reset();
if (TraceParameters.Type == ESmartObjectTraceType::ByChannel)
{
bHasHit = World.OverlapMultiByChannel(Overlaps, Collider.Location, Collider.Rotation, UEngineTypes::ConvertToCollisionChannel(TraceParameters.TraceChannel), Collider.CollisionShape, CollisionQueryParams);
}
else if (TraceParameters.Type == ESmartObjectTraceType::ByProfile)
{
bHasHit = World.OverlapMultiByProfile(Overlaps, Collider.Location, Collider.Rotation, TraceParameters.CollisionProfile.Name, Collider.CollisionShape, CollisionQueryParams);
}
else if (TraceParameters.Type == ESmartObjectTraceType::ByObjectTypes)
{
// Overlap tests with object types will only ever return non-blocking results (due to historical reasons), so using the any variant here (blocking does not exist).
const FCollisionObjectQueryParams ObjectQueryParams(TraceParameters.ObjectTypes);
bHasHit = World.OverlapMultiByObjectType(Overlaps, Collider.Location, Collider.Rotation, ObjectQueryParams, Collider.CollisionShape, CollisionQueryParams);
}
if (bHasHit)
{
break;
}
}
return bHasHit;
}
static constexpr FColor EntryColor(0, 64, 192);
static constexpr FColor CollisionColor(255, 255, 255, 128);
static constexpr FColor InvalidEntryColor(192, 32, 16);
struct FVisualizationData
{
FVector SlotLocation;
FVector MarkerLocation;
FVector MarkerAxisX;
FVector MarkerAxisY;
FVector ValidatedLocation;
FVector GroundLocationStart;
FVector GroundLocationEnd;
TArray<FSmartObjectAnnotationCollider> Colliders;
FLinearColor MarkerColor = EntryColor;
FLinearColor ColliderColor = CollisionColor;
};
void UpdateVisualizationLogic(const UWorld& World, const FSmartObjectSlotEntranceAnnotation& Annotation, const FTransform& SlotTransform, const FTransform& AnnotationTransform,
const AActor* PreviewSmartObjectActor, const AActor* PreviewUserActor, TSubclassOf<USmartObjectSlotValidationFilter> PreviewValidationFilterClass, FVisualizationData& OutData)
{
const USmartObjectSlotValidationFilter* PreviewValidationFilter = PreviewValidationFilterClass.GetDefaultObject();
if (!PreviewValidationFilter)
{
PreviewValidationFilter = GetDefault<USmartObjectSlotValidationFilter>();
}
const ANavigationData* DefaultNavData = GetDefaultNavData(World);
const FSmartObjectSlotValidationParams& ValidationParams = PreviewValidationFilter->GetEntryValidationParams();
const FSmartObjectTraceParams& GroundTraceParameters = ValidationParams.GetGroundTraceParameters();
const FSmartObjectTraceParams& TransitionTraceParameters = ValidationParams.GetTransitionTraceParameters();
FCollisionQueryParams GroundTraceQueryParams(SCENE_QUERY_STAT(SmartObjectTrace), GroundTraceParameters.bTraceComplex);
FCollisionQueryParams TransitionTraceQueryParams(SCENE_QUERY_STAT(SmartObjectTrace), TransitionTraceParameters.bTraceComplex);
GroundTraceQueryParams.bIgnoreTouches = true;
TransitionTraceQueryParams.bIgnoreTouches = true;
if (PreviewSmartObjectActor)
{
GroundTraceQueryParams.AddIgnoredActor(PreviewSmartObjectActor);
TransitionTraceQueryParams.AddIgnoredActor(PreviewSmartObjectActor);
}
if (PreviewUserActor)
{
GroundTraceQueryParams.AddIgnoredActor(PreviewUserActor);
TransitionTraceQueryParams.AddIgnoredActor(PreviewUserActor);
}
const FVector AnnotationWorldLocation = AnnotationTransform.GetTranslation();
const FVector AxisX = AnnotationTransform.GetUnitAxis(EAxis::X);
const FVector AxisY = AnnotationTransform.GetUnitAxis(EAxis::Y);
OutData.SlotLocation = SlotTransform.GetLocation();
OutData.MarkerColor = EntryColor;
OutData.ColliderColor = CollisionColor;
OutData.MarkerLocation = AnnotationWorldLocation;
OutData.MarkerAxisX = AxisX;
OutData.MarkerAxisY = AxisY;
const FBox SearchBounds(AnnotationWorldLocation - ValidationParams.GetSearchExtents(), AnnotationWorldLocation + ValidationParams.GetSearchExtents());
OutData.ValidatedLocation = AnnotationWorldLocation;
// Validate location on navmesh
// @todo: add visualization for missing navdata.
if (DefaultNavData)
{
FSharedConstNavQueryFilter NavigationFilter;
if (ValidationParams.GetNavigationFilter())
{
NavigationFilter = UNavigationQueryFilter::GetQueryFilter(*DefaultNavData, nullptr, ValidationParams.GetNavigationFilter());
}
FNavLocation NavLocation;
if (ProjectNavigationLocation(*DefaultNavData, AnnotationWorldLocation, SearchBounds, NavigationFilter, /*RequesterActor*/nullptr, NavLocation))
{
OutData.ValidatedLocation = NavLocation.Location;
}
else
{
OutData.MarkerColor = InvalidEntryColor;
}
}
// Try to trace the slot on location on ground.
if (Annotation.bTraceGroundLocation)
{
FVector GroundLocation;
if (TraceGroundLocation(World, OutData.ValidatedLocation, SearchBounds, GroundTraceParameters, GroundTraceQueryParams, GroundLocation))
{
OutData.ValidatedLocation = GroundLocation;
}
else
{
OutData.MarkerColor = InvalidEntryColor;
}
}
if (Annotation.bCheckTransitionTrajectory)
{
Annotation.GetTrajectoryColliders(SlotTransform, OutData.Colliders);
if (TestCollidersOverlap(World, OutData.Colliders, TransitionTraceParameters, TransitionTraceQueryParams))
{
OutData.ColliderColor = InvalidEntryColor;
}
}
OutData.GroundLocationStart = FVector(OutData.ValidatedLocation.X, OutData.ValidatedLocation.Y, FMath::Max(OutData.ValidatedLocation.Z, OutData.MarkerLocation.Z));
OutData.GroundLocationEnd = FVector(OutData.ValidatedLocation.X, OutData.ValidatedLocation.Y, FMath::Min(OutData.ValidatedLocation.Z, OutData.MarkerLocation.Z));
}
} // UE::SmartObject::Annotations
#if WITH_EDITOR
void FSmartObjectSlotEntranceAnnotation::DrawVisualization(FSmartObjectVisualizationContext& VisContext) const
{
constexpr FVector::FReal MarkerRadius = 20.0;
constexpr FVector::FReal TickSize = 5.0;
constexpr FVector::FReal MinArrowDrawDistance = 20.0;
constexpr float DepthBias = 2.0f;
constexpr bool ScreenSpace = true;
const FTransform SlotTransform = VisContext.Definition.GetSlotWorldTransform(VisContext.SlotIndex, VisContext.OwnerLocalToWorld);
const FTransform AnnotationTransform = GetAnnotationWorldTransform(SlotTransform);
UE::SmartObject::Annotations::FVisualizationData Data;
UE::SmartObject::Annotations::UpdateVisualizationLogic(VisContext.World, *this, SlotTransform, AnnotationTransform,
VisContext.PreviewActor, nullptr, VisContext.PreviewValidationFilterClass, Data);
// Draw validated location in relation to the marker locations.
if (FVector::Distance(Data.MarkerLocation, Data.ValidatedLocation) > UE_KINDA_SMALL_NUMBER)
{
VisContext.PDI->DrawTranslucentLine(Data.GroundLocationStart, Data.GroundLocationEnd, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(Data.ValidatedLocation - Data.MarkerAxisX * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisX * TickSize * 0.5, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(Data.ValidatedLocation - Data.MarkerAxisY * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisY * TickSize * 0.5, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace);
}
if (VisContext.bIsAnnotationSelected)
{
Data.MarkerColor = VisContext.SelectedColor;
}
if (bIsEntry)
{
const FVector V0 = Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius;
const FVector V1 = Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 + Data.MarkerAxisY * MarkerRadius;
const FVector V2 = Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 - Data.MarkerAxisY * MarkerRadius;
const FVector V3 = Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius;
const FVector V4 = Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius;
VisContext.PDI->DrawTranslucentLine(V0, V1, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(V0, V2, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(V1, V3, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(V2, V4, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
}
if (bIsExit)
{
const FVector V1 = Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.75 + Data.MarkerAxisY * MarkerRadius;
const FVector V2 = Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.75 - Data.MarkerAxisY * MarkerRadius;
const FVector V3 = Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius;
const FVector V4 = Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius;
VisContext.PDI->DrawTranslucentLine(V1, V2, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(V1, V3, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(V2, V4, Data.MarkerColor, SDPG_World, 4.0f, DepthBias, ScreenSpace);
}
if (!Data.Colliders.IsEmpty())
{
for (const FSmartObjectAnnotationCollider& Collider : Data.Colliders)
{
if (Collider.CollisionShape.IsCapsule())
{
DrawWireCapsule(VisContext.PDI, Collider.Location, Collider.Rotation.GetAxisX(), Collider.Rotation.GetAxisY(), Collider.Rotation.GetAxisZ(),
Data.ColliderColor, Collider.CollisionShape.GetCapsuleRadius(), Collider.CollisionShape.GetCapsuleHalfHeight(), 12, SDPG_World, 1.0f, DepthBias, ScreenSpace);
}
else if (Collider.CollisionShape.IsBox())
{
DrawOrientedWireBox(VisContext.PDI, Collider.Location, Collider.Rotation.GetAxisX(), Collider.Rotation.GetAxisY(), Collider.Rotation.GetAxisZ(),
Collider.CollisionShape.GetExtent(), Data.ColliderColor, SDPG_World, 1.0f, DepthBias, ScreenSpace);
}
else if (Collider.CollisionShape.IsSphere())
{
DrawWireSphere(VisContext.PDI, Collider.Location, Data.ColliderColor, Collider.CollisionShape.GetSphereRadius(), 12, SDPG_World, 1.0f, DepthBias, ScreenSpace);
}
}
}
// Tick at the center.
VisContext.PDI->DrawTranslucentLine(Data.MarkerLocation - Data.MarkerAxisX * TickSize, Data.MarkerLocation + Data.MarkerAxisX * TickSize, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace);
VisContext.PDI->DrawTranslucentLine(Data.MarkerLocation - Data.MarkerAxisY * TickSize, Data.MarkerLocation + Data.MarkerAxisY * TickSize, Data.MarkerColor, SDPG_World, 1.0f, DepthBias, ScreenSpace);
// Arrow pointing at the the slot, if far enough from the slot.
if (FVector::DistSquared(Data.MarkerLocation, Data.SlotLocation) > FMath::Square(MinArrowDrawDistance))
{
VisContext.DrawArrow(Data.MarkerLocation, Data.SlotLocation, Data.MarkerColor, 5.0f, 5.0f, /*DepthPrioGroup*/0, /*Thickness*/1.0f, DepthBias, ScreenSpace);
}
}
void FSmartObjectSlotEntranceAnnotation::DrawVisualizationHUD(FSmartObjectVisualizationContext& VisContext) const
{
// @todo: move this into a setting.
constexpr FVector::FReal MaxDrawDistance = 1500.0;
constexpr FVector::FReal FadeDrawDistance = MaxDrawDistance * 0.75;
const FTransform SlotTransform = VisContext.Definition.GetSlotWorldTransform(VisContext.SlotIndex, VisContext.OwnerLocalToWorld);
const FTransform AnnotationTransform = GetAnnotationWorldTransform(SlotTransform);
const FVector AnnotationWorldLocation = AnnotationTransform.GetTranslation();
const FVector::FReal Distance = VisContext.GetDistanceToCamera(AnnotationWorldLocation);
if (Distance < MaxDrawDistance)
{
FString Text = FString::Printf(TEXT("S%d NAV%d \n"), VisContext.SlotIndex, VisContext.AnnotationIndex);
if (SelectionPriority != ESmartObjectEntrancePriority::Normal)
{
Text += UEnum::GetDisplayValueAsText(SelectionPriority).ToString();
}
if (Tags.IsValid())
{
Text += Tags.ToString();
}
FLinearColor Color = FLinearColor::White;
Color.A = static_cast<float>(FMath::Clamp(1.0 - (Distance - FadeDrawDistance) / (MaxDrawDistance - FadeDrawDistance), 0.0, 1.0));
VisContext.DrawString(AnnotationWorldLocation, *Text, Color);
}
}
void FSmartObjectSlotEntranceAnnotation::AdjustWorldTransform(const FTransform& SlotTransform, const FVector& DeltaTranslation, const FRotator& DeltaRotation)
{
if (!DeltaTranslation.IsZero())
{
const FVector LocalTranslation = SlotTransform.InverseTransformVector(DeltaTranslation);
Offset += FVector3f(LocalTranslation);
}
if (!DeltaRotation.IsZero())
{
const FRotator3f LocalRotation = FRotator3f(SlotTransform.InverseTransformRotation(DeltaRotation.Quaternion()).Rotator());
Rotation += LocalRotation;
Rotation.Normalize();
}
}
#endif // WITH_EDITOR
void FSmartObjectSlotEntranceAnnotation::GetTrajectoryColliders(const FTransform& SlotTransform, TArray<FSmartObjectAnnotationCollider>& OutColliders) const
{
const FVector SlotWorldLocation = SlotTransform.GetLocation();
const FVector AnnotationWorldLocation = SlotTransform.TransformPosition(FVector(Offset));
const FVector SegmentStart = AnnotationWorldLocation + FVector(0, 0, TrajectoryStartHeightOffset);
const FVector SegmentEnd = SlotWorldLocation + FVector(0, 0, TrajectorySlotHeightOffset);
const FVector Center = (SegmentStart + SegmentEnd) * 0.5;
const FVector Dir = SegmentEnd - SegmentStart;
FVector Up;
float Length = 0.0f;
Dir.ToDirectionAndLength(Up, Length);
FSmartObjectAnnotationCollider& Collider = OutColliders.AddDefaulted_GetRef();
Collider.Location = Center;
Collider.Rotation = FQuat::FindBetweenNormals(FVector::UpVector, Up);
Collider.CollisionShape = FCollisionShape::MakeCapsule(TransitionCheckRadius, Length * 0.5f + TransitionCheckRadius);
}
FTransform FSmartObjectSlotEntranceAnnotation::GetAnnotationWorldTransform(const FTransform& SlotTransform) const
{
const FTransform LocalTransform = FTransform(FRotator(Rotation), FVector(Offset));
return LocalTransform * SlotTransform;
}
FVector FSmartObjectSlotEntranceAnnotation::GetWorldLocation(const FTransform& SlotTransform) const
{
return SlotTransform.TransformPosition(FVector(Offset));
}
FRotator FSmartObjectSlotEntranceAnnotation::GetWorldRotation(const FTransform& SlotTransform) const
{
return SlotTransform.TransformRotation(FQuat(Rotation.Quaternion())).Rotator();
}
#if WITH_EDITORONLY_DATA
void FSmartObjectSlotEntranceAnnotation::PostSerialize(const FArchive& Ar)
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (Tag_DEPRECATED.IsValid())
{
Tags.AddTag(Tag_DEPRECATED);
Tag_DEPRECATED = FGameplayTag();
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
#endif
#if WITH_GAMEPLAY_DEBUGGER
void FSmartObjectSlotEntranceAnnotation::CollectDataForGameplayDebugger(FSmartObjectAnnotationGameplayDebugContext& DebugContext) const
{
constexpr FVector::FReal MarkerRadius = 20.0;
constexpr FVector::FReal TickSize = 5.0;
constexpr FVector::FReal MinArrowDrawDistance = 20.0;
const UWorld* World = DebugContext.Category.GetWorldFromReplicator();
if (!World)
{
return;
}
const FTransform AnnotationTransform = GetAnnotationWorldTransform(DebugContext.SlotTransform);
TSubclassOf<USmartObjectSlotValidationFilter> ValidationFilterClass;
if (DebugContext.DebugActor)
{
// If user actor is present, try to query some data automatically from interfaces and components.
if (!ValidationFilterClass.Get())
{
if (const USmartObjectUserComponent* UserComponent = DebugContext.DebugActor->GetComponentByClass<USmartObjectUserComponent>())
{
if (UserComponent->GetValidationFilter().Get())
{
ValidationFilterClass = UserComponent->GetValidationFilter();
}
}
}
}
UE::SmartObject::Annotations::FVisualizationData Data;
UE::SmartObject::Annotations::UpdateVisualizationLogic(*World, *this, DebugContext.SlotTransform, AnnotationTransform,
DebugContext.SmartObjectOwnerActor, DebugContext.DebugActor, ValidationFilterClass, Data);
// Draw validated location in relation to the marker locations.
if (FVector::Distance(Data.MarkerLocation, Data.ValidatedLocation) > UE_KINDA_SMALL_NUMBER)
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeSegmentList( {
Data.GroundLocationStart, Data.GroundLocationEnd,
Data.ValidatedLocation - Data.MarkerAxisX * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisX * TickSize * 0.5,
Data.ValidatedLocation - Data.MarkerAxisY * TickSize * 0.5, Data.ValidatedLocation + Data.MarkerAxisY * TickSize * 0.5
}, 1.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true)));
}
if (bIsEntry)
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakePolyline({
Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius,
Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 + Data.MarkerAxisY * MarkerRadius,
Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius,
Data.MarkerLocation + Data.MarkerAxisX * MarkerRadius * 0.25 - Data.MarkerAxisY * MarkerRadius,
Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius
}, 4.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true)));
}
if (bIsExit)
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakePolyline({
Data.MarkerLocation + Data.MarkerAxisY * MarkerRadius,
Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.5 + Data.MarkerAxisY * MarkerRadius,
Data.MarkerLocation - Data.MarkerAxisX * MarkerRadius * 0.5 - Data.MarkerAxisY * MarkerRadius,
Data.MarkerLocation - Data.MarkerAxisY * MarkerRadius
}, 4.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true)));
}
if (!Data.Colliders.IsEmpty())
{
for (const FSmartObjectAnnotationCollider& Collider : Data.Colliders)
{
if (Collider.CollisionShape.IsCapsule())
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeCapsule(Collider.Location, Collider.Rotation.Rotator(), Collider.CollisionShape.GetCapsuleRadius(), Collider.CollisionShape.GetCapsuleHalfHeight(), Data.ColliderColor.ToFColor(/*bSRGB*/true)));
}
else if (Collider.CollisionShape.IsBox())
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeBox(Collider.Location, Collider.Rotation.Rotator(), Collider.CollisionShape.GetExtent(), Data.ColliderColor.ToFColor(/*bSRGB*/true)));
}
else if (Collider.CollisionShape.IsSphere())
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakePoint(Collider.Location, Collider.CollisionShape.GetSphereRadius(), Data.ColliderColor.ToFColor(/*bSRGB*/true)));
}
}
}
// Tick at the center.
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeSegmentList( {
Data.MarkerLocation - Data.MarkerAxisX * TickSize,
Data.MarkerLocation + Data.MarkerAxisX * TickSize,
Data.MarkerLocation - Data.MarkerAxisY * TickSize,
Data.MarkerLocation + Data.MarkerAxisY * TickSize
}, 1.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true)));
// Arrow pointing at the the slot, if far enough from the slot.
if (FVector::DistSquared(Data.MarkerLocation, Data.SlotLocation) > FMath::Square(MinArrowDrawDistance))
{
DebugContext.Category.AddShape(FGameplayDebuggerShape::MakeSegment(Data.MarkerLocation, Data.SlotLocation, 1.0f, Data.MarkerColor.ToFColor(/*bSRGB*/true)));
}
}
#endif // WITH_GAMEPLAY_DEBUGGER