From a40af98394c543d2ad736fd76f5325028351f9c2 Mon Sep 17 00:00:00 2001 From: hultonha <82228511+hultonha@users.noreply.github.com> Date: Wed, 6 Oct 2021 14:52:59 +0100 Subject: [PATCH] Add focus camera and custom camera input (#4493) * add focus camera and custom camera input Signed-off-by: hultonha * very minor tidy before publishing PR Signed-off-by: hultonha * small updates after PR feedback Signed-off-by: hultonha --- .../EditorModularViewportCameraComposer.cpp | 43 +++++++-- .../EditorModularViewportCameraComposer.h | 2 + .../EditorPreferencesPageViewportCamera.cpp | 12 ++- .../EditorPreferencesPageViewportCamera.h | 1 + Code/Editor/EditorViewportSettings.cpp | 11 +++ Code/Editor/EditorViewportSettings.h | 3 + .../AzFramework/Viewport/CameraInput.cpp | 92 +++++++++++++++++-- .../AzFramework/Viewport/CameraInput.h | 54 +++++++++++ 8 files changed, 198 insertions(+), 20 deletions(-) diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 29cf348ab5..c492010336 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -95,6 +95,7 @@ namespace SandboxEditor cameras.AddCamera(m_firstPersonPanCamera); cameras.AddCamera(m_firstPersonTranslateCamera); cameras.AddCamera(m_firstPersonScrollCamera); + cameras.AddCamera(m_firstPersonFocusCamera); cameras.AddCamera(m_pivotCamera); }); @@ -111,6 +112,7 @@ namespace SandboxEditor viewportId, &AzToolsFramework::ViewportInteraction::ViewportMouseCursorRequestBus::Events::BeginCursorCapture); } }; + const auto showCursor = [viewportId = m_viewportId] { if (SandboxEditor::CameraCaptureCursorForLook()) @@ -172,19 +174,34 @@ namespace SandboxEditor return SandboxEditor::CameraScrollSpeed(); }; + const auto pivotFn = [] + { + // use the manipulator transform as the pivot point + AZStd::optional entityPivot; + AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( + entityPivot, AzToolsFramework::GetEntityContextId(), + &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); + + if (entityPivot.has_value()) + { + return entityPivot->GetTranslation(); + } + + // otherwise just use the identity + return AZ::Vector3::CreateZero(); + }; + + m_firstPersonFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusLook); + + m_firstPersonFocusCamera->SetPivotFn(pivotFn); + m_pivotCamera = AZStd::make_shared(SandboxEditor::CameraPivotChannelId()); m_pivotCamera->SetPivotFn( - []([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) + [pivotFn]([[maybe_unused]] const AZ::Vector3& position, [[maybe_unused]] const AZ::Vector3& direction) { - // use the manipulator transform as the pivot point - AZStd::optional entityPivot; - AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( - entityPivot, AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); - - // otherwise just use the identity - return entityPivot.value_or(AZ::Transform::CreateIdentity()).GetTranslation(); + return pivotFn(); }); m_pivotRotateCamera = AZStd::make_shared(SandboxEditor::CameraPivotLookChannelId()); @@ -244,11 +261,17 @@ namespace SandboxEditor return SandboxEditor::CameraPanInvertedY(); }; + m_pivotFocusCamera = + AZStd::make_shared(SandboxEditor::CameraFocusChannelId(), AzFramework::FocusPivot); + + m_pivotFocusCamera->SetPivotFn(pivotFn); + m_pivotCamera->m_pivotCameras.AddCamera(m_pivotRotateCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotTranslateCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyScrollCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotDollyMoveCamera); m_pivotCamera->m_pivotCameras.AddCamera(m_pivotPanCamera); + m_pivotCamera->m_pivotCameras.AddCamera(m_pivotFocusCamera); } void EditorModularViewportCameraComposer::OnEditorModularViewportCameraComposerSettingsChanged() @@ -257,12 +280,14 @@ namespace SandboxEditor m_firstPersonTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); m_firstPersonPanCamera->SetPanInputChannelId(SandboxEditor::CameraFreePanChannelId()); m_firstPersonRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraFreeLookChannelId()); + m_firstPersonFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); m_pivotCamera->SetPivotInputChannelId(SandboxEditor::CameraPivotChannelId()); m_pivotTranslateCamera->SetTranslateCameraInputChannelIds(translateCameraInputChannelIds); m_pivotPanCamera->SetPanInputChannelId(SandboxEditor::CameraPivotPanChannelId()); m_pivotRotateCamera->SetRotateInputChannelId(SandboxEditor::CameraPivotLookChannelId()); m_pivotDollyMoveCamera->SetDollyInputChannelId(SandboxEditor::CameraPivotDollyChannelId()); + m_pivotFocusCamera->SetFocusInputChannelId(SandboxEditor::CameraFocusChannelId()); } void EditorModularViewportCameraComposer::OnViewportViewEntityChanged(const AZ::EntityId& viewEntityId) diff --git a/Code/Editor/EditorModularViewportCameraComposer.h b/Code/Editor/EditorModularViewportCameraComposer.h index e691ca1c89..90103dba43 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.h +++ b/Code/Editor/EditorModularViewportCameraComposer.h @@ -42,12 +42,14 @@ namespace SandboxEditor AZStd::shared_ptr m_firstPersonPanCamera; AZStd::shared_ptr m_firstPersonTranslateCamera; AZStd::shared_ptr m_firstPersonScrollCamera; + AZStd::shared_ptr m_firstPersonFocusCamera; AZStd::shared_ptr m_pivotCamera; AZStd::shared_ptr m_pivotRotateCamera; AZStd::shared_ptr m_pivotTranslateCamera; AZStd::shared_ptr m_pivotDollyScrollCamera; AZStd::shared_ptr m_pivotDollyMoveCamera; AZStd::shared_ptr m_pivotPanCamera; + AZStd::shared_ptr m_pivotFocusCamera; AzFramework::ViewportId m_viewportId; }; diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.cpp b/Code/Editor/EditorPreferencesPageViewportCamera.cpp index 55b631e1f6..b632163186 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.cpp +++ b/Code/Editor/EditorPreferencesPageViewportCamera.cpp @@ -91,7 +91,8 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->Field("FreePan", &CameraInputSettings::m_freePanChannelId) ->Field("PivotLook", &CameraInputSettings::m_pivotLookChannelId) ->Field("PivotDolly", &CameraInputSettings::m_pivotDollyChannelId) - ->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId); + ->Field("PivotPan", &CameraInputSettings::m_pivotPanChannelId) + ->Field("Focus", &CameraInputSettings::m_focusChannelId); serialize.Class() ->Version(1) @@ -206,14 +207,17 @@ void CEditorPreferencesPage_ViewportCamera::Reflect(AZ::SerializeContext& serial ->DataElement( AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_pivotPanChannelId, "Pivot Pan", "Key/button to begin camera pivot pan") + ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames) + ->DataElement( + AZ::Edit::UIHandlers::ComboBox, &CameraInputSettings::m_focusChannelId, "Focus", "Key/button to focus camera pivot") ->Attribute(AZ::Edit::Attributes::StringList, &GetEditorInputNames); editContext->Class("Viewport Preferences", "Viewport Preferences") ->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->Attribute(AZ::Edit::Attributes::Visibility, AZ_CRC("PropertyVisibility_ShowChildrenOnly", 0xef428f20)) ->DataElement( - AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, - "Camera Movement Settings", "Camera Movement Settings") + AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraMovementSettings, "Camera Movement Settings", + "Camera Movement Settings") ->DataElement( AZ::Edit::UIHandlers::Default, &CEditorPreferencesPage_ViewportCamera::m_cameraInputSettings, "Camera Input Settings", "Camera Input Settings"); @@ -281,6 +285,7 @@ void CEditorPreferencesPage_ViewportCamera::OnApply() SandboxEditor::SetCameraPivotLookChannelId(m_cameraInputSettings.m_pivotLookChannelId); SandboxEditor::SetCameraPivotDollyChannelId(m_cameraInputSettings.m_pivotDollyChannelId); SandboxEditor::SetCameraPivotPanChannelId(m_cameraInputSettings.m_pivotPanChannelId); + SandboxEditor::SetCameraFocusChannelId(m_cameraInputSettings.m_focusChannelId); SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Broadcast( &SandboxEditor::EditorModularViewportCameraComposerNotificationBus::Events::OnEditorModularViewportCameraComposerSettingsChanged); @@ -316,4 +321,5 @@ void CEditorPreferencesPage_ViewportCamera::InitializeSettings() m_cameraInputSettings.m_pivotLookChannelId = SandboxEditor::CameraPivotLookChannelId().GetName(); m_cameraInputSettings.m_pivotDollyChannelId = SandboxEditor::CameraPivotDollyChannelId().GetName(); m_cameraInputSettings.m_pivotPanChannelId = SandboxEditor::CameraPivotPanChannelId().GetName(); + m_cameraInputSettings.m_focusChannelId = SandboxEditor::CameraFocusChannelId().GetName(); } diff --git a/Code/Editor/EditorPreferencesPageViewportCamera.h b/Code/Editor/EditorPreferencesPageViewportCamera.h index 01dc4664f6..cd31141c4a 100644 --- a/Code/Editor/EditorPreferencesPageViewportCamera.h +++ b/Code/Editor/EditorPreferencesPageViewportCamera.h @@ -86,6 +86,7 @@ private: AZStd::string m_pivotLookChannelId; AZStd::string m_pivotDollyChannelId; AZStd::string m_pivotPanChannelId; + AZStd::string m_focusChannelId; }; CameraMovementSettings m_cameraMovementSettings; diff --git a/Code/Editor/EditorViewportSettings.cpp b/Code/Editor/EditorViewportSettings.cpp index fe8efecd17..c2539b008c 100644 --- a/Code/Editor/EditorViewportSettings.cpp +++ b/Code/Editor/EditorViewportSettings.cpp @@ -50,6 +50,7 @@ namespace SandboxEditor constexpr AZStd::string_view CameraPivotLookIdSetting = "/Amazon/Preferences/Editor/Camera/PivotLookId"; constexpr AZStd::string_view CameraPivotDollyIdSetting = "/Amazon/Preferences/Editor/Camera/PivotDollyId"; constexpr AZStd::string_view CameraPivotPanIdSetting = "/Amazon/Preferences/Editor/Camera/PivotPanId"; + constexpr AZStd::string_view CameraFocusIdSetting = "/Amazon/Preferences/Editor/Camera/FocusId"; template void SetRegistry(const AZStd::string_view setting, T&& value) @@ -462,4 +463,14 @@ namespace SandboxEditor { SetRegistry(CameraPivotPanIdSetting, cameraPivotPanId); } + + AzFramework::InputChannelId CameraFocusChannelId() + { + return AzFramework::InputChannelId(GetRegistry(CameraFocusIdSetting, AZStd::string("keyboard_key_alphanumeric_X")).c_str()); + } + + void SetCameraFocusChannelId(AZStd::string_view cameraFocusId) + { + SetRegistry(CameraFocusIdSetting, cameraFocusId); + } } // namespace SandboxEditor diff --git a/Code/Editor/EditorViewportSettings.h b/Code/Editor/EditorViewportSettings.h index d1271017c6..0253b01621 100644 --- a/Code/Editor/EditorViewportSettings.h +++ b/Code/Editor/EditorViewportSettings.h @@ -136,4 +136,7 @@ namespace SandboxEditor SANDBOX_API AzFramework::InputChannelId CameraPivotPanChannelId(); SANDBOX_API void SetCameraPivotPanChannelId(AZStd::string_view cameraPivotPanId); + + SANDBOX_API AzFramework::InputChannelId CameraFocusChannelId(); + SANDBOX_API void SetCameraFocusChannelId(AZStd::string_view cameraFocusId); } // namespace SandboxEditor diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index a3eb4d8329..13f5303eaf 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -623,15 +623,24 @@ namespace AzFramework { Camera nextCamera = targetCamera; - const auto pivotDirection = targetCamera.m_offset.GetNormalized(); - nextCamera.m_offset -= pivotDirection * delta; - const auto pivotDot = targetCamera.m_offset.Dot(nextCamera.m_offset); - const auto distance = nextCamera.m_offset.GetLength() * AZ::GetSign(pivotDot); - - const auto minDistance = 0.01f; - if (distance < minDistance || pivotDot < 0.0f) + // handle case where pivot and offset may be the same to begin with + // choose negative y-axis for offset to default to moving the camera backwards from the pivot (standard centered pivot behavior) + const auto pivotDirection = [&targetCamera] { - nextCamera.m_offset = pivotDirection * minDistance; + if (const auto offsetLength = targetCamera.m_offset.GetLength(); AZ::IsCloseMag(offsetLength, 0.0f)) + { + return -AZ::Vector3::CreateAxisY(); + } + else + { + return targetCamera.m_offset / offsetLength; + } + }(); + + nextCamera.m_offset -= pivotDirection * delta; + if (pivotDirection.Dot(nextCamera.m_offset) < 0.0f) + { + nextCamera.m_offset = pivotDirection * 0.001f; } return nextCamera; @@ -771,6 +780,73 @@ namespace AzFramework return camera; } + FocusCameraInput::FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn) + : m_focusChannelId(focusChannelId) + , m_offsetFn(offsetFn) + { + } + + bool FocusCameraInput::HandleEvents( + const InputEvent& event, [[maybe_unused]] const ScreenVector& cursorDelta, [[maybe_unused]] float scrollDelta) + { + if (const auto* input = AZStd::get_if(&event)) + { + if (input->m_channelId == m_focusChannelId && input->m_state == InputChannel::State::Began) + { + BeginActivation(); + } + } + + return !Idle(); + } + + Camera FocusCameraInput::StepCamera( + const Camera& targetCamera, + [[maybe_unused]] const ScreenVector& cursorDelta, + [[maybe_unused]] float scrollDelta, + [[maybe_unused]] float deltaTime) + { + if (Beginning()) + { + // as the camera starts, record the camera we would like to end up as + m_nextCamera.m_offset = m_offsetFn(m_pivotFn().GetDistance(targetCamera.Translation())); + const auto angles = + EulerAngles(AZ::Matrix3x3::CreateFromMatrix3x4(AZ::Matrix3x4::CreateLookAt(targetCamera.Translation(), m_pivotFn()))); + m_nextCamera.m_pitch = angles.GetX(); + m_nextCamera.m_yaw = angles.GetZ(); + m_nextCamera.m_pivot = targetCamera.m_pivot; + } + + // end the behavior when the camera is in alignment + if (AZ::IsCloseMag(targetCamera.m_pitch, m_nextCamera.m_pitch) && AZ::IsCloseMag(targetCamera.m_yaw, m_nextCamera.m_yaw)) + { + EndActivation(); + } + + return m_nextCamera; + } + + void FocusCameraInput::SetPivotFn(PivotFn pivotFn) + { + m_pivotFn = AZStd::move(pivotFn); + } + + void FocusCameraInput::SetFocusInputChannelId(const InputChannelId& focusChannelId) + { + m_focusChannelId = focusChannelId; + } + + bool CustomCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, const float scrollDelta) + { + return m_handleEventsFn(*this, event, cursorDelta, scrollDelta); + } + + Camera CustomCameraInput::StepCamera( + const Camera& targetCamera, const ScreenVector& cursorDelta, const float scrollDelta, const float deltaTime) + { + return m_stepCameraFn(*this, targetCamera, cursorDelta, scrollDelta, deltaTime); + } + InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize) { const auto& inputChannelId = inputChannel.GetInputChannelId(); diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index c44d951292..9b1988f5ec 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -617,6 +617,60 @@ namespace AzFramework return true; } + //! Callback to use for FocusCameraInput when a free look camera is being used. + //! @note This is when offset is zero. + inline AZ::Vector3 FocusLook(float) + { + return AZ::Vector3::CreateZero(); + } + + //! Callback to use for FocusCameraInput when a pivot camera is being used. + //! @note This is when offset is non zero. + inline AZ::Vector3 FocusPivot(const float length) + { + return AZ::Vector3::CreateAxisY(-length); + } + + using FocusOffsetFn = AZStd::function; + + //! A focus behavior to align the camera view to the position returned by the pivot function. + //! @note This only alters the camera orientation, the translation is unaffected. + class FocusCameraInput : public CameraInput + { + public: + using PivotFn = AZStd::function; + + FocusCameraInput(const InputChannelId& focusChannelId, FocusOffsetFn offsetFn); + + // CameraInput overrides ... + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; + + //! Override the default behavior for how a pivot point is calculated. + void SetPivotFn(PivotFn pivotFn); + + void SetFocusInputChannelId(const InputChannelId& focusChannelId); + + private: + InputChannelId m_focusChannelId; //!< Input channel to begin the focus camera input. + Camera m_nextCamera; + PivotFn m_pivotFn; + FocusOffsetFn m_offsetFn; + }; + + //! Provides a CameraInput type that can be implemented without needing to create a new type deriving from CameraInput. + //! This can be very useful for specific use cases that are less generally applicable. + class CustomCameraInput : public CameraInput + { + public: + // CameraInput overrides ... + bool HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, float scrollDelta) override; + Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override; + + AZStd::function m_handleEventsFn; + AZStd::function m_stepCameraFn; + }; + //! Map from a generic InputChannel event to a camera specific InputEvent. InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize); } // namespace AzFramework