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

181 lines
5.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseGizmos/GizmoElementLineStrip.h"
#include "BaseGizmos/GizmoRenderingUtil.h"
#include "BaseGizmos/GizmoMath.h"
#include "InputState.h"
#include "Materials/MaterialInterface.h"
#include "Math/Ray.h"
#include "PrimitiveDrawingUtils.h"
#include "Math/UnrealMathUtility.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GizmoElementLineStrip)
void UGizmoElementLineStrip::Render(IToolsContextRenderAPI* RenderAPI, const FRenderTraversalState& RenderState)
{
FRenderTraversalState CurrentRenderState(RenderState);
const bool bVisibleViewDependent = UpdateRenderState(RenderAPI, Base, CurrentRenderState);
if (bVisibleViewDependent && Vertices.Num() > 1)
{
FPrimitiveDrawInterface* PDI = RenderAPI->GetPrimitiveDrawInterface();
ComputeProjectedVertices(RenderState.LocalToWorldTransform, true);
check(RenderAPI);
const FSceneView* View = RenderAPI->GetSceneView();
check(View);
const float CurrentLineThickness = GetCurrentLineThickness(View->IsPerspectiveProjection(), View->FOV);
const FColor LineColor = CurrentRenderState.GetCurrentLineColor().ToFColor(false);
if(bDrawLineStrip)
{
for(int32 Index = 0; Index < ProjectedVertices.Num() - 1; Index++)
{
const FVector& A = ProjectedVertices[Index];
const FVector& B = ProjectedVertices[Index+1];
PDI->DrawLine(A, B, LineColor, SDPG_Foreground, CurrentLineThickness, 0.0, bScreenSpaceLine);
}
}
else
{
for(int32 Index = 0; Index < ProjectedVertices.Num(); Index+=2)
{
const FVector& A = ProjectedVertices[Index];
const FVector& B = ProjectedVertices[Index+1];
PDI->DrawLine(A, B, LineColor, SDPG_Foreground, CurrentLineThickness, 0.0, bScreenSpaceLine);
}
}
}
}
FInputRayHit UGizmoElementLineStrip::LineTrace(const UGizmoViewContext* ViewContext, const FLineTraceTraversalState& LineTraceState, const FVector& RayOrigin, const FVector& RayDirection)
{
FLineTraceTraversalState CurrentLineTraceState(LineTraceState);
const bool bHittableViewDependent = UpdateLineTraceState(ViewContext, Base, CurrentLineTraceState);
if (bHittableViewDependent && Vertices.Num() > 1)
{
const double PixelHitThresholdAdjust = CurrentLineTraceState.PixelToWorldScale * PixelHitDistanceThreshold;
ComputeProjectedVertices(CurrentLineTraceState.LocalToWorldTransform, false);
const FRay Ray(RayOrigin, RayDirection);
const FVector RayB = Ray.Origin + Ray.Direction * 10000.0;
auto Intersect = [Ray, RayB](const FVector& A, const FVector&B, double InTolerance, double& Distance) -> bool
{
FVector HitPointA = Ray.Origin;
FVector HitPointB = A;
FMath::SegmentDistToSegmentSafe(Ray.Origin, RayB, A, B, HitPointA, HitPointB);
if(HitPointA.Equals(Ray.Origin))
{
return false;
}
Distance = FVector::Distance(HitPointA, HitPointB);
return Distance < InTolerance;
};
double HitDepth = 0;
if(bDrawLineStrip)
{
for(int32 Index = 0; Index < ProjectedVertices.Num() - 1; Index++)
{
const FVector& A = ProjectedVertices[Index];
const FVector& B = ProjectedVertices[Index+1];
if(Intersect(A, B, PixelHitThresholdAdjust, HitDepth))
{
FInputRayHit RayHit(static_cast<float>(HitDepth));
RayHit.SetHitObject(this);
RayHit.HitIdentifier = PartIdentifier;
return RayHit;
}
}
}
else
{
for(int32 Index = 0; Index < ProjectedVertices.Num(); Index+=2)
{
const FVector& A = ProjectedVertices[Index];
const FVector& B = ProjectedVertices[Index+1];
if(Intersect(A, B, PixelHitThresholdAdjust, HitDepth))
{
FInputRayHit RayHit(static_cast<float>(HitDepth));
RayHit.SetHitObject(this);
RayHit.HitIdentifier = PartIdentifier;
return RayHit;
}
}
}
}
return FInputRayHit();
}
void UGizmoElementLineStrip::SetVertices(const TArrayView<const FVector>& InVertices)
{
Vertices = InVertices;
}
const TArray<FVector>& UGizmoElementLineStrip::GetVertices() const
{
return Vertices;
}
void UGizmoElementLineStrip::SetBase(FVector InBase)
{
Base = InBase;
}
FVector UGizmoElementLineStrip::GetBase() const
{
return Base;
}
void UGizmoElementLineStrip::SetUpDirection(const FVector& InUpDirection)
{
UpDirection = InUpDirection.GetSafeNormal();
}
FVector UGizmoElementLineStrip::GetUpDirection() const
{
return UpDirection;
}
void UGizmoElementLineStrip::SetSideDirection(const FVector& InSideDirection)
{
SideDirection = InSideDirection.GetSafeNormal();
}
FVector UGizmoElementLineStrip::GetSideDirection() const
{
return SideDirection;
}
void UGizmoElementLineStrip::SetDrawLineStrip(bool InDrawLineStrip)
{
bDrawLineStrip = InDrawLineStrip;
}
bool UGizmoElementLineStrip::GetDrawLineStrip() const
{
return bDrawLineStrip;
}
void UGizmoElementLineStrip::ComputeProjectedVertices(const FTransform& InLocalToWorldTransform, bool bIncludeBase)
{
const FVector WorldUpAxis = InLocalToWorldTransform.TransformVectorNoScale(UpDirection);
const FVector WorldSideAxis = InLocalToWorldTransform.TransformVectorNoScale(SideDirection);
const FVector WorldNormal = FVector::CrossProduct(WorldSideAxis, WorldUpAxis);
const FVector WorldCenter = InLocalToWorldTransform.TransformPosition(bIncludeBase ? Base : FVector::ZeroVector);
const FVector WorldScale = InLocalToWorldTransform.GetScale3D();
ProjectedVertices.Reset();
ProjectedVertices.Reserve(Vertices.Num());
for(int32 Index = 0; Index < Vertices.Num(); Index++)
{
FVector Vertex = Vertices[Index] * WorldScale;
ProjectedVertices.Add(WorldCenter + WorldNormal * Vertex.X + WorldSideAxis * Vertex.Y + WorldUpAxis * Vertex.Z);
}
}