// Copyright Epic Games, Inc. All Rights Reserved. #include "BaseGizmos/GizmoElementRectangle.h" #include "BaseGizmos/GizmoRenderingUtil.h" #include "BaseGizmos/GizmoMath.h" #include "InputState.h" #include "Materials/MaterialInterface.h" #include "PrimitiveDrawingUtils.h" #include "Math/UnrealMathUtility.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(GizmoElementRectangle) void UGizmoElementRectangle::Render(IToolsContextRenderAPI* RenderAPI, const FRenderTraversalState& RenderState) { FRenderTraversalState CurrentRenderState(RenderState); bool bVisibleViewDependent = UpdateRenderState(RenderAPI, Center, CurrentRenderState); if (bVisibleViewDependent) { const FVector WorldUpAxis = CurrentRenderState.LocalToWorldTransform.TransformVectorNoScale(UpDirection); const FVector WorldSideAxis = CurrentRenderState.LocalToWorldTransform.TransformVectorNoScale(SideDirection); const FVector WorldCenter = CurrentRenderState.LocalToWorldTransform.TransformPosition(FVector::ZeroVector); const float WorldWidth = static_cast(Width * CurrentRenderState.LocalToWorldTransform.GetScale3D().X); const float WorldHeight = static_cast(Height * CurrentRenderState.LocalToWorldTransform.GetScale3D().X); FPrimitiveDrawInterface* PDI = RenderAPI->GetPrimitiveDrawInterface(); if (bDrawMesh) { if (const UMaterialInterface* UseMaterial = CurrentRenderState.GetCurrentMaterial()) { FColor VertexColor = CurrentRenderState.GetCurrentVertexColor().ToFColor(false); DrawRectangleMesh(PDI, WorldCenter, WorldUpAxis, WorldSideAxis, VertexColor, WorldWidth, WorldHeight, UseMaterial->GetRenderProxy(), SDPG_Foreground); } } if (bDrawLine) { check(RenderAPI); const FSceneView* View = RenderAPI->GetSceneView(); check(View); float CurrentLineThickness = GetCurrentLineThickness(View->IsPerspectiveProjection(), View->FOV); FColor LineColor = CurrentRenderState.GetCurrentLineColor().ToFColor(false); DrawRectangle(PDI, WorldCenter, WorldUpAxis, WorldSideAxis, LineColor, WorldWidth, WorldHeight, SDPG_Foreground, CurrentLineThickness, 0.0, bScreenSpaceLine); } } } FInputRayHit UGizmoElementRectangle::LineTrace(const UGizmoViewContext* ViewContext, const FLineTraceTraversalState& LineTraceState, const FVector& RayOrigin, const FVector& RayDirection) { FLineTraceTraversalState CurrentLineTraceState(LineTraceState); bool bHittableViewDependent = UpdateLineTraceState(ViewContext, Center, CurrentLineTraceState); if (bHittableViewDependent) { FTransform LocalToWorldTransform = CurrentLineTraceState.LocalToWorldTransform; const FVector WorldUpAxis = LocalToWorldTransform.TransformVectorNoScale(UpDirection); const FVector WorldSideAxis = LocalToWorldTransform.TransformVectorNoScale(SideDirection); const FVector WorldNormal = FVector::CrossProduct(WorldUpAxis, WorldSideAxis); const FVector WorldCenter = LocalToWorldTransform.TransformPosition(FVector::ZeroVector); const double Scale = LocalToWorldTransform.GetScale3D().X; const double PixelHitThresholdAdjust = CurrentLineTraceState.PixelToWorldScale * PixelHitDistanceThreshold; const double WorldHeight = Scale * Height + PixelHitThresholdAdjust * 2.0; const double WorldWidth = Scale * Width + PixelHitThresholdAdjust * 2.0; const FVector Base = WorldCenter - WorldUpAxis * WorldHeight * 0.5 - WorldSideAxis * WorldWidth * 0.5; // if ray is parallel to rectangle, no hit if (FMath::IsNearlyZero(FVector::DotProduct(WorldNormal, RayDirection))) { return FInputRayHit(); } if (bHitMesh) { FPlane Plane(Base, WorldNormal); double HitDepth = FMath::RayPlaneIntersectionParam(RayOrigin, RayDirection, Plane); if (HitDepth < 0) { return FInputRayHit(); } FVector HitPoint = RayOrigin + RayDirection * HitDepth; FVector HitOffset = HitPoint - Base; double HdU = FVector::DotProduct(HitOffset, WorldUpAxis); double HdS = FVector::DotProduct(HitOffset, WorldSideAxis); // clip to rectangle dimensions if (HdU >= 0.0 && HdU <= WorldHeight && HdS >= 0.0 && HdS <= WorldWidth) { FInputRayHit RayHit(static_cast(HitDepth)); RayHit.SetHitObject(this); RayHit.HitIdentifier = PartIdentifier; return RayHit; } } else if (bHitLine) { FPlane Plane(Base, WorldNormal); double HitDepth = FMath::RayPlaneIntersectionParam(RayOrigin, RayDirection, Plane); if (HitDepth < 0) { return FInputRayHit(); } FVector HitPoint = RayOrigin + RayDirection * HitDepth; FVector HitOffset = HitPoint - Base; double HdU = FVector::DotProduct(HitOffset, WorldUpAxis); double HdS = FVector::DotProduct(HitOffset, WorldSideAxis); const double HitBuffer = PixelHitThresholdAdjust + LineThickness; // determine if the hit is within pixel tolerance of the edges of rectangle if (HdU >= 0.0 && HdU <= WorldHeight && HdS >= 0.0 && HdS <= WorldWidth && (HdS <= HitBuffer || HdS >= WorldWidth - HitBuffer || HdU <= HitBuffer || HdU >= WorldHeight - HitBuffer)) { FInputRayHit RayHit(static_cast(HitDepth)); RayHit.SetHitObject(this); RayHit.HitIdentifier = PartIdentifier; return RayHit; } } } return FInputRayHit(); } void UGizmoElementRectangle::SetCenter(FVector InCenter) { Center = InCenter; } FVector UGizmoElementRectangle::GetCenter() const { return Center; } void UGizmoElementRectangle::SetWidth(float InWidth) { Width = InWidth; } float UGizmoElementRectangle::GetWidth() const { return Width; } void UGizmoElementRectangle::SetHeight(float InHeight) { Height = InHeight; } float UGizmoElementRectangle::GetHeight() const { return Height; } void UGizmoElementRectangle::SetUpDirection(const FVector& InUpDirection) { UpDirection = InUpDirection.GetSafeNormal(); } FVector UGizmoElementRectangle::GetUpDirection() const { return UpDirection; } void UGizmoElementRectangle::SetSideDirection(const FVector& InSideDirection) { SideDirection = InSideDirection.GetSafeNormal(); } FVector UGizmoElementRectangle::GetSideDirection() const { return SideDirection; } void UGizmoElementRectangle::SetDrawMesh(bool InDrawMesh) { bDrawMesh = InDrawMesh; } bool UGizmoElementRectangle::GetDrawMesh() const { return bDrawMesh; } void UGizmoElementRectangle::SetDrawLine(bool InDrawLine) { bDrawLine = InDrawLine; } bool UGizmoElementRectangle::GetDrawLine() const { return bDrawLine; } void UGizmoElementRectangle::SetHitMesh(bool InHitMesh) { bHitMesh = InHitMesh; } bool UGizmoElementRectangle::GetHitMesh() const { return bHitMesh; } void UGizmoElementRectangle::SetHitLine(bool InHitLine) { bHitLine = InHitLine; } bool UGizmoElementRectangle::GetHitLine() const { return bHitLine; }