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

156 lines
4.8 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseGizmos/GizmoElementCircle.h"
#include "BaseGizmos/GizmoRenderingUtil.h"
#include "BaseGizmos/GizmoMath.h"
#include "InputState.h"
#include "Intersection/IntersectionUtil.h"
#include "Materials/MaterialInterface.h"
#include "PrimitiveDrawingUtils.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GizmoElementCircle)
void UGizmoElementCircle::Render(IToolsContextRenderAPI* RenderAPI, const FRenderTraversalState& RenderState)
{
FRenderTraversalState CurrentRenderState(RenderState);
bool bVisibleViewDependent = UpdateRenderState(RenderAPI, Center, CurrentRenderState);
if (bVisibleViewDependent)
{
const double WorldRadius = Radius * CurrentRenderState.LocalToWorldTransform.GetScale3D().X;
const FVector WorldCenter = CurrentRenderState.LocalToWorldTransform.TransformPosition(FVector::ZeroVector);
const FVector WorldAxis0 = CurrentRenderState.LocalToWorldTransform.TransformVectorNoScale(Axis0);
const FVector WorldAxis1 = CurrentRenderState.LocalToWorldTransform.TransformVectorNoScale(Axis1);
FPrimitiveDrawInterface* PDI = RenderAPI->GetPrimitiveDrawInterface();
if (bDrawMesh)
{
if (const UMaterialInterface* UseMaterial = CurrentRenderState.GetCurrentMaterial())
{
FColor VertexColor = CurrentRenderState.GetCurrentVertexColor().ToFColor(false);
DrawDisc(PDI, WorldCenter, WorldAxis0, WorldAxis1, VertexColor, WorldRadius, NumSegments, UseMaterial->GetRenderProxy(), SDPG_Foreground);
}
}
if (bDrawLine)
{
check(RenderAPI);
const FSceneView* View = RenderAPI->GetSceneView();
check(View);
float CurrentLineThickness = GetCurrentLineThickness(View->IsPerspectiveProjection(), View->FOV);
DrawCircle(PDI, WorldCenter, WorldAxis0, WorldAxis1, CurrentRenderState.GetCurrentLineColor(), WorldRadius, NumSegments, SDPG_Foreground, CurrentLineThickness, 0.0, bScreenSpaceLine);
}
}
}
FInputRayHit UGizmoElementCircle::LineTrace(const UGizmoViewContext* ViewContext, const FLineTraceTraversalState& LineTraceState, const FVector& RayOrigin, const FVector& RayDirection)
{
FLineTraceTraversalState CurrentLineTraceState(LineTraceState);
bool bHittableViewDependent = UpdateLineTraceState(ViewContext, Center, CurrentLineTraceState);
if (bHittableViewDependent)
{
const FVector WorldCenter = CurrentLineTraceState.LocalToWorldTransform.GetLocation();
const FVector WorldAxis0 = CurrentLineTraceState.LocalToWorldTransform.TransformVectorNoScale(Axis0);
const FVector WorldAxis1 = CurrentLineTraceState.LocalToWorldTransform.TransformVectorNoScale(Axis1);
const FVector WorldNormal = WorldAxis0 ^ WorldAxis1;
const double PixelHitThresholdAdjust = CurrentLineTraceState.PixelToWorldScale * PixelHitDistanceThreshold;
double WorldRadius = CurrentLineTraceState.LocalToWorldTransform.GetScale3D().X * Radius;
// if ray is parallel to circle, no hit
if (FMath::IsNearlyZero(FVector::DotProduct(WorldNormal, RayDirection)))
{
return FInputRayHit();
}
if (bHitMesh)
{
WorldRadius += PixelHitThresholdAdjust;
UE::Geometry::FLinearIntersection Result;
IntersectionUtil::RayCircleIntersection(RayOrigin, RayDirection, WorldCenter, WorldRadius, WorldNormal, Result);
if (Result.intersects)
{
FInputRayHit RayHit(static_cast<float>(Result.parameter.Min));
RayHit.SetHitObject(this);
RayHit.HitIdentifier = PartIdentifier;
return RayHit;
}
}
else if (bHitLine)
{
FPlane Plane(WorldCenter, WorldNormal);
double HitDepth = FMath::RayPlaneIntersectionParam(RayOrigin, RayDirection, Plane);
if (HitDepth < 0)
{
return FInputRayHit();
}
FVector HitPoint = RayOrigin + RayDirection * HitDepth;
FVector NearestCirclePos;
GizmoMath::ClosetPointOnCircle(HitPoint, WorldCenter, WorldNormal, static_cast<float>(WorldRadius), NearestCirclePos);
FRay Ray(RayOrigin, RayDirection, true);
FVector NearestRayPos = Ray.ClosestPoint(NearestCirclePos);
const double HitBuffer = PixelHitThresholdAdjust + LineThickness;
double Distance = FVector::Distance(NearestCirclePos, NearestRayPos);
if (Distance <= HitBuffer)
{
FInputRayHit RayHit(static_cast<float>(HitDepth));
RayHit.SetHitObject(this);
RayHit.HitIdentifier = PartIdentifier;
return RayHit;
}
}
}
return FInputRayHit();
}
void UGizmoElementCircle::SetDrawMesh(bool InDrawMesh)
{
bDrawMesh = InDrawMesh;
}
bool UGizmoElementCircle::GetDrawMesh() const
{
return bDrawMesh;
}
void UGizmoElementCircle::SetDrawLine(bool InDrawLine)
{
bDrawLine = InDrawLine;
}
bool UGizmoElementCircle::GetDrawLine() const
{
return bDrawLine;
}
void UGizmoElementCircle::SetHitMesh(bool InHitMesh)
{
bHitMesh = InHitMesh;
}
bool UGizmoElementCircle::GetHitMesh() const
{
return bHitMesh;
}
void UGizmoElementCircle::SetHitLine(bool InHitLine)
{
bHitLine = InHitLine;
}
bool UGizmoElementCircle::GetHitLine() const
{
return bHitLine;
}