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

219 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "BaseGizmos/GizmoArrowComponent.h"
#include "BaseGizmos/GizmoRenderingUtil.h"
#include "BaseGizmos/GizmoViewContext.h"
#include "MeshElementCollector.h"
#include "PrimitiveSceneProxy.h"
#include "Materials/Material.h"
#include "MaterialShared.h"
#include "PrimitiveDrawingUtils.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GizmoArrowComponent)
namespace GizmoArrowComponentLocals
{
const float ARROW_RENDERVISIBILITY_DOT_THRESHOLD = 0.965f; //~15 degrees
template <typename SceneViewOrGizmoViewContext>
bool GetWorldEndpoints(bool bIsViewDependent, const SceneViewOrGizmoViewContext* View,
const FVector& WorldOrigin, const FVector& Direction, float Gap,
float Length, bool bUseWorldAxes,
TFunctionRef<FVector(const FVector&)> VectorTransform,
FVector& StartPointOut, FVector& EndPointOut, float& PixelToWorldScaleOut)
{
FVector ArrowDirection = bUseWorldAxes ? Direction : VectorTransform(Direction);
float LengthScale = 1;
if (bIsViewDependent)
{
// direction to origin of gizmo
FVector ViewDirection = View->IsPerspectiveProjection() ?
WorldOrigin - View->ViewLocation : View->GetViewDirection();
ViewDirection.Normalize();
bool bFlipped = (FVector::DotProduct(ViewDirection, ArrowDirection) > 0);
ArrowDirection = (bFlipped) ? -ArrowDirection : ArrowDirection;
//bRenderVisibilityOut = FMath::Abs(FVector::DotProduct(ArrowDirection, ViewDirection)) < 0.985f; // ~10 degrees
bool bRenderVisibility =
FMath::Abs(FVector::DotProduct(ArrowDirection, ViewDirection)) < ARROW_RENDERVISIBILITY_DOT_THRESHOLD; // ~15 degrees
if (!bRenderVisibility)
{
return false;
}
LengthScale = UE::GizmoRenderingUtil::CalculateLocalPixelToWorldScale(View, WorldOrigin);
}
PixelToWorldScaleOut = LengthScale;
double StartDist = LengthScale * Gap;
double EndDist = LengthScale * (Gap + Length);
StartPointOut = WorldOrigin + StartDist * ArrowDirection;
EndPointOut = WorldOrigin + EndDist * ArrowDirection;
return true;
}
}
class FGizmoArrowComponentSceneProxy final : public FPrimitiveSceneProxy
{
public:
SIZE_T GetTypeHash() const override
{
static size_t UniquePointer;
return reinterpret_cast<size_t>(&UniquePointer);
}
FGizmoArrowComponentSceneProxy(const UGizmoArrowComponent* InComponent)
: FPrimitiveSceneProxy(InComponent),
Color(InComponent->Color),
Direction(InComponent->Direction),
Gap(InComponent->Gap),
Length(InComponent->Length),
Thickness(InComponent->Thickness),
HoverThicknessMultiplier(InComponent->HoverSizeMultiplier)
{
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
using namespace GizmoArrowComponentLocals;
const FMatrix& LocalToWorldMatrix = GetLocalToWorld();
FVector Origin = LocalToWorldMatrix.TransformPosition(FVector::ZeroVector);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
bool bWorldAxis = (bExternalWorldLocalState) ? (*bExternalWorldLocalState) : false;
FVector StartPoint, EndPoint;
float PixelToWorldScale = 0;
bool bIsViewDependent = (bExternalIsViewDependent) ? (*bExternalIsViewDependent) : false;
bool bRenderVisibility = GetWorldEndpoints(bIsViewDependent, View, Origin,
Direction, Gap, Length, bWorldAxis,
[&LocalToWorldMatrix](const FVector& VectorIn) {
return FVector{ LocalToWorldMatrix.TransformVector(VectorIn) };
}, StartPoint, EndPoint, PixelToWorldScale);
if (!bRenderVisibility)
{
continue;
}
float UseThickness = (bExternalHoverState != nullptr && *bExternalHoverState == true) ?
(HoverThicknessMultiplier * Thickness) : (Thickness);
if (View->IsPerspectiveProjection())
{
UseThickness *= (View->FOV / 90.0f); // compensate for FOV scaling in Gizmos...
}
PDI->DrawLine(StartPoint, EndPoint, Color, SDPG_Foreground, UseThickness, 0.0f, true);
}
}
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const override
{
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View);
Result.bDynamicRelevance = true;
Result.bShadowRelevance = false;
Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
Result.bRenderCustomDepth = ShouldRenderCustomDepth();
return Result;
}
virtual bool CanBeOccluded() const override
{
return false;
}
virtual uint32 GetMemoryFootprint(void) const override { return IntCastChecked<uint32>(sizeof *this + GetAllocatedSize()); }
SIZE_T GetAllocatedSize(void) const { return FPrimitiveSceneProxy::GetAllocatedSize(); }
void SetExternalHoverState(bool* HoverState)
{
bExternalHoverState = HoverState;
}
void SetExternalWorldLocalState(bool* bWorldLocalState)
{
bExternalWorldLocalState = bWorldLocalState;
}
void SetExternalIsViewDependent(bool* bExternalIsViewDependentIn)
{
bExternalIsViewDependent = bExternalIsViewDependentIn;
}
private:
FLinearColor Color;
FVector Direction;
float Gap;
float Length;
float Thickness;
float HoverThicknessMultiplier;
// set on Component for use in ::GetDynamicMeshElements()
bool* bExternalHoverState = nullptr;
bool* bExternalWorldLocalState = nullptr;
bool* bExternalIsViewDependent = nullptr;
};
FPrimitiveSceneProxy* UGizmoArrowComponent::CreateSceneProxy()
{
FGizmoArrowComponentSceneProxy* NewProxy = new FGizmoArrowComponentSceneProxy(this);
NewProxy->SetExternalHoverState(&bHovering);
NewProxy->SetExternalWorldLocalState(&bWorld);
NewProxy->SetExternalIsViewDependent(&bIsViewDependent);
return NewProxy;
}
FBoxSphereBounds UGizmoArrowComponent::CalcBounds(const FTransform& LocalToWorld) const
{
return FBoxSphereBounds(FSphere(FVector::ZeroVector, Gap+Length).TransformBy(LocalToWorld));
}
bool UGizmoArrowComponent::LineTraceComponent(FHitResult& OutHit, const FVector Start, const FVector End, const FCollisionQueryParams& Params)
{
using namespace GizmoArrowComponentLocals;
const FTransform& Transform = this->GetComponentToWorld();
FVector UseOrigin = Transform.TransformPosition(FVector::ZeroVector);
// Copy what is done in the proxy object, but get data from the gizmo view context.
FVector StartPoint = FVector::ZeroVector, EndPoint = FVector::ZeroVector; // initialized to appease CIS
float PixelToWorldScale = 0;
bool bRenderVisibility = GetWorldEndpoints(bIsViewDependent, ToRawPtr(GizmoViewContext), UseOrigin, Direction, Gap, Length, bWorld,
[&Transform](const FVector& VectorIn) { return Transform.TransformVector(VectorIn); },
StartPoint, EndPoint, PixelToWorldScale);
if (!bRenderVisibility)
{
return false;
}
FVector NearestArrow, NearestLine;
FMath::SegmentDistToSegmentSafe(StartPoint, EndPoint, Start, End, NearestArrow, NearestLine);
double Distance = FVector::Distance(NearestArrow, NearestLine);
if (Distance > PixelHitDistanceThreshold * PixelToWorldScale)
{
return false;
}
OutHit.Component = this;
OutHit.Distance = static_cast<float>(FVector::Distance(Start, NearestLine));
OutHit.ImpactPoint = NearestLine;
return true;
}