Merge pull request #538 from aws-lumberyard-dev/hultonha_LYN-2315_camera-phase-3

Camera Updates - Part 3
This commit is contained in:
Tom Hulton-Harrop
2021-05-04 17:30:12 +01:00
committed by GitHub
6 changed files with 310 additions and 150 deletions
@@ -15,15 +15,146 @@
#include <AzCore/Console/IConsole.h>
#include <AzCore/Math/MathUtils.h>
#include <AzCore/Math/Plane.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Windowing/WindowBus.h>
AZ_CVAR(
float, ed_newCameraSystemDefaultPlaneHeight, 34.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The default height of the ground plane to do intersection tests against when orbiting");
namespace AzFramework
{
AZ_CVAR(
float, ed_cameraSystemDefaultPlaneHeight, 34.0f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The default height of the ground plane to do intersection tests against when orbiting");
AZ_CVAR(float, ed_cameraSystemBoostMultiplier, 3.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSpeed, 10.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyScrollSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemOrbitDollyCursorSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemScrollTranslateSpeed, 0.02f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemDefaultOrbitDistance, 60.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemMaxOrbitDistance, 100.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemLookSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemTranslateSmoothness, 5.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemRotateSpeed, 0.005f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(float, ed_cameraSystemPanSpeed, 0.01f, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, ed_cameraSystemPanInvertX, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(bool, ed_cameraSystemPanInvertY, true, nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateForwardKey, "keyboard_key_alphanumeric_W", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateBackwardKey, "keyboard_key_alphanumeric_S", nullptr, AZ::ConsoleFunctorFlags::Null,
"");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateLeftKey, "keyboard_key_alphanumeric_A", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateRightKey, "keyboard_key_alphanumeric_D", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemTranslateUpKey, "keyboard_key_alphanumeric_E", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateDownKey, "keyboard_key_alphanumeric_Q", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(
AZ::CVarFixedString, ed_cameraSystemTranslateBoostKey, "keyboard_key_modifier_shift_l", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitKey, "keyboard_key_modifier_alt_l", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemFreeLookButton, "mouse_button_right", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemFreePanButton, "mouse_button_middle", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitLookButton, "mouse_button_left", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitDollyButton, "mouse_button_right", nullptr, AZ::ConsoleFunctorFlags::Null, "");
AZ_CVAR(AZ::CVarFixedString, ed_cameraSystemOrbitPanButton, "mouse_button_middle", nullptr, AZ::ConsoleFunctorFlags::Null, "");
static InputChannelId CameraTranslateForwardId;
static InputChannelId CameraTranslateBackwardId;
static InputChannelId CameraTranslateLeftId;
static InputChannelId CameraTranslateRightId;
static InputChannelId CameraTranslateDownId;
static InputChannelId CameraTranslateUpId;
static InputChannelId CameraTranslateBoostId;
static InputChannelId CameraOrbitId;
// externed elsewhere
InputChannelId CameraFreeLookButton;
InputChannelId CameraFreePanButton;
InputChannelId CameraOrbitLookButton;
InputChannelId CameraOrbitDollyButton;
InputChannelId CameraOrbitPanButton;
void ReloadCameraKeyBindings()
{
const AZ::CVarFixedString& forward = ed_cameraSystemTranslateForwardKey;
CameraTranslateForwardId = InputChannelId(forward.c_str());
const AZ::CVarFixedString& backward = ed_cameraSystemTranslateBackwardKey;
CameraTranslateBackwardId = InputChannelId(backward.c_str());
const AZ::CVarFixedString& left = ed_cameraSystemTranslateLeftKey;
CameraTranslateLeftId = InputChannelId(left.c_str());
const AZ::CVarFixedString& right = ed_cameraSystemTranslateRightKey;
CameraTranslateRightId = InputChannelId(right.c_str());
const AZ::CVarFixedString& down = ed_cameraSystemTranslateDownKey;
CameraTranslateDownId = InputChannelId(down.c_str());
const AZ::CVarFixedString& up = ed_cameraSystemTranslateUpKey;
CameraTranslateUpId = InputChannelId(up.c_str());
const AZ::CVarFixedString& boost = ed_cameraSystemTranslateBoostKey;
CameraTranslateBoostId = InputChannelId(boost.c_str());
const AZ::CVarFixedString& orbit = ed_cameraSystemOrbitKey;
CameraOrbitId = InputChannelId(orbit.c_str());
const AZ::CVarFixedString& freeLook = ed_cameraSystemFreeLookButton;
CameraFreeLookButton = InputChannelId(freeLook.c_str());
const AZ::CVarFixedString& freePan = ed_cameraSystemFreePanButton;
CameraFreePanButton = InputChannelId(freePan.c_str());
const AZ::CVarFixedString& orbitLook = ed_cameraSystemOrbitLookButton;
CameraOrbitLookButton = InputChannelId(orbitLook.c_str());
const AZ::CVarFixedString& orbitDolly = ed_cameraSystemOrbitDollyButton;
CameraOrbitDollyButton = InputChannelId(orbitDolly.c_str());
const AZ::CVarFixedString& orbitPan = ed_cameraSystemOrbitPanButton;
CameraOrbitPanButton = InputChannelId(orbitPan.c_str());
}
static void ReloadCameraKeyBindingsConsole(const AZ::ConsoleCommandContainer&)
{
ReloadCameraKeyBindings();
}
AZ_CONSOLEFREEFUNC(ReloadCameraKeyBindingsConsole, AZ::ConsoleFunctorFlags::Null, "Reload keybindings for the modern camera system");
// Based on paper by David Eberly - https://www.geometrictools.com/Documentation/EulerAngles.pdf
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation)
{
float x;
float y;
float z;
// 2.4 Factor as RzRyRx
if (orientation.GetElement(2, 0) < 1.0f)
{
if (orientation.GetElement(2, 0) > -1.0f)
{
x = std::atan2(orientation.GetElement(2, 1), orientation.GetElement(2, 2));
y = std::asin(-orientation.GetElement(2, 0));
z = std::atan2(orientation.GetElement(1, 0), orientation.GetElement(0, 0));
}
else
{
x = 0.0f;
y = AZ::Constants::Pi * 0.5f;
z = -std::atan2(-orientation.GetElement(2, 1), orientation.GetElement(1, 1));
}
}
else
{
x = 0.0f;
y = -AZ::Constants::Pi * 0.5f;
z = std::atan2(-orientation.GetElement(1, 2), orientation.GetElement(1, 1));
}
return {x, y, z};
}
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform)
{
const auto eulerAngles = AzFramework::EulerAngles(AZ::Matrix3x3::CreateFromTransform(transform));
camera.m_lookAt = transform.GetTranslation();
camera.m_pitch = eulerAngles.GetX();
camera.m_yaw = eulerAngles.GetZ();
}
bool CameraSystem::HandleEvents(const InputEvent& event)
{
if (const auto& cursor_motion = AZStd::get_if<CursorMotionEvent>(&event))
@@ -144,7 +275,7 @@ namespace AzFramework
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == m_channelId)
if (input->m_channelId == m_rotateChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -164,8 +295,8 @@ namespace AzFramework
{
Camera nextCamera = targetCamera;
nextCamera.m_pitch -= float(cursorDelta.m_y) * m_props.m_rotateSpeed;
nextCamera.m_yaw -= float(cursorDelta.m_x) * m_props.m_rotateSpeed;
nextCamera.m_pitch -= float(cursorDelta.m_y) * ed_cameraSystemRotateSpeed;
nextCamera.m_yaw -= float(cursorDelta.m_x) * ed_cameraSystemRotateSpeed;
const auto clampRotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
@@ -180,7 +311,7 @@ namespace AzFramework
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Middle)
if (input->m_channelId == m_panChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -202,49 +333,48 @@ namespace AzFramework
const auto panAxes = m_panAxesFn(nextCamera);
const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * m_props.m_panSpeed;
const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * m_props.m_panSpeed;
const auto deltaPanX = float(cursorDelta.m_x) * panAxes.m_horizontalAxis * ed_cameraSystemPanSpeed;
const auto deltaPanY = float(cursorDelta.m_y) * panAxes.m_verticalAxis * ed_cameraSystemPanSpeed;
const auto inv = [](const bool invert) {
constexpr float Dir[] = {1.0f, -1.0f};
return Dir[static_cast<int>(invert)];
};
nextCamera.m_lookAt += deltaPanX * inv(m_props.m_panInvertX);
nextCamera.m_lookAt += deltaPanY * -inv(m_props.m_panInvertY);
nextCamera.m_lookAt += deltaPanX * inv(ed_cameraSystemPanInvertX);
nextCamera.m_lookAt += deltaPanY * -inv(ed_cameraSystemPanInvertY);
return nextCamera;
}
TranslateCameraInput::TranslationType TranslateCameraInput::translationFromKey(InputChannelId channelId)
{
// note: remove hard-coded InputDevice keys
if (channelId == InputDeviceKeyboard::Key::AlphanumericW)
if (channelId == CameraTranslateForwardId)
{
return TranslationType::Forward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericS)
if (channelId == CameraTranslateBackwardId)
{
return TranslationType::Backward;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericA)
if (channelId == CameraTranslateLeftId)
{
return TranslationType::Left;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericD)
if (channelId == CameraTranslateRightId)
{
return TranslationType::Right;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericQ)
if (channelId == CameraTranslateDownId)
{
return TranslationType::Down;
}
if (channelId == InputDeviceKeyboard::Key::AlphanumericE)
if (channelId == CameraTranslateUpId)
{
return TranslationType::Up;
}
@@ -269,19 +399,19 @@ namespace AzFramework
BeginActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
if (input->m_channelId == CameraTranslateBoostId)
{
m_boost = true;
}
}
else if (input->m_state == InputChannel::State::Ended)
{
m_translation ^= translationFromKey(input->m_channelId);
m_translation &= ~(translationFromKey(input->m_channelId));
if (m_translation == TranslationType::Nil)
{
EndActivation();
}
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierShiftL)
if (input->m_channelId == CameraTranslateBoostId)
{
m_boost = false;
}
@@ -300,8 +430,8 @@ namespace AzFramework
const auto axisY = translationBasis.GetBasisY();
const auto axisZ = translationBasis.GetBasisZ();
const float speed = [boost = m_boost, props = m_props]() {
return props.m_translateSpeed * (boost ? props.m_boostMultiplier : 1.0f);
const float speed = [boost = m_boost]() {
return ed_cameraSystemTranslateSpeed * (boost ? ed_cameraSystemBoostMultiplier : 1.0f);
}();
if ((m_translation & TranslationType::Forward) == TranslationType::Forward)
@@ -352,7 +482,7 @@ namespace AzFramework
{
if (const auto* input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceKeyboard::Key::ModifierAltL)
if (input->m_channelId == CameraOrbitId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -379,18 +509,17 @@ namespace AzFramework
if (Beginning())
{
float hit_distance = 0.0f;
if (AZ::Plane::CreateFromNormalAndPoint(
AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_newCameraSystemDefaultPlaneHeight))
if (AZ::Plane::CreateFromNormalAndPoint(AZ::Vector3::CreateAxisZ(), AZ::Vector3::CreateAxisZ(ed_cameraSystemDefaultPlaneHeight))
.CastRay(targetCamera.Translation(), targetCamera.Rotation().GetBasisY(), hit_distance))
{
hit_distance = AZStd::min(hit_distance, m_props.m_maxOrbitDistance);
hit_distance = AZStd::min<float>(hit_distance, ed_cameraSystemMaxOrbitDistance);
nextCamera.m_lookDist = -hit_distance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * hit_distance;
}
else
{
nextCamera.m_lookDist = -m_props.m_defaultOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * m_props.m_defaultOrbitDistance;
nextCamera.m_lookDist = -ed_cameraSystemMaxOrbitDistance;
nextCamera.m_lookAt = targetCamera.Translation() + targetCamera.Rotation().GetBasisY() * ed_cameraSystemMaxOrbitDistance;
}
}
@@ -423,7 +552,7 @@ namespace AzFramework
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * m_props.m_dollySpeed, 0.0f);
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + scrollDelta * ed_cameraSystemOrbitDollyScrollSpeed, 0.0f);
EndActivation();
return nextCamera;
}
@@ -432,7 +561,7 @@ namespace AzFramework
{
if (const auto& input = AZStd::get_if<DiscreteInputEvent>(&event))
{
if (input->m_channelId == InputDeviceMouse::Button::Right)
if (input->m_channelId == m_dollyChannelId)
{
if (input->m_state == InputChannel::State::Began)
{
@@ -451,7 +580,7 @@ namespace AzFramework
[[maybe_unused]] const float deltaTime)
{
Camera nextCamera = targetCamera;
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * m_props.m_dollySpeed, 0.0f);
nextCamera.m_lookDist = AZ::GetMin(nextCamera.m_lookDist + float(cursorDelta.m_y) * ed_cameraSystemOrbitDollyCursorSpeed, 0.0f);
return nextCamera;
}
@@ -472,14 +601,14 @@ namespace AzFramework
const auto translation_basis = LookTranslation(nextCamera);
const auto axisY = translation_basis.GetBasisY();
nextCamera.m_lookAt += axisY * scrollDelta * m_props.m_translateSpeed;
nextCamera.m_lookAt += axisY * scrollDelta * ed_cameraSystemScrollTranslateSpeed;
EndActivation();
return nextCamera;
}
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, const float deltaTime)
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const float deltaTime)
{
const auto clamp_rotation = [](const float angle) { return std::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi); };
@@ -500,11 +629,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
const float lookRate = std::exp2(props.m_lookSmoothness);
const float lookRate = std::exp2(ed_cameraSystemLookSmoothness);
const float lookT = std::exp2(-lookRate * deltaTime);
camera.m_pitch = AZ::Lerp(targetCamera.m_pitch, currentCamera.m_pitch, lookT);
camera.m_yaw = AZ::Lerp(targetYaw, currentYaw, lookT);
const float moveRate = std::exp2(props.m_moveSmoothness);
const float moveRate = std::exp2(ed_cameraSystemTranslateSmoothness);
const float moveT = std::exp2(-moveRate * deltaTime);
camera.m_lookDist = AZ::Lerp(targetCamera.m_lookDist, currentCamera.m_lookDist, moveT);
camera.m_lookAt = targetCamera.m_lookAt.Lerp(currentCamera.m_lookAt, moveT);
@@ -521,20 +650,19 @@ namespace AzFramework
return button == inputChannelId;
});
if (inputChannelId == InputDeviceMouse::SystemCursorPosition)
if (inputChannelId == InputDeviceMouse::Movement::X || inputChannelId == InputDeviceMouse::Movement::Y)
{
AZ::Vector2 systemCursorPositionNormalized = AZ::Vector2::CreateZero();
InputSystemCursorRequestBus::EventResult(
systemCursorPositionNormalized, inputDeviceId, &InputSystemCursorRequestBus::Events::GetSystemCursorPositionNormalized);
const auto* position = inputChannel.GetCustomData<AzFramework::InputChannel::PositionData2D>();
AZ_Assert(position, "Expected PositionData2D but found nullptr");
return CursorMotionEvent{ScreenPoint(
systemCursorPositionNormalized.GetX() * windowSize.m_width, systemCursorPositionNormalized.GetY() * windowSize.m_height)};
position->m_normalizedPosition.GetX() * windowSize.m_width, position->m_normalizedPosition.GetY() * windowSize.m_height)};
}
else if (inputChannelId == InputDeviceMouse::Movement::Z)
{
return ScrollEvent{inputChannel.GetValue()};
}
else if ((InputDeviceMouse::IsMouseDevice(inputDeviceId) && wasMouseButton) || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
else if (wasMouseButton || InputDeviceKeyboard::IsKeyboardDevice(inputDeviceId))
{
return DiscreteInputEvent{inputChannelId, inputChannel.GetState()};
}
@@ -12,36 +12,21 @@
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/std/containers/variant.h>
#include <AzCore/std/optional.h>
#include <AzFramework/Input/Channels/InputChannel.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Viewport/ViewportId.h>
namespace AzFramework
{
struct WindowSize;
//! Update camera key bindings that can be overridden with AZ console vars (invoke from console to update)
void ReloadCameraKeyBindings();
// to be moved
class ModernViewportCameraControllerRequests : public AZ::EBusTraits
{
public:
using BusIdType = AzFramework::ViewportId; ///< ViewportId - used to address requests to this EBus.
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::ById;
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
virtual void SetTargetCameraTransform(const AZ::Transform& transform) = 0;
protected:
~ModernViewportCameraControllerRequests() = default;
};
using ModernViewportCameraControllerRequestBus = AZ::EBus<ModernViewportCameraControllerRequests>;
//! Return Euler angles (pitch, roll, yaw) for the incoming orientation.
AZ::Vector3 EulerAngles(const AZ::Matrix3x3& orientation);
struct Camera
{
@@ -83,6 +68,8 @@ namespace AzFramework
return Transform().GetTranslation();
}
void UpdateCameraFromTransform(Camera& camera, const AZ::Transform& transform);
struct CursorMotionEvent
{
ScreenPoint m_position;
@@ -177,13 +164,7 @@ namespace AzFramework
Activation m_activation = Activation::Idle;
};
struct SmoothProps
{
float m_lookSmoothness = 5.0f;
float m_moveSmoothness = 5.0f;
};
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const SmoothProps& props, float deltaTime);
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, float deltaTime);
class Cameras
{
@@ -215,19 +196,16 @@ namespace AzFramework
class RotateCameraInput : public CameraInput
{
public:
explicit RotateCameraInput(const InputChannelId channelId)
: m_channelId(channelId)
explicit RotateCameraInput(const InputChannelId rotateChannelId)
: m_rotateChannelId(rotateChannelId)
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
InputChannelId m_channelId;
struct Props
{
float m_rotateSpeed = 0.005f;
} m_props;
private:
InputChannelId m_rotateChannelId;
};
struct PanAxes
@@ -260,22 +238,17 @@ namespace AzFramework
class PanCameraInput : public CameraInput
{
public:
explicit PanCameraInput(PanAxesFn panAxesFn)
PanCameraInput(const InputChannelId panChannelId, PanAxesFn panAxesFn)
: m_panAxesFn(AZStd::move(panAxesFn))
, m_panChannelId(panChannelId)
{
}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_panSpeed = 0.01f;
bool m_panInvertX = true;
bool m_panInvertY = true;
} m_props;
private:
PanAxesFn m_panAxesFn;
InputChannelId m_panChannelId;
};
using TranslationAxesFn = AZStd::function<AZ::Matrix3x3(const Camera& camera)>;
@@ -316,12 +289,6 @@ namespace AzFramework
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
void ResetImpl() override;
struct Props
{
float m_translateSpeed = 10.0f;
float m_boostMultiplier = 3.0f;
} m_props;
private:
enum class TranslationType
{
@@ -372,6 +339,11 @@ namespace AzFramework
return lhs;
}
friend TranslationType operator~(const TranslationType lhs)
{
return static_cast<TranslationType>(~static_cast<std::underlying_type_t<TranslationType>>(lhs));
}
static TranslationType translationFromKey(InputChannelId channelId);
TranslationType m_translation = TranslationType::Nil;
@@ -384,23 +356,19 @@ namespace AzFramework
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.02f;
} m_props;
};
class OrbitDollyCursorMoveCameraInput : public CameraInput
{
public:
explicit OrbitDollyCursorMoveCameraInput(const InputChannelId dollyChannelId)
: m_dollyChannelId(dollyChannelId) {}
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_dollySpeed = 0.1f;
} m_props;
private:
InputChannelId m_dollyChannelId;
};
class ScrollTranslationCameraInput : public CameraInput
@@ -408,11 +376,6 @@ namespace AzFramework
public:
void HandleEvents(const InputEvent& event) override;
Camera StepCamera(const Camera& targetCamera, const ScreenVector& cursorDelta, float scrollDelta, float deltaTime) override;
struct Props
{
float m_translateSpeed = 0.02f;
} m_props;
};
class OrbitCameraInput : public CameraInput
@@ -426,13 +389,10 @@ namespace AzFramework
}
Cameras m_orbitCameras;
struct Props
{
float m_defaultOrbitDistance = 60.0f;
float m_maxOrbitDistance = 100.0f;
} m_props;
};
struct WindowSize;
//! Map from a generic InputChannel event to a camera specific InputEvent.
InputEvent BuildInputEvent(const InputChannel& inputChannel, const WindowSize& windowSize);
} // namespace AzFramework
+2 -9
View File
@@ -652,21 +652,14 @@ void CCryEditDoc::SerializeViewSettings(CXmlArchive& xmlAr)
CViewport* pVP = GetIEditor()->GetViewManager()->GetView(i);
Matrix34 tm = Matrix34::CreateRotationXYZ(va);
tm.SetTranslation(vp);
if (pVP)
{
Matrix34 tm = Matrix34::CreateRotationXYZ(va);
tm.SetTranslation(vp);
pVP->SetViewTM(tm);
}
if (auto viewportContext = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get()->GetDefaultViewportContext())
{
AzFramework::ModernViewportCameraControllerRequestBus::Event(
viewportContext->GetId(), &AzFramework::ModernViewportCameraControllerRequestBus::Events::SetTargetCameraTransform,
LYTransformToAZTransform(tm));
}
// Load grid.
auto gridName = QString("Grid%1").arg(useOldViewFormat ? "" : QString::number(i));
XmlNodeRef gridNode = xmlAr.root->newChild(gridName.toUtf8().constData());
@@ -1234,6 +1234,7 @@ void EditorViewportWidget::SetViewportId(int id)
if (ed_useNewCameraSystem)
{
AzFramework::ReloadCameraKeyBindings();
m_renderViewport->GetControllerList()->Add(AZStd::make_shared<SandboxEditor::ModernViewportCameraController>());
}
else
@@ -16,14 +16,33 @@
#include <Atom/RPI.Public/ViewportContextBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Interface/Interface.h>
#include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzFramework/Windowing/WindowBus.h>
#include <AzToolsFramework/Viewport/ViewportMessages.h>
AZ_CVAR(bool, ed_newCameraSystemDebug, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enable debug drawing for the new camera system");
namespace AzFramework
{
extern InputChannelId CameraFreeLookButton;
extern InputChannelId CameraFreePanButton;
extern InputChannelId CameraOrbitLookButton;
extern InputChannelId CameraOrbitDollyButton;
extern InputChannelId CameraOrbitPanButton;
}
namespace SandboxEditor
{
static void DrawPreviewAxis(AzFramework::DebugDisplayRequests& display, const AZ::Transform& transform, const float axisLength)
{
display.SetColor(AZ::Colors::Red);
display.DrawLine(transform.GetTranslation(), transform.GetTranslation() + transform.GetBasisX().GetNormalizedSafe() * axisLength);
display.SetColor(AZ::Colors::Green);
display.DrawLine(transform.GetTranslation(), transform.GetTranslation() + transform.GetBasisY().GetNormalizedSafe() * axisLength);
display.SetColor(AZ::Colors::Blue);
display.DrawLine(transform.GetTranslation(), transform.GetTranslation() + transform.GetBasisZ().GetNormalizedSafe() * axisLength);
}
static AZ::RPI::ViewportContextPtr RetrieveViewportContext(const AzFramework::ViewportId viewportId)
{
auto viewportContextManager = AZ::Interface<AZ::RPI::ViewportContextRequestsInterface>::Get();
@@ -45,17 +64,21 @@ namespace SandboxEditor
: MultiViewportControllerInstanceInterface(viewportId)
{
// LYN-2315 TODO - move setup out of constructor, pass cameras in
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Right);
auto firstPersonPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::LookPan);
auto firstPersonRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraFreeLookButton);
auto firstPersonPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::CameraFreePanButton, AzFramework::LookPan);
auto firstPersonTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::LookTranslation);
auto firstPersonWheelCamera = AZStd::make_shared<AzFramework::ScrollTranslationCameraInput>();
auto orbitCamera = AZStd::make_shared<AzFramework::OrbitCameraInput>();
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::InputDeviceMouse::Button::Left);
auto orbitRotateCamera = AZStd::make_shared<AzFramework::RotateCameraInput>(AzFramework::CameraOrbitLookButton);
auto orbitTranslateCamera = AZStd::make_shared<AzFramework::TranslateCameraInput>(AzFramework::OrbitTranslation);
auto orbitDollyWheelCamera = AZStd::make_shared<AzFramework::OrbitDollyScrollCameraInput>();
auto orbitDollyMoveCamera = AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>();
auto orbitPanCamera = AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::OrbitPan);
auto orbitDollyMoveCamera =
AZStd::make_shared<AzFramework::OrbitDollyCursorMoveCameraInput>(AzFramework::CameraOrbitDollyButton);
auto orbitPanCamera =
AZStd::make_shared<AzFramework::PanCameraInput>(AzFramework::CameraOrbitPanButton, AzFramework::OrbitPan);
orbitCamera->m_orbitCameras.AddCamera(orbitRotateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitTranslateCamera);
orbitCamera->m_orbitCameras.AddCamera(orbitDollyWheelCamera);
@@ -68,23 +91,24 @@ namespace SandboxEditor
m_cameraSystem.m_cameras.AddCamera(firstPersonWheelCamera);
m_cameraSystem.m_cameras.AddCamera(orbitCamera);
if (const auto viewportContext = RetrieveViewportContext(viewportId))
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
{
// set position but not orientation
m_targetCamera.m_lookAt = viewportContext->GetCameraTransform().GetTranslation();
auto handleCameraChange = [this](const AZ::Matrix4x4& matrix) {
UpdateCameraFromTransform(
m_targetCamera,
AZ::Transform::CreateFromMatrix3x3AndTranslation(AZ::Matrix3x3::CreateFromMatrix4x4(matrix), matrix.GetTranslation()));
};
// LYN-2315 TODO https://www.geometrictools.com/Documentation/EulerAngles.pdf
m_cameraViewMatrixChangeHandler = AZ::RPI::ViewportContext::MatrixChangedEvent::Handler(handleCameraChange);
m_camera = m_targetCamera;
viewportContext->ConnectViewMatrixChangedHandler(m_cameraViewMatrixChangeHandler);
}
AzFramework::ViewportDebugDisplayEventBus::Handler::BusConnect(AzToolsFramework::GetEntityContextId());
AzFramework::ModernViewportCameraControllerRequestBus::Handler::BusConnect(viewportId);
}
ModernViewportCameraControllerInstance::~ModernViewportCameraControllerInstance()
{
AzFramework::ModernViewportCameraControllerRequestBus::Handler::BusDisconnect();
AzFramework::ViewportDebugDisplayEventBus::Handler::BusDisconnect();
}
@@ -93,6 +117,28 @@ namespace SandboxEditor
AzFramework::WindowSize windowSize;
AzFramework::WindowRequestBus::EventResult(
windowSize, event.m_windowHandle, &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
if (m_cameraMode == CameraMode::Control)
{
if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(event.m_inputChannel.GetInputDevice().GetInputDeviceId()))
{
if (event.m_inputChannel.GetInputChannelId() == AzFramework::InputDeviceKeyboard::Key::AlphanumericR)
{
m_transformEnd = m_camera.Transform();
return true;
}
else if (event.m_inputChannel.GetInputChannelId() == AzFramework::InputDeviceKeyboard::Key::AlphanumericP)
{
m_animationT = 0.0f;
m_cameraMode = CameraMode::Animation;
m_transformStart = m_camera.Transform();
return true;
}
}
}
return m_cameraSystem.HandleEvents(AzFramework::BuildInputEvent(event.m_inputChannel, windowSize));
}
@@ -100,26 +146,49 @@ namespace SandboxEditor
{
if (auto viewportContext = RetrieveViewportContext(GetViewportId()))
{
m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count());
m_camera =
AzFramework::SmoothCamera(m_camera, m_targetCamera, m_smoothProps, event.m_deltaTime.count());
if (m_cameraMode == CameraMode::Control)
{
m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count());
m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, event.m_deltaTime.count());
viewportContext->SetCameraTransform(m_camera.Transform());
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 float transitionT = smootherStepFn(m_animationT);
const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation(
m_transformStart.GetRotation().Slerp(m_transformEnd.GetRotation(), transitionT),
m_transformStart.GetTranslation().Lerp(m_transformEnd.GetTranslation(), transitionT));
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 (m_animationT >= 1.0f)
{
m_cameraMode = CameraMode::Control;
}
m_animationT = AZ::GetClamp(m_animationT + event.m_deltaTime.count(), 0.0f, 1.0f);
viewportContext->SetCameraTransform(current);
}
}
}
void ModernViewportCameraControllerInstance::DisplayViewport(
[[maybe_unused]] const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay)
{
if (ed_newCameraSystemDebug)
if (const float alpha = AZStd::min(-m_camera.m_lookDist / 5.0f, 1.0f); alpha > AZ::Constants::FloatEpsilon)
{
debugDisplay.SetColor(AZ::Colors::White);
debugDisplay.DrawWireSphere(m_targetCamera.m_lookAt, 0.5f);
debugDisplay.SetColor(1.0f, 1.0f, 1.0f, alpha);
debugDisplay.DrawWireSphere(m_camera.m_lookAt, 0.5f);
}
}
void ModernViewportCameraControllerInstance::SetTargetCameraTransform(const AZ::Transform& transform)
{
m_targetCamera.m_lookAt = transform.GetTranslation();
DrawPreviewAxis(debugDisplay, m_transformEnd, 2.0f);
}
} // namespace SandboxEditor
@@ -12,6 +12,7 @@
#pragma once
#include <Atom/RPI.Public/ViewportContext.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Viewport/CameraInput.h>
#include <AzFramework/Viewport/MultiViewportController.h>
@@ -19,8 +20,7 @@
namespace SandboxEditor
{
class ModernViewportCameraControllerInstance final : public AzFramework::MultiViewportControllerInstanceInterface,
private AzFramework::ViewportDebugDisplayEventBus::Handler,
private AzFramework::ModernViewportCameraControllerRequestBus::Handler
private AzFramework::ViewportDebugDisplayEventBus::Handler
{
public:
explicit ModernViewportCameraControllerInstance(AzFramework::ViewportId viewportId);
@@ -33,14 +33,23 @@ namespace SandboxEditor
// AzFramework::ViewportDebugDisplayEventBus overrides ...
void DisplayViewport(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
// ModernViewportCameraControllerRequestBus overrides ...
void SetTargetCameraTransform(const AZ::Transform& transform) override;
private:
enum class CameraMode
{
Control,
Animation
};
AzFramework::Camera m_camera;
AzFramework::Camera m_targetCamera;
AzFramework::SmoothProps m_smoothProps;
AzFramework::CameraSystem m_cameraSystem;
AZ::Transform m_transformStart = AZ::Transform::CreateIdentity();
AZ::Transform m_transformEnd = AZ::Transform::CreateIdentity();
float m_animationT = 0.0f;
CameraMode m_cameraMode = CameraMode::Control;
AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler;
};
using ModernViewportCameraController = AzFramework::MultiViewportController<ModernViewportCameraControllerInstance>;