// 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(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(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(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; }