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

221 lines
6.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseGizmos/GizmoElementCylinder.h"
#include "GizmoPrivateUtil.h"
#include "BaseGizmos/GizmoRenderingUtil.h"
#include "BaseGizmos/GizmoMath.h"
#include "InputState.h"
#include "Materials/MaterialInterface.h"
#include "PrimitiveDrawingUtils.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GizmoElementCylinder)
namespace GizmoElementCylinderLocals
{
/**
* Normalizes the given dash parameters to fit the input range.
* @param OutScaleFactor the scale factor to apply to the dash length and gap length.
* @return the number of dashes that fit within the range.
*/
template < typename RealType = float>
int32 GetDashNumAndScale(const RealType& InLineLength, const RealType& InDashLength, const RealType& InDashGapLength, RealType& OutScaleFactor)
{
const RealType DashLengthAndGap = InDashLength + InDashGapLength;
OutScaleFactor = static_cast<RealType>(1);
// Number based on sequence of dash, gap pairs, and last dash. This should always be an even number for fit alignment
const int32 NumDashes = FMath::Max(2, FMath::CeilToInt32((InLineLength / DashLengthAndGap)));
// Get a scale factor based on how many dashes with gaps (and an end dash) can actually fit on this line
OutScaleFactor = InLineLength / (((NumDashes - 1) * DashLengthAndGap) + InDashLength);
return NumDashes;
}
}
void UGizmoElementCylinder::Render(IToolsContextRenderAPI* RenderAPI, const FRenderTraversalState& RenderState)
{
FRenderTraversalState CurrentRenderState(RenderState);
bool bVisibleViewDependent = UpdateRenderState(RenderAPI, Base, CurrentRenderState);
if (bVisibleViewDependent)
{
if (const UMaterialInterface* UseMaterial = CurrentRenderState.GetCurrentMaterial())
{
const FQuat Rotation = FRotationMatrix::MakeFromZ(Direction).ToQuat();
const double HalfHeight = Height * 0.5;
const FVector OriginOffset = Direction * HalfHeight;
FTransform RenderLocalToWorldTransform = FTransform(Rotation, OriginOffset) * CurrentRenderState.LocalToWorldTransform;
FPrimitiveDrawInterface* PDI = RenderAPI->GetPrimitiveDrawInterface();
if (bIsDashed)
{
// Get a scale factor based on how many dashes with gaps (and an end dash) can actually fit on this line
float DashLengthScale = 0.0f;
// Number based on sequence of dash, gap pairs, and last dash
const int32 NumDashes = GizmoElementCylinderLocals::GetDashNumAndScale(FMath::Max(0.0f, Height - DashLength), DashLength, DashGapLength, DashLengthScale);
const float AdjustedDashLength = DashLength * DashLengthScale;
const float AdjustDashGapLength = DashGapLength * DashLengthScale;
const float DashLengthAndGaps = (AdjustedDashLength + AdjustDashGapLength);
const float HalfDashLength = AdjustedDashLength * 0.5f;
for (int32 DashIndex = 0; DashIndex <= NumDashes; ++DashIndex)
{
DrawCylinder(
PDI,
RenderLocalToWorldTransform.ToMatrixWithScale(),
(-FVector::UpVector * HalfHeight) + FVector::UpVector * (HalfDashLength + (DashIndex * DashLengthAndGaps)),
FVector(1, 0, 0),
FVector(0, 1, 0),
FVector(0, 0, 1),
Radius,
HalfDashLength,
NumSides,
UseMaterial->GetRenderProxy(),
SDPG_Foreground);
}
}
else
{
DrawCylinder(
PDI,
RenderLocalToWorldTransform.ToMatrixWithScale(),
FVector::ZeroVector,
FVector(1, 0, 0),
FVector(0, 1, 0),
FVector(0, 0, 1),
Radius,
HalfHeight,
NumSides,
UseMaterial->GetRenderProxy(),
SDPG_Foreground);
}
}
}
}
FInputRayHit UGizmoElementCylinder::LineTrace(const UGizmoViewContext* ViewContext, const FLineTraceTraversalState& LineTraceState, const FVector& RayOrigin, const FVector& RayDirection)
{
FLineTraceTraversalState CurrentLineTraceState(LineTraceState);
bool bHittableViewDependent = UpdateLineTraceState(ViewContext, Base, CurrentLineTraceState);
if (bHittableViewDependent)
{
bool bIntersects = false;
double RayParam = 0.0;
const double PixelHitThresholdAdjust = CurrentLineTraceState.PixelToWorldScale * PixelHitDistanceThreshold;
const double WorldHeight = Height * CurrentLineTraceState.LocalToWorldTransform.GetScale3D().X + PixelHitThresholdAdjust * 2.0;
const double WorldRadius = Radius * CurrentLineTraceState.LocalToWorldTransform.GetScale3D().X + PixelHitThresholdAdjust;
const FVector WorldDirection = CurrentLineTraceState.LocalToWorldTransform.TransformVectorNoScale(Direction);
const FVector LocalCenter = Direction * Height * 0.5;
const FVector WorldCenter = CurrentLineTraceState.LocalToWorldTransform.TransformPosition(LocalCenter);
// due to numerical imprecision, the ray origin needs to be clamped in ortho views
// (cf. UEditorInteractiveToolsContext::GetRayFromMousePos)
FVector ClampedRayOrigin(RayOrigin);
const double DepthBias = UE::GizmoUtil::ClampRayOrigin(ViewContext, ClampedRayOrigin, RayDirection);
GizmoMath::RayCylinderIntersection(
WorldCenter,
WorldDirection,
WorldRadius,
WorldHeight,
ClampedRayOrigin, RayDirection,
bIntersects, RayParam);
if (bIntersects)
{
// add the depth bias if any
RayParam += DepthBias;
FInputRayHit RayHit(static_cast<float>(RayParam));
RayHit.SetHitObject(this);
RayHit.HitIdentifier = PartIdentifier;
return RayHit;
}
}
return FInputRayHit();
}
void UGizmoElementCylinder::SetBase(const FVector& InBase)
{
Base = InBase;
}
FVector UGizmoElementCylinder::GetBase() const
{
return Base;
}
void UGizmoElementCylinder::SetDirection(const FVector& InDirection)
{
Direction = InDirection;
Direction.Normalize();
}
FVector UGizmoElementCylinder::GetDirection() const
{
return Direction;
}
void UGizmoElementCylinder::SetHeight(float InHeight)
{
Height = InHeight;
}
float UGizmoElementCylinder::GetHeight() const
{
return Height;
}
void UGizmoElementCylinder::SetRadius(float InRadius)
{
Radius = InRadius;
}
float UGizmoElementCylinder::GetRadius() const
{
return Radius;
}
void UGizmoElementCylinder::SetNumSides(int32 InNumSides)
{
NumSides = InNumSides;
}
int32 UGizmoElementCylinder::GetNumSides() const
{
return NumSides;
}
void UGizmoElementCylinder::SetIsDashed(bool bInDashing)
{
bIsDashed = bInDashing;
}
bool UGizmoElementCylinder::GetIsDashed() const
{
return bIsDashed;
}
void UGizmoElementCylinder::SetDashParameters(const float InDashLength, const TOptional<float>& InGapLength)
{
DashLength = InDashLength;
DashGapLength = InGapLength.Get(InDashLength * 0.5f);
}
void UGizmoElementCylinder::GetDashParameters(float& OutDashLength, float& OutGapLength) const
{
OutDashLength = DashLength;
OutGapLength = DashGapLength;
}