Fix issue with mouse input for viewport camera (#3210)

* fix for drift accumulating in the viewport camera

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* fix typo and update how events are stored

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* respond to PR feedback and fix linux and windows build issues

Signed-off-by: hultonha <hultonha@amazon.co.uk>

* fix failing unit tests in camera input

Signed-off-by: hultonha <hultonha@amazon.co.uk>
This commit is contained in:
hultonha
2021-08-19 09:06:24 +01:00
committed by GitHub
parent 586678a5f9
commit 80e08dd947
18 changed files with 538 additions and 134 deletions
@@ -41,6 +41,7 @@ namespace AtomToolsFramework
display.DrawLine(transform.GetTranslation(), transform.GetTranslation() + transform.GetBasisZ().GetNormalizedSafe() * axisLength);
}
// convenience function to access the ViewportContext for the given ViewportId.
static AZ::RPI::ViewportContextPtr RetrieveViewportContext(const AzFramework::ViewportId viewportId)
{
auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
@@ -58,6 +59,35 @@ namespace AtomToolsFramework
return viewportContext;
}
ModularCameraViewportContextImpl::ModularCameraViewportContextImpl(const AzFramework::ViewportId viewportId)
: m_viewportId(viewportId)
{
}
AZ::Transform ModularCameraViewportContextImpl::GetCameraTransform() const
{
if (auto viewportContext = RetrieveViewportContext(m_viewportId))
{
return viewportContext->GetCameraTransform();
}
return AZ::Transform::CreateIdentity();
}
void ModularCameraViewportContextImpl::SetCameraTransform(const AZ::Transform& transform)
{
if (auto viewportContext = RetrieveViewportContext(m_viewportId))
{
viewportContext->SetCameraTransform(transform);
}
}
void ModularCameraViewportContextImpl::ConnectViewMatrixChangedHandler(AZ::RPI::ViewportContext::MatrixChangedEvent::Handler& handler)
{
if (auto viewportContext = RetrieveViewportContext(m_viewportId))
{
viewportContext->ConnectViewMatrixChangedHandler(handler);
}
}
void ModularViewportCameraController::SetCameraListBuilderCallback(const CameraListBuilder& builder)
{
m_cameraListBuilder = builder;
@@ -73,6 +103,11 @@ namespace AtomToolsFramework
m_cameraControllerPriorityBuilder = builder;
}
void ModularViewportCameraController::SetCameraViewportContextBuilderCallback(const CameraViewportContextBuilder& builder)
{
m_cameraViewportContextBuilder = builder;
}
void ModularViewportCameraController::SetupCameras(AzFramework::Cameras& cameras)
{
if (m_cameraListBuilder)
@@ -97,6 +132,15 @@ namespace AtomToolsFramework
}
}
void ModularViewportCameraController::SetupCameraControllerViewportContext(
AZStd::unique_ptr<ModularCameraViewportContext>& cameraViewportContext)
{
if (m_cameraViewportContextBuilder)
{
m_cameraViewportContextBuilder(cameraViewportContext);
}
}
// what priority should the camera system respond to
AzFramework::ViewportControllerPriority DefaultCameraControllerPriority(const AzFramework::CameraSystem& cameraSystem)
{
@@ -119,23 +163,20 @@ namespace AtomToolsFramework
controller->SetupCameras(m_cameraSystem.m_cameras);
controller->SetupCameraProperties(m_cameraProps);
controller->SetupCameraControllerPriority(m_priorityFn);
controller->SetupCameraControllerViewportContext(m_modularCameraViewportContext);
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
auto handleCameraChange = [this](const AZ::Matrix4x4&)
{
auto handleCameraChange = [this, viewportContext](const AZ::Matrix4x4&)
// ignore these updates if the camera is being updated internally
if (!m_updatingTransformInternally)
{
// ignore these updates if the camera is being updated internally
if (!m_updatingTransformInternally)
{
UpdateCameraFromTransform(m_targetCamera, viewportContext->GetCameraTransform());
m_camera = m_targetCamera;
}
};
UpdateCameraFromTransform(m_targetCamera, m_modularCameraViewportContext->GetCameraTransform());
m_camera = m_targetCamera;
}
};
m_cameraViewMatrixChangeHandler = AZ::RPI::ViewportContext::MatrixChangedEvent::Handler(handleCameraChange);
viewportContext->ConnectViewMatrixChangedHandler(m_cameraViewMatrixChangeHandler);
}
m_cameraViewMatrixChangeHandler = AZ::RPI::ViewportContext::MatrixChangedEvent::Handler(handleCameraChange);
m_modularCameraViewportContext->ConnectViewMatrixChangedHandler(m_cameraViewMatrixChangeHandler);
AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
ModularViewportCameraControllerRequestBus::Handler::BusConnect(viewportId);
@@ -151,7 +192,11 @@ namespace AtomToolsFramework
{
if (event.m_priority == m_priorityFn(m_cameraSystem))
{
return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel));
AzFramework::WindowSize windowSize;
AzFramework::WindowRequestBus::EventResult(
windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize));
}
return false;
@@ -165,61 +210,58 @@ namespace AtomToolsFramework
return;
}
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
m_updatingTransformInternally = true;
if (m_cameraMode == CameraMode::Control)
{
m_updatingTransformInternally = true;
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());
if (m_cameraMode == CameraMode::Control)
// if there has been an interpolation, only clear the look at point if it is no longer
// centered in the view (the camera has looked away from it)
if (m_lookAtAfterInterpolation.has_value())
{
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());
// if there has been an interpolation, only clear the look at point if it is no longer
// centered in the view (the camera has looked away from it)
if (m_lookAtAfterInterpolation.has_value())
if (const float lookDirection =
(*m_lookAtAfterInterpolation - m_camera.Translation()).GetNormalized().Dot(m_camera.Transform().GetBasisY());
!AZ::IsCloseMag(lookDirection, 1.0f, 0.001f))
{
if (const float lookDirection =
(*m_lookAtAfterInterpolation - m_camera.Translation()).GetNormalized().Dot(m_camera.Transform().GetBasisY());
!AZ::IsCloseMag(lookDirection, 1.0f, 0.001f))
{
m_lookAtAfterInterpolation = {};
}
m_lookAtAfterInterpolation = {};
}
viewportContext->SetCameraTransform(m_camera.Transform());
}
else if (m_cameraMode == CameraMode::Animation)
{
const auto smootherStepFn = [](const float t)
{
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
};
const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation;
const float transitionTime = smootherStepFn(animationTime);
const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation(
transformStart.GetRotation().Slerp(transformEnd.GetRotation(), transitionTime),
transformStart.GetTranslation().Lerp(transformEnd.GetTranslation(), transitionTime));
const AZ::Vector3 eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(current));
m_camera.m_pitch = eulerAngles.GetX();
m_camera.m_yaw = eulerAngles.GetZ();
m_camera.m_lookAt = current.GetTranslation();
m_targetCamera = m_camera;
if (animationTime >= 1.0f)
{
m_cameraMode = CameraMode::Control;
}
m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f);
viewportContext->SetCameraTransform(current);
}
m_updatingTransformInternally = false;
m_modularCameraViewportContext->SetCameraTransform(m_camera.Transform());
}
else if (m_cameraMode == CameraMode::Animation)
{
const auto smootherStepFn = [](const float t)
{
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
};
const auto& [transformStart, transformEnd, animationTime] = m_cameraAnimation;
const float transitionTime = smootherStepFn(animationTime);
const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation(
transformStart.GetRotation().Slerp(transformEnd.GetRotation(), transitionTime),
transformStart.GetTranslation().Lerp(transformEnd.GetTranslation(), transitionTime));
const AZ::Vector3 eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(current));
m_camera.m_pitch = eulerAngles.GetX();
m_camera.m_yaw = eulerAngles.GetZ();
m_camera.m_lookAt = current.GetTranslation();
m_targetCamera = m_camera;
if (animationTime >= 1.0f)
{
m_cameraMode = CameraMode::Control;
}
m_cameraAnimation.m_time = AZ::GetClamp(animationTime + event.m_deltaTime.count(), 0.0f, 1.0f);
m_modularCameraViewportContext->SetCameraTransform(current);
}
m_updatingTransformInternally = false;
}
void ModularViewportCameraControllerInstance::DisplayViewport(