442 lines
17 KiB
C++
442 lines
17 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Containers/Array.h"
|
|
#include "Containers/Map.h"
|
|
#include "Containers/UnrealString.h"
|
|
#include "Delegates/Delegate.h"
|
|
#include "Editor.h"
|
|
#include "Engine/EngineBaseTypes.h"
|
|
#include "Engine/World.h"
|
|
#include "HAL/Platform.h"
|
|
#include "InputCoreTypes.h"
|
|
#include "InputState.h"
|
|
#include "InteractiveTool.h"
|
|
#include "InteractiveToolManager.h"
|
|
#include "InteractiveToolsContext.h"
|
|
#include "Math/Ray.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Misc/Optional.h"
|
|
#include "Templates/SharedPointer.h"
|
|
#include "UObject/ObjectMacros.h"
|
|
#include "UObject/ObjectPtr.h"
|
|
#include "UObject/UObjectGlobals.h"
|
|
|
|
#include "EdModeInteractiveToolsContext.generated.h"
|
|
|
|
class FCanvas;
|
|
class FEdMode;
|
|
class FEditorModeTools;
|
|
class FEditorViewportClient;
|
|
class FPrimitiveDrawInterface;
|
|
class FSceneView;
|
|
class FViewport;
|
|
class FViewportClient;
|
|
class ILevelEditor;
|
|
class IToolsContextQueriesAPI;
|
|
class IToolsContextRenderAPI;
|
|
class IToolsContextTransactionsAPI;
|
|
class UDragToolsBehaviorSource;
|
|
class UEdModeInteractiveToolsContext;
|
|
class UGizmoViewContext;
|
|
class UInputRouter;
|
|
class UInteractiveToolBuilder;
|
|
class UMaterialInterface;
|
|
class UObject;
|
|
class USelection;
|
|
class UTypedElementSelectionSet;
|
|
struct FToolBuilderState;
|
|
|
|
/**
|
|
* UEditorInteractiveToolsContext is an extension/adapter of an InteractiveToolsContext designed
|
|
* for use in the UE Editor. Currently this implementation assumes that it is created by a
|
|
* Mode Manager (FEditorModeTools), and that the Mode Manager will call various API functions
|
|
* like Render() and Tick() when necessary.
|
|
*
|
|
*
|
|
* allows it to be easily embedded inside an FEdMode. A set of functions are provided which can be
|
|
* called from the FEdMode functions of the same name. These will handle the data type
|
|
* conversions and forwarding calls necessary to operate the ToolsContext
|
|
*/
|
|
UCLASS(Transient)
|
|
class UNREALED_API UEditorInteractiveToolsContext : public UInteractiveToolsContext
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UEditorInteractiveToolsContext();
|
|
|
|
/**
|
|
* Initialize a new ToolsContext for an EdMode owned by the given InEditorModeManager
|
|
* @param
|
|
*/
|
|
void InitializeContextWithEditorModeManager(FEditorModeTools* InEditorModeManager, UInputRouter* UseInputRouter = nullptr);
|
|
|
|
/** Shutdown ToolsContext and clean up any connections/etc */
|
|
virtual void ShutdownContext();
|
|
|
|
// default behavior is to accept active tool
|
|
virtual void TerminateActiveToolsOnPIEStart();
|
|
|
|
// default behavior is to accept active tool
|
|
virtual void TerminateActiveToolsOnSaveWorld();
|
|
|
|
// default behavior is to cancel active tool
|
|
virtual void TerminateActiveToolsOnWorldTearDown();
|
|
|
|
// default behavior is to cancel active tool
|
|
virtual void TerminateActiveToolsOnLevelChange();
|
|
|
|
FEditorModeTools* GetParentEditorModeManager() const { return EditorModeManager; }
|
|
|
|
IToolsContextQueriesAPI* GetQueriesAPI() const { return QueriesAPI; }
|
|
IToolsContextTransactionsAPI* GetTransactionAPI() const { return TransactionAPI; }
|
|
|
|
/** Call this to notify the Editor that the viewports this ToolsContext is related to may need a repaint, ie during interactive tool usage */
|
|
virtual void PostInvalidation();
|
|
|
|
// UObject Interface
|
|
virtual UWorld* GetWorld() const override;
|
|
|
|
// call functions of the same name on the ToolManager and GizmoManager
|
|
virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime);
|
|
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI);
|
|
virtual void DrawHUD(FViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View, FCanvas* Canvas);
|
|
|
|
// These delegates can be used to hook into the Render() / DrawHUD() / Tick() calls above. In particular, non-legacy UEdMode's
|
|
// don't normally receive Render() and DrawHUD() calls from the mode manager, but can attach to these.
|
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnRender, IToolsContextRenderAPI* RenderAPI);
|
|
FOnRender OnRender;
|
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnDrawHUD, FCanvas* Canvas, IToolsContextRenderAPI* RenderAPI);
|
|
FOnDrawHUD OnDrawHUD;
|
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnTick, float DeltaTime);
|
|
FOnTick OnTick;
|
|
|
|
/** @return true if selected actors/components can be deleted */
|
|
virtual bool ProcessEditDelete();
|
|
|
|
//
|
|
// Utility functions useful for hooking up to UICommand/etc
|
|
//
|
|
|
|
virtual bool CanStartTool(const FString ToolTypeIdentifier) const;
|
|
virtual bool HasActiveTool() const;
|
|
virtual FString GetActiveToolName() const;
|
|
virtual bool ActiveToolHasAccept() const;
|
|
virtual bool CanAcceptActiveTool() const;
|
|
virtual bool CanCancelActiveTool() const;
|
|
virtual bool CanCompleteActiveTool() const;
|
|
virtual void StartTool(const FString ToolTypeIdentifier);
|
|
virtual void EndTool(EToolShutdownType ShutdownType);
|
|
void Activate();
|
|
void Deactivate();
|
|
|
|
|
|
/** @return Ray into 3D scene at last mouse event */
|
|
virtual FRay GetLastWorldRay() const
|
|
{
|
|
check(false);
|
|
return FRay();
|
|
}
|
|
|
|
//
|
|
// Configuration functions
|
|
//
|
|
|
|
/*
|
|
* Configure whether ::Render() should early-out for HitProxy rendering passes.
|
|
* If the Mode does not use HitProxy, and the Tools/Gizmos have expensive Render() calls, this can help with interactive performance.
|
|
*/
|
|
void SetEnableRenderingDuringHitProxyPass(bool bEnabled);
|
|
|
|
/** @return true if HitProxy rendering will be allowed in ::Render() */
|
|
bool GetEnableRenderingDuringHitProxyPass() const { return bEnableRenderingDuringHitProxyPass; }
|
|
|
|
|
|
/**
|
|
* Configure whether Transform Gizmos created by the ITF (eg CombinedTransformGizmo) should prefer to show in 'Combined' mode.
|
|
* If this is disabled, the Gizmo should respect the active Editor Gizmo setting (eg in the Level Viewport)
|
|
*/
|
|
void SetForceCombinedGizmoMode(bool bEnabled);
|
|
|
|
/** @return true if Force Combined Gizmo mode is Enabled */
|
|
bool GetForceCombinedGizmoModeEnabled() const { return bForceCombinedGizmoMode; }
|
|
|
|
|
|
/**
|
|
* Configure whether Transform Gizmos created by the ITF (eg CombinedTransformGizmo) should, when in World coordinate system,
|
|
* snap to an Absolute world-aligned grid, or snap Relative to the initial position of any particular gizmo transform.
|
|
* Relative is the default and is also the behavior of the standard UE Gizmo.
|
|
*/
|
|
void SetAbsoluteWorldSnappingEnabled(bool bEnabled);
|
|
|
|
/** @return true if Absolute World Snapping mode is Enabled */
|
|
bool GetAbsoluteWorldSnappingEnabled() const { return bEnableAbsoluteWorldSnapping; }
|
|
|
|
/*
|
|
* Configure whether tools should shutdown when entering PIE.
|
|
* By default, the context will shut down any active tools on PIE start.
|
|
* Setting this to true will prevent this, but the system makes no promises about whether tools work properly across PIE start/run/shutdown.
|
|
* Therefore, do not use this if you do not know for certain that it is safe to persist tools across PIE start in your situation.
|
|
*/
|
|
void SetDeactivateToolsOnPIEStart(bool bDeactivateTools);
|
|
|
|
/** @return true if tools should be deactivated when PIE is started */
|
|
bool GetDeactivateToolsOnPIEStart() const { return bDeactivateOnPIEStart; }
|
|
|
|
/*
|
|
* Configure whether tools should shutdown when the world is saving.
|
|
* By default, the context will shut down any active tools on save.
|
|
* Setting this to true will prevent this, but the system makes no promises about whether tools work properly across a save.
|
|
* Therefore, do not use this if you do not know for certain that it is safe to persist tools across a save in your situation.
|
|
*/
|
|
void SetDeactivateToolsOnSaveWorld(bool bDeactivateTools);
|
|
|
|
/** @return true if tools should be deactivated when save is started */
|
|
bool GetDeactivateToolsOnSaveWorld() const { return bDeactivateOnSaveWorld; }
|
|
|
|
protected:
|
|
|
|
// we hide these
|
|
virtual void Initialize(IToolsContextQueriesAPI* QueriesAPI, IToolsContextTransactionsAPI* TransactionsAPI) override;
|
|
virtual void Shutdown() override;
|
|
|
|
virtual void DeactivateActiveTool(EToolSide WhichSide, EToolShutdownType ShutdownType);
|
|
virtual void DeactivateAllActiveTools(EToolShutdownType ShutdownType);
|
|
|
|
public:
|
|
UPROPERTY()
|
|
TObjectPtr<UMaterialInterface> StandardVertexColorMaterial;
|
|
|
|
protected:
|
|
// EdMode implementation of InteractiveToolFramework APIs - see ToolContextInterfaces.h
|
|
IToolsContextQueriesAPI* QueriesAPI;
|
|
IToolsContextTransactionsAPI* TransactionAPI;
|
|
|
|
// Tools need to be able to Invalidate the view, in case it is not Realtime.
|
|
// Currently we do this very aggressively, and also force Realtime to be on, but in general we should be able to rely on Invalidation.
|
|
// However there are multiple Views and we do not want to Invalidate immediately, so we store a timestamp for each
|
|
// ViewportClient, and invalidate it when we see it if it's timestamp is out-of-date.
|
|
// (In theory this map will continually grow as new Viewports are created...)
|
|
TMap<FViewportClient*, int32> InvalidationMap;
|
|
// current invalidation timestamp, incremented by invalidation calls
|
|
int32 InvalidationTimestamp = 0;
|
|
|
|
// An object in which we save the current scene view information that gizmos can use on the game thread
|
|
// to figure out how big the gizmo is for hit testing. Lives in the context store, but we keep a pointer here
|
|
// to avoid having to look for it.
|
|
UGizmoViewContext* GizmoViewContext = nullptr;
|
|
|
|
// Utility function to convert viewport x/y from mouse events (and others?) into scene ray.
|
|
// Copy-pasted from other Editor code, seems kind of expensive?
|
|
static FRay GetRayFromMousePos(FEditorViewportClient* ViewportClient, FViewport* Viewport, int MouseX, int MouseY);
|
|
|
|
// editor UI state that we set before starting tool and when exiting tool
|
|
// Currently disabling anti-aliasing during active Tools because it causes PDI flickering
|
|
void SetEditorStateForTool();
|
|
void RestoreEditorState();
|
|
|
|
void OnToolEnded(UInteractiveToolManager* InToolManager, UInteractiveTool* InEndedTool);
|
|
void OnToolPostBuild(UInteractiveToolManager* InToolManager, EToolSide InSide, UInteractiveTool* InBuiltTool, UInteractiveToolBuilder* InToolBuilder, const FToolBuilderState& ToolState);
|
|
|
|
TOptional<FString> PendingToolToStart = {};
|
|
TOptional<EToolShutdownType> PendingToolShutdownType = {};
|
|
|
|
private:
|
|
FEditorModeTools* EditorModeManager = nullptr;
|
|
|
|
// currently defaulting to enabled as FEdModes generally assume this, and in most cases hitproxy pass is not expensive.
|
|
bool bEnableRenderingDuringHitProxyPass = true;
|
|
|
|
bool bForceCombinedGizmoMode = false;
|
|
bool bEnableAbsoluteWorldSnapping = false;
|
|
|
|
bool bDeactivateOnPIEStart = true;
|
|
|
|
bool bDeactivateOnSaveWorld = true;
|
|
bool bIsActive = false;
|
|
};
|
|
|
|
|
|
/**
|
|
* UModeManagerInteractiveToolsContext extends UEditorInteractiveToolsContext with various functions for handling
|
|
* device (mouse) input. These functions are currently called by the EdMode Manager (FEditorModeTools).
|
|
*/
|
|
UCLASS(Transient)
|
|
class UNREALED_API UModeManagerInteractiveToolsContext : public UEditorInteractiveToolsContext
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
//
|
|
// UEditorInteractiveToolsContext API implementations that also forward calls to any child EdMode ToolsContexts
|
|
//
|
|
public:
|
|
virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override;
|
|
virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override;
|
|
virtual void DrawHUD(FViewportClient* ViewportClient,FViewport* Viewport,const FSceneView* View, FCanvas* Canvas) override;
|
|
|
|
virtual bool ProcessEditDelete() override;
|
|
|
|
//
|
|
// Input handling, these functions forward ViewportClient events to the UInputRouter
|
|
//
|
|
public:
|
|
bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event);
|
|
/**
|
|
* This updates internal state like InputKey, but doesn't route the results to the input router.
|
|
* Use this if the input is captured by some higher system, to avoid this class from having an
|
|
* incorrect view of e.g. the mouse state because it did not receive a mouse release event.
|
|
*/
|
|
void UpdateStateWithoutRoutingInputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event);
|
|
|
|
bool MouseEnter(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y);
|
|
bool MouseLeave(FEditorViewportClient* ViewportClient, FViewport* Viewport);
|
|
bool MouseMove(FEditorViewportClient* ViewportClient, FViewport* Viewport, int32 x, int32 y);
|
|
bool LostFocus(FEditorViewportClient* InViewportClient, FViewport* Viewport) const;
|
|
|
|
bool StartTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport);
|
|
bool CapturedMouseMove(FEditorViewportClient* InViewportClient, FViewport* InViewport, int32 InMouseX, int32 InMouseY);
|
|
bool EndTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport);
|
|
|
|
/** @return True if the context has overriden the cursor style, false if not. */
|
|
bool GetCursor(EMouseCursor::Type& OutCursor) const;
|
|
|
|
/** @return Ray into 3D scene at last mouse event */
|
|
virtual FRay GetLastWorldRay() const override;
|
|
|
|
protected:
|
|
virtual void DeactivateAllActiveTools(EToolShutdownType ShutdownType) override;
|
|
virtual void Initialize(IToolsContextQueriesAPI* QueriesAPIIn, IToolsContextTransactionsAPI* TransactionsAPIIn) override;
|
|
virtual void Shutdown() override;
|
|
|
|
/** Input event instance used to keep track of various button states, etc, that we cannot directly query on-demand */
|
|
FInputDeviceState CurrentMouseState;
|
|
// called when PIE is about to start, shuts down active tools
|
|
FDelegateHandle BeginPIEDelegateHandle;
|
|
// called before a Save starts. This currently shuts down active tools.
|
|
FDelegateHandle PreSaveWorldDelegateHandle;
|
|
// called when a map is changed
|
|
FDelegateHandle WorldTearDownDelegateHandle;
|
|
// called when viewport clients change
|
|
FDelegateHandle ViewportClientListChangedHandle;
|
|
|
|
private:
|
|
bool bIsTrackingMouse;
|
|
|
|
|
|
|
|
|
|
protected:
|
|
UPROPERTY()
|
|
TArray<TObjectPtr<UEdModeInteractiveToolsContext>> EdModeToolsContexts;
|
|
|
|
public:
|
|
/**
|
|
* Create and initialize a new EdMode-level ToolsContext derived from the ModeManager ToolsContext.
|
|
* The EdMode ToolsContext does not have it's own InputRouter, it shares the InputRouter with the ModeManager ToolsContext.
|
|
* The ModeManager ToolsContext keeps track of these derived ToolsContext's and automatically Tick()'s them/etc.
|
|
* When the child ToolsContext is shut down, OnChildEdModeToolsContextShutdown() must be called to clean up
|
|
* @return new ToolsContext
|
|
*/
|
|
UEdModeInteractiveToolsContext* CreateNewChildEdModeToolsContext();
|
|
|
|
/**
|
|
* Call to add a child EdMode ToolsContext created using the above function
|
|
* @return true if child was added
|
|
*/
|
|
bool OnChildEdModeActivated(UEdModeInteractiveToolsContext* ChildToolsContext);
|
|
|
|
/**
|
|
* Call to release a child EdMode ToolsContext created using the above function
|
|
* @return true if child was found and removed
|
|
*/
|
|
bool OnChildEdModeDeactivated(UEdModeInteractiveToolsContext* ChildToolsContext);
|
|
|
|
|
|
/**
|
|
* Mark Editor Modes Tools for Drag Tools Support
|
|
*/
|
|
void SetDragToolsEnabled(bool bInEnabled)
|
|
{
|
|
bUsesDragTools = bInEnabled;
|
|
}
|
|
|
|
/**
|
|
* Are Drag Tools supported? e.g. Marquee Select
|
|
* Enable/Disable support by using SetDragToolsEnabled(...)
|
|
*/
|
|
bool UsesDragTools() const
|
|
{
|
|
return bUsesDragTools;
|
|
};
|
|
|
|
protected:
|
|
/**
|
|
* Activating Drag Tools if current Mode Manager requires them
|
|
*/
|
|
void RegisterDragTools();
|
|
|
|
/**
|
|
* Deactivating Drag Tools
|
|
*/
|
|
void UnregisterDragTools();
|
|
|
|
/**
|
|
* Responding to Mode Changes in order to deactivate/activate Drag Tools accordingly
|
|
*/
|
|
void OnEditorModeChanged(const FEditorModeID& InModeID, bool bInIsEnteringMode);
|
|
|
|
//~Begin ITF Alt processing Bypass
|
|
|
|
/**
|
|
* These functions are declared protected to avoid having to deal with deprecation later on.
|
|
* The Alt modifier bypass will be removed once a complete switch to ITF happens
|
|
*/
|
|
|
|
/** Enable/Disable Alt modifier bypass */
|
|
void SetBypassAltModifier(bool bInBypassAltModifier);
|
|
|
|
/** Is this Interactive Tools Context skipping Alt processing? */
|
|
bool BypassAltModifier() const { return bBypassAltModifier; }
|
|
|
|
/** Should we skip processing Alt modifier inputs? See UModeManagerInteractiveToolsContext::InputKey */
|
|
bool bBypassAltModifier = true;
|
|
|
|
//~End ITF Alt processing Bypass
|
|
|
|
/**
|
|
*
|
|
*/
|
|
UPROPERTY(Transient)
|
|
TObjectPtr<UDragToolsBehaviorSource> DragToolsBehaviorSource;
|
|
|
|
bool bUsesDragTools = false;
|
|
};
|
|
|
|
|
|
/**
|
|
* UEdModeInteractiveToolsContext is an UEditorInteractiveToolsContext intended for use/lifetime in the context of a UEdMode.
|
|
* This ITC subclass is dependent on a UModeManagerInteractiveToolsContext to provide an InputRouter.
|
|
*/
|
|
UCLASS(Transient)
|
|
class UNREALED_API UEdModeInteractiveToolsContext : public UEditorInteractiveToolsContext
|
|
{
|
|
friend class UModeManagerInteractiveToolsContext;
|
|
|
|
GENERATED_BODY()
|
|
public:
|
|
/**
|
|
* Initialize a new EdModeToolsContext that is derived from a ModeManagerToolsContext.
|
|
* This new ToolsContext will not have it's own InputRouter, it will share the InputRouter with the ModeManagerToolsContext
|
|
*/
|
|
void InitializeContextFromModeManagerContext(UModeManagerInteractiveToolsContext* ModeManagerToolsContext);
|
|
|
|
/** @return Ray into 3D scene at last mouse event */
|
|
virtual FRay GetLastWorldRay() const override;
|
|
|
|
protected:
|
|
UPROPERTY()
|
|
TObjectPtr<UModeManagerInteractiveToolsContext> ParentModeManagerToolsContext = nullptr;
|
|
};
|