358 lines
13 KiB
C++
358 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "ComponentVisualizer.h"
|
|
#include "CoreMinimal.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "HitProxies.h"
|
|
#include "ComponentVisualizer.h"
|
|
#include "UObject/GCObject.h"
|
|
#include "ZoneShapeComponent.h"
|
|
#include "ZoneShapeComponentVisualizer.generated.h"
|
|
|
|
class AActor;
|
|
class FEditorViewportClient;
|
|
class FMenuBuilder;
|
|
class FPrimitiveDrawInterface;
|
|
class FSceneView;
|
|
class FUICommandList;
|
|
class FViewport;
|
|
class SWidget;
|
|
struct FViewportClick;
|
|
struct FConvexVolume;
|
|
|
|
UENUM()
|
|
enum class FZoneShapeControlPointType : uint8
|
|
{
|
|
None,
|
|
In,
|
|
Out,
|
|
};
|
|
|
|
/** Selection state data that will be captured by scoped transactions.*/
|
|
UCLASS(Transient)
|
|
class ZONEGRAPHEDITOR_API UZoneShapeComponentVisualizerSelectionState : public UObject
|
|
{
|
|
GENERATED_BODY()
|
|
public:
|
|
FComponentPropertyPath GetShapePropertyPath() const { return ShapePropertyPath; }
|
|
void SetShapePropertyPath(const FComponentPropertyPath& InShapePropertyPath) { ShapePropertyPath = InShapePropertyPath; }
|
|
const TSet<int32>& GetSelectedPoints() const { return SelectedPoints; }
|
|
TSet<int32>& ModifySelectedPoints() { return SelectedPoints; }
|
|
int32 GetLastPointIndexSelected() const { return LastPointIndexSelected; }
|
|
void SetLastPointIndexSelected(const int32 InLastPointIndexSelected) { LastPointIndexSelected = InLastPointIndexSelected; }
|
|
int32 GetSelectedSegmentIndex() const { return SelectedSegmentIndex; }
|
|
void SetSelectedSegmentIndex(const int32 InSelectedSegmentIndex) { SelectedSegmentIndex = InSelectedSegmentIndex; }
|
|
FVector GetSelectedSegmentPoint() const { return SelectedSegmentPoint; }
|
|
void SetSelectedSegmentPoint(const FVector& InSelectedSegmentPoint) { SelectedSegmentPoint = InSelectedSegmentPoint; }
|
|
float GetSelectedSegmentT() const { return SelectedSegmentT; }
|
|
void SetSelectedSegmentT(const float InSelectedSegmentT) { SelectedSegmentT = InSelectedSegmentT; }
|
|
int32 GetSelectedControlPoint() const { return SelectedControlPoint; }
|
|
void SetSelectedControlPoint(const int32 InSelectedControlPoint) { SelectedControlPoint = InSelectedControlPoint; }
|
|
FZoneShapeControlPointType GetSelectedControlPointType() const { return SelectedControlPointType; }
|
|
void SetSelectedControlPointType(const FZoneShapeControlPointType InSelectedControlPointType) { SelectedControlPointType = InSelectedControlPointType; }
|
|
|
|
protected:
|
|
/** Property path from the parent actor to the component */
|
|
UPROPERTY()
|
|
FComponentPropertyPath ShapePropertyPath;
|
|
/** Index of keys we have selected */
|
|
UPROPERTY()
|
|
TSet<int32> SelectedPoints;
|
|
/** Index of the last key we selected */
|
|
UPROPERTY()
|
|
int32 LastPointIndexSelected = 0;
|
|
/** Index of segment we have selected */
|
|
UPROPERTY()
|
|
int32 SelectedSegmentIndex = 0;
|
|
/** Position on selected segment */
|
|
UPROPERTY()
|
|
FVector SelectedSegmentPoint = FVector::ZeroVector;
|
|
/** Interpolation value along the selected segment */
|
|
UPROPERTY()
|
|
float SelectedSegmentT = 0.0f;
|
|
/** Index of tangent handle we have selected */
|
|
UPROPERTY()
|
|
int32 SelectedControlPoint = 0;
|
|
/** The type of the selected tangent handle */
|
|
UPROPERTY()
|
|
FZoneShapeControlPointType SelectedControlPointType = FZoneShapeControlPointType::None;
|
|
};
|
|
|
|
/** Base class for clickable shape editing proxies */
|
|
struct ZONEGRAPHEDITOR_API HZoneShapeVisProxy : public HComponentVisProxy
|
|
{
|
|
DECLARE_HIT_PROXY();
|
|
|
|
HZoneShapeVisProxy(const UActorComponent* InComponent, EHitProxyPriority InPriority = HPP_Wireframe)
|
|
: HComponentVisProxy(InComponent, InPriority)
|
|
{}
|
|
|
|
virtual EMouseCursor::Type GetMouseCursor() override
|
|
{
|
|
return EMouseCursor::CardinalCross;
|
|
}
|
|
};
|
|
|
|
/** Proxy for a shape point */
|
|
struct ZONEGRAPHEDITOR_API HZoneShapePointProxy : public HZoneShapeVisProxy
|
|
{
|
|
DECLARE_HIT_PROXY();
|
|
|
|
HZoneShapePointProxy(const UActorComponent* InComponent, int32 InPointIndex)
|
|
: HZoneShapeVisProxy(InComponent, HPP_Foreground)
|
|
, PointIndex(InPointIndex)
|
|
{}
|
|
|
|
virtual EMouseCursor::Type GetMouseCursor() override
|
|
{
|
|
return EMouseCursor::CardinalCross;
|
|
}
|
|
|
|
int32 PointIndex;
|
|
};
|
|
|
|
/** Proxy for a shape segment */
|
|
struct ZONEGRAPHEDITOR_API HZoneShapeSegmentProxy : public HZoneShapeVisProxy
|
|
{
|
|
DECLARE_HIT_PROXY();
|
|
|
|
HZoneShapeSegmentProxy(const UActorComponent* InComponent, int32 InSegmentIndex)
|
|
: HZoneShapeVisProxy(InComponent)
|
|
, SegmentIndex(InSegmentIndex)
|
|
{}
|
|
|
|
virtual EMouseCursor::Type GetMouseCursor() override
|
|
{
|
|
return EMouseCursor::CardinalCross;
|
|
}
|
|
|
|
int32 SegmentIndex;
|
|
};
|
|
|
|
/** Proxy for a control point handle */
|
|
struct ZONEGRAPHEDITOR_API HZoneShapeControlPointProxy : public HZoneShapeVisProxy
|
|
{
|
|
DECLARE_HIT_PROXY();
|
|
|
|
HZoneShapeControlPointProxy(const UActorComponent* InComponent, int32 InPointIndex, bool bInInControlPoint)
|
|
: HZoneShapeVisProxy(InComponent, HPP_Foreground)
|
|
, PointIndex(InPointIndex)
|
|
, bInControlPoint(bInInControlPoint)
|
|
{}
|
|
|
|
virtual EMouseCursor::Type GetMouseCursor() override
|
|
{
|
|
return EMouseCursor::CardinalCross;
|
|
}
|
|
|
|
int32 PointIndex;
|
|
bool bInControlPoint;
|
|
};
|
|
|
|
/** ZoneShapeComponent visualizer/edit functionality */
|
|
class ZONEGRAPHEDITOR_API FZoneShapeComponentVisualizer : public FComponentVisualizer, public FGCObject
|
|
{
|
|
public:
|
|
FZoneShapeComponentVisualizer();
|
|
virtual ~FZoneShapeComponentVisualizer();
|
|
|
|
//~ Begin FComponentVisualizer Interface
|
|
virtual void OnRegister() override;
|
|
virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) override;
|
|
virtual bool VisProxyHandleClick(FEditorViewportClient* InViewportClient, HComponentVisProxy* VisProxy, const FViewportClick& Click) override;
|
|
virtual void DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) override;
|
|
virtual void EndEditing() override;
|
|
virtual bool GetWidgetLocation(const FEditorViewportClient* ViewportClient, FVector& OutLocation) const override;
|
|
virtual bool GetCustomInputCoordinateSystem(const FEditorViewportClient* ViewportClient, FMatrix& OutMatrix) const override;
|
|
virtual bool HandleInputDelta(FEditorViewportClient* ViewportClient, FViewport* Viewport, FVector& DeltaTranslate, FRotator& DeltaRotate, FVector& DeltaScale) override;
|
|
virtual bool HandleInputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) override;
|
|
/** Handle box select input */
|
|
virtual bool HandleBoxSelect(const FBox& InBox, FEditorViewportClient* InViewportClient, FViewport* InViewport) override;
|
|
/** Handle frustum select input */
|
|
virtual bool HandleFrustumSelect(const FConvexVolume& InFrustum, FEditorViewportClient* InViewportClient, FViewport* InViewport) override;
|
|
/** Return whether focus on selection should focus on bounding box defined by active visualizer */
|
|
virtual bool HasFocusOnSelectionBoundingBox(FBox& OutBoundingBox) override;
|
|
/** Pass snap input to active visualizer */
|
|
virtual bool HandleSnapTo(const bool bInAlign, const bool bInUseLineTrace, const bool bInUseBounds, const bool bInUsePivot, AActor* InDestination) override;
|
|
/** Get currently edited component, this is needed to reset the active visualizer after undo/redo */
|
|
virtual UActorComponent* GetEditedComponent() const override;
|
|
virtual TSharedPtr<SWidget> GenerateContextMenu() const override;
|
|
virtual bool IsVisualizingArchetype() const override;
|
|
//~ End FComponentVisualizer Interface
|
|
|
|
/** Get the shape component we are currently editing */
|
|
UZoneShapeComponent* GetEditedShapeComponent() const;
|
|
|
|
const TSet<int32>& GetSelectedPoints() const
|
|
{
|
|
check(SelectionState);
|
|
return SelectionState->GetSelectedPoints();
|
|
}
|
|
|
|
protected:
|
|
|
|
struct ZoneShapeConnectorRenderInfo
|
|
{
|
|
ZoneShapeConnectorRenderInfo(FVector InPosition, FVector InFoward, FVector InUp)
|
|
: Position(InPosition)
|
|
, Foward(InFoward)
|
|
, Up(InUp)
|
|
{
|
|
|
|
}
|
|
|
|
// Position of the connector.
|
|
FVector Position = FVector::ZeroVector;
|
|
|
|
// Foward direction of the connector.
|
|
FVector Foward = FVector::ForwardVector;
|
|
|
|
// Up direction of the connector.
|
|
FVector Up = FVector::UpVector;
|
|
};
|
|
|
|
/** Determine if any selected key index is out of range (perhaps because something external has modified the shape) */
|
|
bool IsAnySelectedPointIndexOutOfRange(const UZoneShapeComponent& Comp) const;
|
|
|
|
/** Whether a single point is currently selected */
|
|
bool IsSinglePointSelected() const;
|
|
|
|
/** Transforms selected control point by given translation */
|
|
bool TransformSelectedControlPoint(const FVector& DeltaTranslate);
|
|
|
|
/** Transforms selected points by given delta translation, rotation and scale. */
|
|
bool TransformSelectedPoints(const FEditorViewportClient* ViewportClient, const FVector& DeltaTranslate, const FRotator& DeltaRotate, const FVector& DeltaScale) const;
|
|
|
|
/** Update the key selection state of the visualizer */
|
|
void ChangeSelectionState(int32 Index, bool bIsCtrlHeld) const;
|
|
|
|
/** Alt-drag: duplicates the selected point */
|
|
bool DuplicatePointForAltDrag(const FVector& InDrag) const;
|
|
|
|
/** Split segment using given interpolation value, selects the new point */
|
|
void SplitSegment(const int32 InSegmentIndex, const float SegmentSplitT, UZoneShapeComponent* ShapeComp = nullptr) const;
|
|
|
|
/** Add segment using given position and index, selects the new point */
|
|
void AddSegment(const FVector& InWorldPos, const int32 InSelectedIndex, UZoneShapeComponent* InShapeComp = nullptr) const;
|
|
|
|
/** Duplicates selected points and selects them. */
|
|
void DuplicateSelectedPoints(const FVector& WorldOffset = FVector::ZeroVector, bool bInsertAfter = true) const;
|
|
|
|
/** Updates the component and selected properties if the component has changed */
|
|
const UZoneShapeComponent* UpdateSelectedShapeComponent(const HComponentVisProxy* VisProxy);
|
|
|
|
/** Returns the rotation of last selected point, or false if last selection is not valid. */
|
|
bool GetLastSelectedPointRotation(FQuat& OutRotation) const;
|
|
|
|
void OnDeletePoint() const;
|
|
bool CanDeletePoint() const;
|
|
|
|
/** Duplicates selected points in place */
|
|
void OnDuplicatePoint() const;
|
|
bool IsPointSelectionValid() const;
|
|
|
|
void OnAddPointToSegment() const;
|
|
bool CanAddPointToSegment() const;
|
|
|
|
void OnSetPointType(FZoneShapePointType Type) const;
|
|
bool IsPointTypeSet(FZoneShapePointType Type) const;
|
|
|
|
void OnSelectAllPoints() const;
|
|
bool CanSelectAllPoints() const;
|
|
|
|
void OnBreakAtPointNewActors() const;
|
|
void OnBreakAtPointNewComponents() const;
|
|
bool CanBreakAtPoint() const;
|
|
|
|
void OnBreakAtSegmentNewActors() const;
|
|
void OnBreakAtSegmentNewComponents() const;
|
|
bool CanBreakAtSegment() const;
|
|
|
|
void GenerateShapePointTypeSubMenu(FMenuBuilder& MenuBuilder) const;
|
|
|
|
void GenerateSnapAlignSubMenu(FMenuBuilder& MenuBuilder) const;
|
|
void GenerateBreakAtPointSubMenu(FMenuBuilder& MenuBuilder) const;
|
|
void GenerateBreakAtSegmentSubMenu(FMenuBuilder& MenuBuilder) const;
|
|
|
|
// FGCObject interface
|
|
virtual void AddReferencedObjects(FReferenceCollector& Collector) override;
|
|
virtual FString GetReferencerName() const override
|
|
{
|
|
return TEXT("FZoneShapeComponentVisualizer");
|
|
}
|
|
// End of FGCObject interface
|
|
|
|
/** Output log commands */
|
|
TSharedPtr<FUICommandList> ShapeComponentVisualizerActions;
|
|
|
|
/** Property to be notified when points change */
|
|
FProperty* ShapePointsProperty;
|
|
|
|
/** Current selection state */
|
|
TObjectPtr<UZoneShapeComponentVisualizerSelectionState> SelectionState;
|
|
|
|
/** Whether we currently allow duplication when dragging */
|
|
bool bAllowDuplication;
|
|
|
|
/** Alt-drag: Accumulates delayed drag offset. */
|
|
FVector DuplicateAccumulatedDrag;
|
|
|
|
/** True if the ControlPointPosition has been initialized */
|
|
bool bControlPointPositionCaptured;
|
|
|
|
/** Selected control pints adjusted position, allows the gizmo to move freely, while we constrain the control point. */
|
|
FVector ControlPointPosition;
|
|
|
|
/** True if cached rotation is set. */
|
|
bool bHasCachedRotation = false;
|
|
|
|
/** Rotation cached when mouse button is pressed. */
|
|
FQuat CachedRotation = FQuat::Identity;
|
|
|
|
bool bIsSelectingComponent = false;
|
|
|
|
/** The point used to calculate the auto connect and intersection states. */
|
|
int32 SelectedPointForConnecting = -1;
|
|
|
|
bool bIsAutoConnecting = false;
|
|
struct FAutoConnectState
|
|
{
|
|
TArray<ZoneShapeConnectorRenderInfo> DestShapeConnectorInfos;
|
|
int32 ClosestShapeConnectorInfoIndex = INDEX_NONE;
|
|
FVector NearestPointWorldPosition = FVector::ZeroVector;
|
|
FVector NearestPointWorldNormal = FVector::ZeroVector;
|
|
};
|
|
FAutoConnectState AutoConnectState;
|
|
|
|
bool bIsCreatingIntersection = false;
|
|
struct FCreateIntersectionState
|
|
{
|
|
TWeakObjectPtr<UZoneShapeComponent> WeakTargetShapeComponent;
|
|
int32 OverlappingSegmentIndex = INDEX_NONE;
|
|
float OverlappingSegmentT = -1.0f;
|
|
int32 ClosePointIndex = INDEX_NONE;
|
|
FVector PreviewLocation = FVector::ZeroVector;
|
|
};
|
|
FCreateIntersectionState CreateIntersectionState;
|
|
|
|
private:
|
|
|
|
TArray<UZoneShapeComponent*> BreakAtPoint(bool bCreateNewActor, UZoneShapeComponent* ShapeComp = nullptr) const;
|
|
void BreakAtSegment(bool bCreateNewActor) const;
|
|
|
|
void DetectCloseByShapeForAutoConnection(const UZoneShapeComponent* ShapeComp, const FZoneShapePoint& DraggedPoint);
|
|
void DetectCloseByShapeForAutoIntersectionCreation(const UZoneShapeComponent* ShapeComp, const FZoneShapePoint& DraggedPoint);
|
|
|
|
void CreateIntersection(UZoneShapeComponent* ShapeComp);
|
|
void CreateIntersectionForSplineShape(UZoneShapeComponent* ShapeComp, FZoneShapePoint& DraggedPoint, bool DestroyCoveredShape = true);
|
|
void CreateIntersectionForPolygonShape(UZoneShapeComponent* ShapeComp, FZoneShapePoint& DraggedPoint);
|
|
|
|
void ClearAutoConnectingStatus();
|
|
void ClearAutoIntersectionStatus();
|
|
|
|
bool CanAutoConnect(const UZoneShapeComponent* ShapeComp) const;
|
|
bool CanAutoCreateIntersection(const UZoneShapeComponent* ShapeComp) const;
|
|
};
|