2020-01-27 20:11:15 -05:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "BaseGizmos/GizmoBoxComponent.h"
|
|
|
|
|
#include "BaseGizmos/GizmoRenderingUtil.h"
|
2021-06-09 16:28:33 -04:00
|
|
|
#include "BaseGizmos/GizmoViewContext.h"
|
2020-01-27 20:11:15 -05:00
|
|
|
#include "PrimitiveSceneProxy.h"
|
2022-03-17 14:03:58 -04:00
|
|
|
#include "SceneManagement.h" // FMeshElementCollector, FPrimitiveDrawInterface
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2021-06-09 16:28:33 -04:00
|
|
|
namespace GizmoBoxComponentLocals
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
template <typename SceneViewOrGizmoViewContext>
|
|
|
|
|
void GetBoxDirections(bool bIsViewDependent, const SceneViewOrGizmoViewContext* View, const FVector& WorldOrigin,
|
|
|
|
|
const FVector& DirectionX, const FVector& DirectionY, const FVector& DirectionZ,
|
|
|
|
|
bool bEnableFlipping, bool bWorldAxis,
|
|
|
|
|
TFunctionRef<FVector(const FVector&)> VectorTransform,
|
|
|
|
|
FVector& UseDirectionXOut, FVector& UseDirectionYOut, FVector& UseDirectionZOut,
|
|
|
|
|
bool& bFlippedXOut, bool& bFlippedYOut, bool& bFlippedZOut
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
UseDirectionXOut = (bWorldAxis) ? DirectionX : VectorTransform(DirectionX);
|
|
|
|
|
UseDirectionYOut = (bWorldAxis) ? DirectionY : VectorTransform(DirectionY);
|
|
|
|
|
UseDirectionZOut = (bWorldAxis) ? DirectionZ : VectorTransform(DirectionZ);
|
|
|
|
|
bFlippedXOut = false;
|
|
|
|
|
bFlippedYOut = false;
|
|
|
|
|
bFlippedZOut = false;
|
|
|
|
|
|
|
|
|
|
if (bIsViewDependent)
|
|
|
|
|
{
|
|
|
|
|
// direction to origin of gizmo
|
|
|
|
|
FVector ViewDirection =
|
|
|
|
|
View->IsPerspectiveProjection() ? WorldOrigin - View->ViewLocation : View->GetViewDirection();
|
|
|
|
|
ViewDirection.Normalize();
|
|
|
|
|
|
|
|
|
|
bFlippedXOut = (FVector::DotProduct(ViewDirection, UseDirectionXOut) > 0);
|
|
|
|
|
UseDirectionXOut = (bEnableFlipping && bFlippedXOut) ? -UseDirectionXOut : UseDirectionXOut;
|
|
|
|
|
|
|
|
|
|
bFlippedYOut = (FVector::DotProduct(ViewDirection, UseDirectionYOut) > 0);
|
|
|
|
|
UseDirectionYOut = (bEnableFlipping && bFlippedYOut) ? -UseDirectionYOut : UseDirectionYOut;
|
|
|
|
|
|
|
|
|
|
bFlippedZOut = (FVector::DotProduct(ViewDirection, UseDirectionZOut) > 0);
|
|
|
|
|
UseDirectionZOut = (bEnableFlipping && bFlippedZOut) ? -UseDirectionZOut : UseDirectionZOut;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
class FGizmoBoxComponentSceneProxy final : public FPrimitiveSceneProxy
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SIZE_T GetTypeHash() const override
|
|
|
|
|
{
|
|
|
|
|
static size_t UniquePointer;
|
|
|
|
|
return reinterpret_cast<size_t>(&UniquePointer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FGizmoBoxComponentSceneProxy(const UGizmoBoxComponent* InComponent)
|
|
|
|
|
: FPrimitiveSceneProxy(InComponent),
|
|
|
|
|
Color(InComponent->Color),
|
|
|
|
|
LocalCenter(InComponent->Origin),
|
|
|
|
|
DirectionX(InComponent->Rotation*FVector(1,0,0)),
|
|
|
|
|
DirectionY(InComponent->Rotation*FVector(0,1,0)),
|
|
|
|
|
DirectionZ(InComponent->Rotation*FVector(0,0,1)),
|
|
|
|
|
Dimensions(InComponent->Dimensions),
|
|
|
|
|
Thickness(InComponent->LineThickness),
|
|
|
|
|
HoverThicknessMultiplier(InComponent->HoverSizeMultiplier),
|
|
|
|
|
bEnableFlipping(InComponent->bEnableAxisFlip),
|
|
|
|
|
bRemoveHiddenLines(InComponent->bRemoveHiddenLines)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
|
|
|
|
|
{
|
2021-06-09 16:28:33 -04:00
|
|
|
using namespace GizmoBoxComponentLocals;
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
const FMatrix& LocalToWorldMatrix = GetLocalToWorld();
|
|
|
|
|
FVector WorldOrigin = LocalToWorldMatrix.TransformPosition(FVector::ZeroVector);
|
|
|
|
|
|
|
|
|
|
FVector Points[8]; // box corners, order is 000, 100, 110, 010, 001, 101, 111, 011
|
|
|
|
|
static const int Lines[12][2] = {
|
|
|
|
|
{0,1}, {1,2}, {2,3}, {3,0},
|
|
|
|
|
{4,5}, {5,6}, {6,7}, {7,4},
|
|
|
|
|
{0,4}, {1,5}, {2,6}, {3,7}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
|
|
|
|
|
{
|
|
|
|
|
if (VisibilityMap & (1 << ViewIndex))
|
|
|
|
|
{
|
|
|
|
|
const FSceneView* View = Views[ViewIndex];
|
|
|
|
|
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
|
2020-06-23 18:40:00 -04:00
|
|
|
bool bIsOrtho = !View->IsPerspectiveProjection();
|
|
|
|
|
|
|
|
|
|
bool bWorldAxis = (bExternalWorldLocalState) ? (*bExternalWorldLocalState) : false;
|
|
|
|
|
|
2021-06-09 16:28:33 -04:00
|
|
|
FVector UseDirectionX, UseDirectionY, UseDirectionZ;
|
|
|
|
|
bool bFlippedX = false;
|
|
|
|
|
bool bFlippedY = false;
|
|
|
|
|
bool bFlippedZ = false;
|
2020-06-23 18:40:00 -04:00
|
|
|
|
2021-06-09 16:28:33 -04:00
|
|
|
bool bIsViewDependent = (bExternalIsViewDependent) ? (*bExternalIsViewDependent) : false;
|
|
|
|
|
GetBoxDirections(bIsViewDependent, View, WorldOrigin, DirectionX, DirectionY, DirectionZ, bEnableFlipping,
|
|
|
|
|
bWorldAxis, [&LocalToWorldMatrix](const FVector& VectorIn) {
|
|
|
|
|
return FVector{ LocalToWorldMatrix.TransformVector(VectorIn) };
|
|
|
|
|
}, UseDirectionX, UseDirectionY, UseDirectionZ, bFlippedX, bFlippedY, bFlippedZ);
|
2020-06-23 18:40:00 -04:00
|
|
|
|
|
|
|
|
FVector UseCenter(
|
|
|
|
|
(bFlippedX) ? -LocalCenter.X : LocalCenter.X,
|
|
|
|
|
(bFlippedY) ? -LocalCenter.Y : LocalCenter.Y,
|
|
|
|
|
(bFlippedZ) ? -LocalCenter.Z : LocalCenter.Z
|
|
|
|
|
);
|
2021-06-09 16:28:33 -04:00
|
|
|
|
|
|
|
|
float LengthScale = 1;
|
|
|
|
|
if (bIsViewDependent)
|
|
|
|
|
{
|
|
|
|
|
LengthScale = GizmoRenderingUtil::CalculateLocalPixelToWorldScale(View, WorldOrigin);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 18:40:00 -04:00
|
|
|
FVector WorldCenter = WorldOrigin
|
|
|
|
|
+ LengthScale * LocalCenter.X * UseDirectionX
|
|
|
|
|
+ LengthScale * LocalCenter.Y * UseDirectionY
|
|
|
|
|
+ LengthScale * LocalCenter.Z * UseDirectionZ;
|
|
|
|
|
//= LocalToWorldMatrix.TransformPosition(LengthScale*UseCenter);
|
|
|
|
|
|
|
|
|
|
float UseThickness = (bExternalHoverState != nullptr && *bExternalHoverState == true) ?
|
|
|
|
|
(HoverThicknessMultiplier * Thickness) : (Thickness);
|
|
|
|
|
if (!bIsOrtho)
|
|
|
|
|
{
|
|
|
|
|
UseThickness *= (View->FOV / 90.0); // compensate for FOV scaling in Gizmos...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double DimensionX = LengthScale * Dimensions.X * 0.5f;
|
|
|
|
|
double DimensionY = LengthScale * Dimensions.Y * 0.5f;
|
|
|
|
|
double DimensionZ = LengthScale * Dimensions.Z * 0.5f;
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
Points[0] = - DimensionX*UseDirectionX - DimensionY*UseDirectionY - DimensionZ*UseDirectionZ;
|
|
|
|
|
Points[1] = + DimensionX*UseDirectionX - DimensionY*UseDirectionY - DimensionZ*UseDirectionZ;
|
|
|
|
|
Points[2] = + DimensionX*UseDirectionX + DimensionY*UseDirectionY - DimensionZ*UseDirectionZ;
|
|
|
|
|
Points[3] = - DimensionX*UseDirectionX + DimensionY*UseDirectionY - DimensionZ*UseDirectionZ;
|
|
|
|
|
|
|
|
|
|
Points[4] = - DimensionX*UseDirectionX - DimensionY*UseDirectionY + DimensionZ*UseDirectionZ;
|
|
|
|
|
Points[5] = + DimensionX*UseDirectionX - DimensionY*UseDirectionY + DimensionZ*UseDirectionZ;
|
|
|
|
|
Points[6] = + DimensionX*UseDirectionX + DimensionY*UseDirectionY + DimensionZ*UseDirectionZ;
|
|
|
|
|
Points[7] = - DimensionX*UseDirectionX + DimensionY*UseDirectionY + DimensionZ*UseDirectionZ;
|
|
|
|
|
|
|
|
|
|
if (bRemoveHiddenLines)
|
|
|
|
|
{
|
2021-06-09 16:28:33 -04:00
|
|
|
FVector ViewDirection =
|
|
|
|
|
View->IsPerspectiveProjection() ? WorldOrigin - View->ViewLocation : View->GetViewDirection();
|
|
|
|
|
|
2020-01-27 20:11:15 -05:00
|
|
|
// find box corner direction that is most aligned with view direction. That's the corner we will hide.
|
|
|
|
|
float MaxDot = -999999;
|
|
|
|
|
int MaxDotIndex = -1;
|
|
|
|
|
for (int j = 0; j < 8; ++j)
|
|
|
|
|
{
|
|
|
|
|
float Dot = FVector::DotProduct(Points[j], ViewDirection);
|
|
|
|
|
if (Dot > MaxDot)
|
|
|
|
|
{
|
|
|
|
|
MaxDot = Dot;
|
|
|
|
|
MaxDotIndex = j;
|
|
|
|
|
}
|
|
|
|
|
Points[j] += WorldCenter;
|
|
|
|
|
}
|
|
|
|
|
for (int j = 0; j < 12; ++j)
|
|
|
|
|
{
|
|
|
|
|
if (Lines[j][0] != MaxDotIndex && Lines[j][1] != MaxDotIndex)
|
|
|
|
|
{
|
|
|
|
|
PDI->DrawLine(Points[Lines[j][0]], Points[Lines[j][1]], Color, SDPG_Foreground, UseThickness, 0.0f, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int j = 0; j < 8; ++j)
|
|
|
|
|
{
|
|
|
|
|
Points[j] += WorldCenter;
|
|
|
|
|
}
|
|
|
|
|
for (int j = 0; j < 12; ++j)
|
|
|
|
|
{
|
|
|
|
|
PDI->DrawLine(Points[Lines[j][0]], Points[Lines[j][1]], 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 sizeof *this + GetAllocatedSize(); }
|
|
|
|
|
uint32 GetAllocatedSize(void) const { return FPrimitiveSceneProxy::GetAllocatedSize(); }
|
|
|
|
|
|
|
|
|
|
void SetExternalHoverState(bool* HoverState)
|
|
|
|
|
{
|
|
|
|
|
bExternalHoverState = HoverState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetExternalWorldLocalState(bool* bWorldLocalState)
|
|
|
|
|
{
|
|
|
|
|
bExternalWorldLocalState = bWorldLocalState;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-09 16:28:33 -04:00
|
|
|
void SetExternalIsViewDependent(bool* bExternalIsViewDependentIn)
|
|
|
|
|
{
|
|
|
|
|
bExternalIsViewDependent = bExternalIsViewDependentIn;
|
|
|
|
|
}
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
FLinearColor Color;
|
|
|
|
|
FVector LocalCenter;
|
|
|
|
|
FVector DirectionX, DirectionY, DirectionZ;;
|
|
|
|
|
FVector Dimensions;
|
|
|
|
|
float Thickness;
|
|
|
|
|
float HoverThicknessMultiplier;
|
|
|
|
|
bool bEnableFlipping = false;
|
|
|
|
|
bool bRemoveHiddenLines = true;
|
|
|
|
|
|
2020-06-23 18:40:00 -04:00
|
|
|
// set on Component for use in ::GetDynamicMeshElements()
|
|
|
|
|
bool* bExternalHoverState = nullptr;
|
|
|
|
|
bool* bExternalWorldLocalState = nullptr;
|
2021-06-09 16:28:33 -04:00
|
|
|
bool* bExternalIsViewDependent = nullptr;
|
2020-01-27 20:11:15 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FPrimitiveSceneProxy* UGizmoBoxComponent::CreateSceneProxy()
|
|
|
|
|
{
|
|
|
|
|
FGizmoBoxComponentSceneProxy* NewProxy = new FGizmoBoxComponentSceneProxy(this);
|
|
|
|
|
NewProxy->SetExternalHoverState(&bHovering);
|
|
|
|
|
NewProxy->SetExternalWorldLocalState(&bWorld);
|
2021-06-09 16:28:33 -04:00
|
|
|
NewProxy->SetExternalIsViewDependent(&bIsViewDependent);
|
2020-01-27 20:11:15 -05:00
|
|
|
return NewProxy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FBoxSphereBounds UGizmoBoxComponent::CalcBounds(const FTransform& LocalToWorld) const
|
|
|
|
|
{
|
|
|
|
|
return FBoxSphereBounds(FSphere(Origin, Dimensions.Size()).TransformBy(LocalToWorld));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UGizmoBoxComponent::LineTraceComponent(FHitResult& OutHit, const FVector Start, const FVector End, const FCollisionQueryParams& Params)
|
|
|
|
|
{
|
2021-06-09 16:28:33 -04:00
|
|
|
using namespace GizmoBoxComponentLocals;
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
const FTransform& Transform = this->GetComponentToWorld();
|
2021-07-19 10:47:22 -04:00
|
|
|
FTransform TransformToUse = Transform;
|
|
|
|
|
if (bWorld)
|
|
|
|
|
{
|
|
|
|
|
TransformToUse.SetRotation(FQuat::Identity);
|
|
|
|
|
}
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
// transform points into local space
|
2021-07-19 10:47:22 -04:00
|
|
|
FVector StartLocal = TransformToUse.InverseTransformPosition(Start);
|
|
|
|
|
FVector EndLocal = TransformToUse.InverseTransformPosition(End);
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2021-07-19 10:47:22 -04:00
|
|
|
// transform into box-specific rotation space
|
|
|
|
|
FQuat InvBoxRotation = Rotation.Inverse();
|
|
|
|
|
StartLocal = InvBoxRotation * StartLocal;
|
|
|
|
|
EndLocal = InvBoxRotation * EndLocal;
|
2020-01-27 20:11:15 -05:00
|
|
|
|
2021-06-09 16:28:33 -04:00
|
|
|
bool bFlippedX = false;
|
|
|
|
|
bool bFlippedY = false;
|
|
|
|
|
bool bFlippedZ = false;
|
|
|
|
|
|
2021-07-14 11:44:25 -04:00
|
|
|
FVector WorldOrigin; // Only used if bIsViewDependent
|
2021-06-09 16:28:33 -04:00
|
|
|
if (bIsViewDependent)
|
|
|
|
|
{
|
2021-07-19 10:47:22 -04:00
|
|
|
// It doesn't matter whether we use TransformToUse or Transform in this block since
|
|
|
|
|
// the rotation component does not affect world origin and the vector transforms in
|
|
|
|
|
// GetBoxDirections are only used if bWorld is false. We'll use the original transform
|
|
|
|
|
// here just to line up with what our scene proxy is doing.
|
|
|
|
|
|
2021-07-14 11:44:25 -04:00
|
|
|
WorldOrigin = Transform.TransformPosition(FVector::ZeroVector);
|
|
|
|
|
|
2021-06-09 16:28:33 -04:00
|
|
|
FVector UseDirectionX, UseDirectionY, UseDirectionZ; // not used
|
2021-06-11 10:09:47 -04:00
|
|
|
GetBoxDirections(bIsViewDependent, ToRawPtr(GizmoViewContext),
|
2021-07-14 11:44:25 -04:00
|
|
|
WorldOrigin,
|
2021-06-09 16:28:33 -04:00
|
|
|
Rotation * FVector(1, 0, 0), // DirectionX
|
|
|
|
|
Rotation * FVector(0, 1, 0), // DirectionY
|
|
|
|
|
Rotation * FVector(0, 0, 1), // DirectionZ
|
|
|
|
|
bEnableAxisFlip, bWorld,
|
|
|
|
|
[&Transform](const FVector& VectorIn) { return Transform.TransformVector(VectorIn); },
|
|
|
|
|
UseDirectionX, UseDirectionY, UseDirectionZ, bFlippedX, bFlippedY, bFlippedZ);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-14 11:44:25 -04:00
|
|
|
FVector UseCenter(
|
2020-01-27 20:11:15 -05:00
|
|
|
(bEnableAxisFlip && bFlippedX) ? -Origin.X : Origin.X,
|
|
|
|
|
(bEnableAxisFlip && bFlippedY) ? -Origin.Y : Origin.Y,
|
|
|
|
|
(bEnableAxisFlip && bFlippedZ) ? -Origin.Z : Origin.Z
|
|
|
|
|
);
|
2021-06-09 16:28:33 -04:00
|
|
|
|
|
|
|
|
float DynamicPixelToWorldScale = 1;
|
|
|
|
|
|
|
|
|
|
if (bIsViewDependent)
|
|
|
|
|
{
|
|
|
|
|
DynamicPixelToWorldScale = GizmoRenderingUtil::CalculateLocalPixelToWorldScale(
|
2021-07-14 11:44:25 -04:00
|
|
|
GizmoViewContext, WorldOrigin);
|
2021-06-09 16:28:33 -04:00
|
|
|
}
|
|
|
|
|
|
2021-07-14 11:44:25 -04:00
|
|
|
UseCenter *= DynamicPixelToWorldScale;
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
FVector ScaledDims = DynamicPixelToWorldScale * Dimensions;
|
2021-07-14 11:44:25 -04:00
|
|
|
FBox Box(UseCenter - 0.5f * ScaledDims, UseCenter + 0.5f * ScaledDims);
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
FVector Extent(SMALL_NUMBER, SMALL_NUMBER, SMALL_NUMBER);
|
|
|
|
|
FVector HitLocal, NormalLocal; float HitTime;
|
|
|
|
|
if (FMath::LineExtentBoxIntersection(Box, StartLocal, EndLocal, Extent, HitLocal, NormalLocal, HitTime) == false)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-19 10:47:22 -04:00
|
|
|
FVector HitWorld = TransformToUse.TransformPosition(Rotation * HitLocal);
|
2020-01-27 20:11:15 -05:00
|
|
|
|
|
|
|
|
OutHit.Component = this;
|
|
|
|
|
OutHit.ImpactPoint = HitWorld;
|
|
|
|
|
OutHit.Distance = FVector::Distance(Start, HitWorld);
|
|
|
|
|
//OutHit.ImpactNormal = ;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UGizmoBoxComponent::GetUsedMaterials(TArray<UMaterialInterface*>& OutMaterials, bool bGetDebugMaterials) const
|
|
|
|
|
{
|
|
|
|
|
//OutMaterials.Add(GEngine->VertexColorMaterial);
|
|
|
|
|
}
|