diff --git a/Code/Editor/EditorModularViewportCameraComposer.cpp b/Code/Editor/EditorModularViewportCameraComposer.cpp index 72fd37605a..5cfb4d2665 100644 --- a/Code/Editor/EditorModularViewportCameraComposer.cpp +++ b/Code/Editor/EditorModularViewportCameraComposer.cpp @@ -337,12 +337,12 @@ namespace SandboxEditor AZ::TransformBus::EventResult(worldFromLocal, viewEntityId, &AZ::TransformBus::Events::GetWorldTM); AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( - m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, worldFromLocal); + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, worldFromLocal); } else { AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( - m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); + m_viewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform); } } diff --git a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp index 1a44d43370..7eb73b64bf 100644 --- a/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp +++ b/Code/Editor/Lib/Tests/Camera/test_EditorCamera.cpp @@ -92,8 +92,8 @@ namespace UnitTest &Camera::EditorCameraNotificationBus::Events::OnViewportViewEntityChanged, m_entity->GetId()); // ensure the viewport updates after the viewport view entity change - const float deltaTime = 1.0f / 60.0f; - m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(deltaTime), AZ::ScriptTimePoint() }); + // note: do a large step to ensure smoothing finishes (e.g. not 1.0f/60.0f) + m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(2.0f), AZ::ScriptTimePoint() }); // retrieve updated camera transform const AZ::Transform cameraTransform = m_cameraViewportContextView->GetCameraTransform(); @@ -103,61 +103,40 @@ namespace UnitTest EXPECT_THAT(cameraTransform, IsClose(entityTransform)); } - TEST_F(EditorCameraFixture, ReferenceFrameRemainsIdentityAfterExternalCameraTransformChangeWhenNotSet) + TEST_F(EditorCameraFixture, TrackingTransformIsTrueAfterTransformIsTracked) { - // Given - m_cameraViewportContextView->SetCameraTransform(AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f))); + // Given/When + const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( + AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); + AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame); - // When - AZ::Transform referenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); + bool trackingTransform = false; AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( - referenceFrame, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform); // Then - // reference frame is still the identity - EXPECT_THAT(referenceFrame, IsClose(AZ::Transform::CreateIdentity())); + EXPECT_THAT(trackingTransform, ::testing::IsTrue()); } - TEST_F(EditorCameraFixture, ExternalCameraTransformChangeWhenReferenceFrameIsSetUpdatesReferenceFrame) + TEST_F(EditorCameraFixture, TrackingTransformIsFalseAfterTransformIsStoppedBeingTracked) { // Given const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( - TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); - - const AZ::Transform nextTransform = AZ::Transform::CreateTranslation(AZ::Vector3(10.0f, 20.0f, 30.0f)); - m_cameraViewportContextView->SetCameraTransform(nextTransform); - - // When - AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); - AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( - currentReferenceFrame, TestViewportId, - &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); - - // Then - EXPECT_THAT(currentReferenceFrame, IsClose(nextTransform)); - } - - TEST_F(EditorCameraFixture, ReferenceFrameReturnedToIdentityAfterClear) - { - // Given - const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( - AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); - AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( - TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame); // When AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( - TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::ClearReferenceFrame); - - AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); - AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( - currentReferenceFrame, TestViewportId, - &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StopTrackingTransform); // Then - EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); + bool trackingTransform = false; + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + trackingTransform, TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::IsTrackingTransform); + + EXPECT_THAT(trackingTransform, ::testing::IsFalse()); } TEST_F(EditorCameraFixture, InterpolateToTransform) @@ -185,7 +164,7 @@ namespace UnitTest const AZ::Transform referenceFrame = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationX(AZ::DegToRad(90.0f)), AZ::Vector3(1.0f, 2.0f, 3.0f)); AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( - TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::SetReferenceFrame, referenceFrame); + TestViewportId, &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::StartTrackingTransform, referenceFrame); AZ::Transform transformToInterpolateTo = AZ::Transform::CreateFromQuaternionAndTranslation( AZ::Quaternion::CreateRotationZ(AZ::DegToRad(90.0f)), AZ::Vector3(20.0f, 40.0f, 60.0f)); @@ -199,16 +178,10 @@ namespace UnitTest m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); m_controllerList->UpdateViewport({ TestViewportId, AzFramework::FloatSeconds(0.5f), AZ::ScriptTimePoint() }); - AZ::Transform currentReferenceFrame = AZ::Transform::CreateTranslation(AZ::Vector3(1.0f, 2.0f, 3.0f)); - AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( - currentReferenceFrame, TestViewportId, - &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::GetReferenceFrame); - const auto finalTransform = m_cameraViewportContextView->GetCameraTransform(); // Then EXPECT_THAT(finalTransform, IsClose(transformToInterpolateTo)); - EXPECT_THAT(currentReferenceFrame, IsClose(AZ::Transform::CreateIdentity())); } } // namespace UnitTest diff --git a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp index 275df11784..c776a57c13 100644 --- a/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp +++ b/Code/Editor/Lib/Tests/test_ModularViewportCameraController.cpp @@ -146,6 +146,17 @@ namespace UnitTest controller->SetCameraPropsBuilderCallback( [](AzFramework::CameraProps& cameraProps) { + // note: rotateSmoothness is also used for roll (not related to camera input directly) + cameraProps.m_rotateSmoothnessFn = [] + { + return 5.0f; + }; + + cameraProps.m_translateSmoothnessFn = [] + { + return 5.0f; + }; + cameraProps.m_rotateSmoothingEnabledFn = [] { return false; diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp index 26c9db64aa..2f637db7cd 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.cpp @@ -94,27 +94,27 @@ namespace AzFramework float y; float z; - // 2.4 Factor as RzRyRx - if (orientation.GetElement(2, 0) < 1.0f) + // 2.5 Factor as RzRxRy + if (orientation.GetElement(2, 1) < 1.0f) { - if (orientation.GetElement(2, 0) > -1.0f) + if (orientation.GetElement(2, 1) > -1.0f) { - x = AZStd::atan2(orientation.GetElement(2, 1), orientation.GetElement(2, 2)); - y = AZStd::asin(-orientation.GetElement(2, 0)); - z = AZStd::atan2(orientation.GetElement(1, 0), orientation.GetElement(0, 0)); + x = AZStd::asin(orientation.GetElement(2, 1)); + y = AZStd::atan2(-orientation.GetElement(2, 0), orientation.GetElement(2, 2)); + z = AZStd::atan2(-orientation.GetElement(0, 1), orientation.GetElement(1, 1)); } else { - x = 0.0f; - y = AZ::Constants::Pi * 0.5f; - z = -AZStd::atan2(-orientation.GetElement(2, 1), orientation.GetElement(1, 1)); + x = -AZ::Constants::Pi * 0.5f; + y = 0.0f; + z = -AZStd::atan2(orientation.GetElement(0, 2), orientation.GetElement(0, 0)); } } else { - x = 0.0f; - y = -AZ::Constants::Pi * 0.5f; - z = AZStd::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1)); + x = AZ::Constants::Pi * 0.5f; + y = 0.0f; + z = AZStd::atan2(orientation.GetElement(0, 2), orientation.GetElement(0, 0)); } return { x, y, z }; @@ -122,14 +122,36 @@ namespace AzFramework void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform) { - const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform)); + UpdateCameraFromTranslationAndRotation( + camera, transform.GetTranslation(), AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform))); + } + void UpdateCameraFromTranslationAndRotation(Camera& camera, const AZ::Vector3& translation, const AZ::Vector3& eulerAngles) + { camera.m_pitch = eulerAngles.GetX(); camera.m_yaw = eulerAngles.GetZ(); - camera.m_pivot = transform.GetTranslation(); + camera.m_pivot = translation; camera.m_offset = AZ::Vector3::CreateZero(); } + float SmoothValueTime(const float smoothness, float deltaTime) + { + // note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent + // article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php + const float rate = AZStd::exp2(smoothness); + return AZStd::exp2(-rate * deltaTime); + } + + float SmoothValue(const float target, const float current, const float time) + { + return AZ::Lerp(target, current, time); + } + + float SmoothValue(const float target, const float current, const float smoothness, const float deltaTime) + { + return SmoothValue(target, current, SmoothValueTime(smoothness, deltaTime)); + } + bool CameraSystem::HandleEvents(const InputEvent& event) { if (const auto& cursor = AZStd::get_if(&event)) @@ -749,14 +771,11 @@ namespace AzFramework } Camera camera; - // note: the math for the lerp smoothing implementation for camera rotation and translation was inspired by this excellent - // article by Scott Lembcke: https://www.gamasutra.com/blogs/ScottLembcke/20180404/316046/Improved_Lerp_Smoothing.php if (cameraProps.m_rotateSmoothingEnabledFn()) { - const float lookRate = AZStd::exp2(cameraProps.m_rotateSmoothnessFn()); - const float lookTime = AZStd::exp2(-lookRate * deltaTime); - camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookTime); - camera.m_yaw = AZ::Lerp(targetYaw, currentYaw, lookTime); + const float lookTime = SmoothValueTime(cameraProps.m_rotateSmoothnessFn(), deltaTime); + camera.m_pitch = SmoothValue(targetCamera.m_pitch, currentCamera.m_pitch, lookTime); + camera.m_yaw = SmoothValue(targetYaw, currentYaw, lookTime); } else { @@ -766,8 +785,7 @@ namespace AzFramework if (cameraProps.m_translateSmoothingEnabledFn()) { - const float moveRate = AZStd::exp2(cameraProps.m_translateSmoothnessFn()); - const float moveTime = AZStd::exp2(-moveRate * deltaTime); + const float moveTime = SmoothValueTime(cameraProps.m_rotateSmoothnessFn(), deltaTime); camera.m_pivot = targetCamera.m_pivot.Lerp(currentCamera.m_pivot, moveTime); camera.m_offset = targetCamera.m_offset.Lerp(currentCamera.m_offset, moveTime); } diff --git a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h index 2d02bb0b6d..ad9fed0f5d 100644 --- a/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h +++ b/Code/Framework/AzFramework/AzFramework/Viewport/CameraInput.h @@ -85,6 +85,19 @@ namespace AzFramework //! Extracts Euler angles (orientation) and translation from the transform and writes the values to the camera. void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform); + //! Writes the translation value and Euler angles to the camera. + void UpdateCameraFromTranslationAndRotation(Camera& camera, const AZ::Vector3& translation, const AZ::Vector3& eulerAngles); + + //! Returns the time ('t') input value to use with SmoothValue. + //! Useful if it is to be reused for multiple calls to SmoothValue. + float SmoothValueTime(float smoothness, float deltaTime); + + // Smoothly interpolate a value from current to target according to a smoothing parameter. + float SmoothValue(float target, float current, float smoothness, float deltaTime); + + // Overload of SmoothValue that takes time ('t') value directly. + float SmoothValue(float target, float current, float time); + //! Generic motion type. template struct MotionEvent diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 1200cb3d79..65eff30874 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -113,14 +113,14 @@ namespace AtomToolsFramework // ModularViewportCameraControllerRequestBus overrides ... void InterpolateToTransform(const AZ::Transform& worldFromLocal) override; - AZ::Transform GetReferenceFrame() const override; - void SetReferenceFrame(const AZ::Transform& worldFromLocal) override; - void ClearReferenceFrame() override; + void StartTrackingTransform(const AZ::Transform& worldFromLocal) override; + void StopTrackingTransform() override; + bool IsTrackingTransform() const override; private: - //! Update the reference frame after a change has been made to the camera - //! view without updating the internal camera via user input. - void RefreshReferenceFrame(); + //! Combine the current camera transform with any potential roll from the tracked + //! transform (this is usually zero). + AZ::Transform CombinedCameraTransform() const; //! The current mode the camera controller is in. enum class CameraMode @@ -141,21 +141,21 @@ namespace AtomToolsFramework AzFramework::Camera m_camera; //!< The current camera state (pitch/yaw/position/look-distance). AzFramework::Camera m_targetCamera; //!< The target (next) camera state that m_camera is catching up to. AzFramework::Camera m_previousCamera; //!< The state of the camera from the previous frame. - AZStd::optional m_storedCamera; //!< A potentially stored camera for when a custom reference frame is set. + AZStd::optional m_storedCamera; //!< A potentially stored camera for when a transform is being tracked. AzFramework::CameraSystem m_cameraSystem; //!< The camera system responsible for managing all CameraInputs. AzFramework::CameraProps m_cameraProps; //!< Camera properties to control rotate and translate smoothness. CameraControllerPriorityFn m_priorityFn; //!< Controls at what priority the camera controller should respond to events. CameraAnimation m_cameraAnimation; //!< Camera animation state (used during CameraMode::Animation). CameraMode m_cameraMode = CameraMode::Control; //!< The current mode the camera is operating in. - //! An additional reference frame the camera can operate in (identity has no effect). - AZ::Transform m_referenceFrameOverride = AZ::Transform::CreateIdentity(); - //! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally). - bool m_updatingTransformInternally = false; + float m_roll = 0.0f; //!< The current amount of roll to be applied to the camera. + float m_targetRoll = 0.0f; //!< The target amount of roll to be applied to the camera (current will move towards this). //! Listen for camera view changes outside of the camera controller. AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler; //! The current instance of the modular camera viewport context. AZStd::unique_ptr m_modularCameraViewportContext; + //! Flag to prevent circular updates of the camera transform (while the viewport transform is being updated internally). + bool m_updatingTransformInternally = false; }; //! Placeholder implementation for ModularCameraViewportContext (useful for verifying the interface). diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index ab397692e4..59c13bb4b8 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -31,15 +31,16 @@ namespace AtomToolsFramework //! @param worldFromLocal The transform of where the camera should end up. virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0; - //! Return the current reference frame. - //! @note If a reference frame has not been set or a frame has been cleared, this is just the identity. - virtual AZ::Transform GetReferenceFrame() const = 0; + //! Start tracking a transform. + //! Store the current camera transform and move to the next camera transform. + virtual void StartTrackingTransform(const AZ::Transform& worldFromLocal) = 0; - //! Set a new reference frame other than the identity for the camera controller. - virtual void SetReferenceFrame(const AZ::Transform& worldFromLocal) = 0; + //! Stop tracking the set transform. + //! The previously stored camera transform is restored. + virtual void StopTrackingTransform() = 0; - //! Clear the current reference frame to restore the identity. - virtual void ClearReferenceFrame() = 0; + //! Return if the tracking transform is set. + virtual bool IsTrackingTransform() const = 0; protected: ~ModularViewportCameraControllerRequests() = default; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index a92bfdbcdb..e8aca16826 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -175,16 +176,12 @@ namespace AtomToolsFramework // ignore these updates if the camera is being updated internally if (!m_updatingTransformInternally) { - if (m_storedCamera.has_value()) - { - // if an external change occurs ensure we update the stored reference frame if one is set - RefreshReferenceFrame(); - return; - } - m_previousCamera = m_targetCamera; - UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform()); - m_camera = m_targetCamera; + + const AZ::Transform transform = m_modularCameraViewportContext->GetCameraTransform(); + const AZ::Vector3 eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform)); + UpdateCameraFromTranslationAndRotation(m_targetCamera, transform.GetTranslation(), eulerAngles); + m_targetRoll = eulerAngles.GetY(); } }; @@ -227,7 +224,9 @@ namespace AtomToolsFramework { m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count()); m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, m_cameraProps, event.m_deltaTime.count()); - m_modularCameraViewportContext->SetCameraTransform(m_referenceFrameOverride * m_camera.Transform()); + m_roll = AzFramework::SmoothValue(m_targetRoll, m_roll, m_cameraProps.m_rotateSmoothnessFn(), event.m_deltaTime.count()); + + m_modularCameraViewportContext->SetCameraTransform(CombinedCameraTransform()); } else if (m_cameraMode == CameraMode::Animation) { @@ -250,6 +249,7 @@ namespace AtomToolsFramework m_camera.m_yaw = eulerAngles.GetZ(); m_camera.m_pivot = current.GetTranslation(); m_camera.m_offset = AZ::Vector3::CreateZero(); + m_targetRoll = eulerAngles.GetY(); m_targetCamera = m_camera; m_modularCameraViewportContext->SetCameraTransform(current); @@ -257,7 +257,6 @@ namespace AtomToolsFramework if (animationTime >= 1.0f) { m_cameraMode = CameraMode::Control; - RefreshReferenceFrame(); } } @@ -267,45 +266,38 @@ namespace AtomToolsFramework void ModularViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal) { m_cameraMode = CameraMode::Animation; - m_cameraAnimation = CameraAnimation{ m_referenceFrameOverride * m_camera.Transform(), worldFromLocal, 0.0f }; + m_cameraAnimation = CameraAnimation{ CombinedCameraTransform(), worldFromLocal, 0.0f }; } - AZ::Transform ModularViewportCameraControllerInstance::GetReferenceFrame() const - { - return m_referenceFrameOverride; - } - - void ModularViewportCameraControllerInstance::SetReferenceFrame(const AZ::Transform& worldFromLocal) + void ModularViewportCameraControllerInstance::StartTrackingTransform(const AZ::Transform& worldFromLocal) { if (!m_storedCamera.has_value()) { m_storedCamera = m_previousCamera; } - m_referenceFrameOverride = worldFromLocal; - m_targetCamera.m_pitch = 0.0f; - m_targetCamera.m_yaw = 0.0f; + const auto angles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromQuaternion(worldFromLocal.GetRotation())); + m_targetCamera.m_pitch = angles.GetX(); + m_targetCamera.m_yaw = angles.GetZ(); m_targetCamera.m_offset = AZ::Vector3::CreateZero(); - m_targetCamera.m_pivot = AZ::Vector3::CreateZero(); - m_camera = m_targetCamera; + m_targetCamera.m_pivot = worldFromLocal.GetTranslation(); + m_targetRoll = angles.GetY(); } - void ModularViewportCameraControllerInstance::ClearReferenceFrame() + void ModularViewportCameraControllerInstance::StopTrackingTransform() { - m_referenceFrameOverride = AZ::Transform::CreateIdentity(); - if (m_storedCamera.has_value()) { m_targetCamera = m_storedCamera.value(); - m_camera = m_targetCamera; + m_targetRoll = 0.0f; } m_storedCamera.reset(); } - void ModularViewportCameraControllerInstance::RefreshReferenceFrame() + bool ModularViewportCameraControllerInstance::IsTrackingTransform() const { - m_referenceFrameOverride = m_modularCameraViewportContext->GetCameraTransform() * m_camera.Transform().GetInverse(); + return m_storedCamera.has_value(); } AZ::Transform PlaceholderModularCameraViewportContextImpl::GetCameraTransform() const @@ -324,4 +316,9 @@ namespace AtomToolsFramework { handler.Connect(m_viewMatrixChangedEvent); } + + AZ::Transform ModularViewportCameraControllerInstance::CombinedCameraTransform() const + { + return m_camera.Transform() * AZ::Transform::CreateFromMatrix3x3(AZ::Matrix3x3::CreateRotationY(m_targetRoll)); + } } // namespace AtomToolsFramework