Merge pull request #5752 from aws-lumberyard-dev/puvvadar/gitflow_211118_o3de

Merge stabilization/2110
This commit is contained in:
puvvadar
2021-11-19 15:46:16 -08:00
committed by GitHub
387 changed files with 6580 additions and 3688 deletions
@@ -76,9 +76,12 @@ namespace AzFramework
virtual void DrawSolidCone(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius, float height, bool drawShaded = true) { (void)pos; (void)dir; (void)radius; (void)height; (void)drawShaded; }
virtual void DrawWireCylinder(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height) { (void)center; (void)axis; (void)radius; (void)height; }
virtual void DrawSolidCylinder(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height, bool drawShaded = true) { (void)center; (void)axis; (void)radius; (void)height; (void)drawShaded; }
virtual void DrawWireCylinderNoEnds(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height) { (void)center; (void)axis; (void)radius; (void)height; }
virtual void DrawSolidCylinderNoEnds(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float height, bool drawShaded = true) { (void)center; (void)axis; (void)radius; (void)height; (void)drawShaded; }
virtual void DrawWireCapsule(const AZ::Vector3& center, const AZ::Vector3& axis, float radius, float heightStraightSection) { (void)center; (void)axis; (void)radius; (void)heightStraightSection; }
virtual void DrawWireSphere(const AZ::Vector3& pos, float radius) { (void)pos; (void)radius; }
virtual void DrawWireSphere(const AZ::Vector3& pos, const AZ::Vector3 radius) { (void)pos; (void)radius; }
virtual void DrawWireHemisphere(const AZ::Vector3& pos, const AZ::Vector3& axis, float radius) { (void)pos; (void)axis; (void)radius; }
virtual void DrawWireDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) { (void)pos; (void)dir; (void)radius; }
virtual void DrawBall(const AZ::Vector3& pos, float radius, bool drawShaded = true) { (void)pos; (void)radius; (void)drawShaded; }
virtual void DrawDisk(const AZ::Vector3& pos, const AZ::Vector3& dir, float radius) { (void)pos; (void)dir; (void)radius; }
@@ -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<CursorEvent>(&event))
@@ -291,6 +313,11 @@ namespace AzFramework
{
return false;
};
m_constrainPitch = []() constexpr
{
return true;
};
}
bool RotateCameraInput::HandleEvents(const InputEvent& event, const ScreenVector& cursorDelta, [[maybe_unused]] const float scrollDelta)
@@ -312,7 +339,10 @@ namespace AzFramework
nextCamera.m_yaw -= float(cursorDelta.m_x) * rotateSpeed * Invert(m_invertYawFn());
nextCamera.m_yaw = WrapYawRotation(nextCamera.m_yaw);
nextCamera.m_pitch = ClampPitchRotation(nextCamera.m_pitch);
if (m_constrainPitch())
{
nextCamera.m_pitch = ClampPitchRotation(nextCamera.m_pitch);
}
return nextCamera;
}
@@ -726,14 +756,14 @@ namespace AzFramework
Camera SmoothCamera(const Camera& currentCamera, const Camera& targetCamera, const CameraProps& cameraProps, const float deltaTime)
{
const auto clamp_rotation = [](const float angle)
const auto clampRotation = [](const float angle)
{
return AZStd::fmod(angle + AZ::Constants::TwoPi, AZ::Constants::TwoPi);
};
// keep yaw in 0 - 360 range
float targetYaw = clamp_rotation(targetCamera.m_yaw);
const float currentYaw = clamp_rotation(currentCamera.m_yaw);
float targetYaw = clampRotation(targetCamera.m_yaw);
const float currentYaw = clampRotation(currentCamera.m_yaw);
// return the sign of the float input (-1, 0, 1)
const auto sign = [](const float value)
@@ -742,21 +772,17 @@ namespace AzFramework
};
// ensure smooth transition when moving across 0 - 360 boundary
const float yawDelta = targetYaw - currentYaw;
if (AZStd::abs(yawDelta) >= AZ::Constants::Pi)
if (const float yawDelta = targetYaw - currentYaw; AZStd::abs(yawDelta) >= AZ::Constants::Pi)
{
targetYaw -= AZ::Constants::TwoPi * sign(yawDelta);
}
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 +792,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);
}
@@ -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<typename MotionTag>
struct MotionEvent
@@ -334,6 +347,7 @@ namespace AzFramework
AZStd::function<float()> m_rotateSpeedFn;
AZStd::function<bool()> m_invertPitchFn;
AZStd::function<bool()> m_invertYawFn;
AZStd::function<bool()> m_constrainPitch;
private:
InputChannelId m_rotateChannelId; //!< Input channel to begin the rotate camera input.
@@ -8,18 +8,18 @@
#include "CameraState.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Matrix3x4.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace AzFramework
{
void SetCameraClippingVolume(
AzFramework::CameraState& cameraState, const float nearPlane, const float farPlane, const float fovRad)
AzFramework::CameraState& cameraState, const float nearPlane, const float farPlane, const float verticalFovRad)
{
cameraState.m_nearClip = nearPlane;
cameraState.m_farClip = farPlane;
cameraState.m_fovOrZoom = fovRad;
cameraState.m_fovOrZoom = verticalFovRad;
}
void SetCameraTransform(CameraState& cameraState, const AZ::Transform& transform)
@@ -35,20 +35,34 @@ namespace AzFramework
SetCameraClippingVolume(cameraState, 0.1f, 1000.0f, AZ::DegToRad(60.0f));
}
AzFramework::CameraState CreateDefaultCamera(
const AZ::Transform& transform, const AZ::Vector2& viewportSize)
CameraState CreateCamera(
const AZ::Transform& transform,
const float nearPlane,
const float farPlane,
const float verticalFovRad,
const AZ::Vector2& viewportSize)
{
AzFramework::CameraState cameraState;
SetDefaultCameraClippingVolume(cameraState);
SetCameraTransform(cameraState, transform);
SetCameraClippingVolume(cameraState, nearPlane, farPlane, verticalFovRad);
cameraState.m_viewportSize = viewportSize;
return cameraState;
}
AzFramework::CameraState CreateIdentityDefaultCamera(
const AZ::Vector3& position, const AZ::Vector2& viewportSize)
AzFramework::CameraState CreateDefaultCamera(const AZ::Transform& transform, const AZ::Vector2& viewportSize)
{
AzFramework::CameraState cameraState;
SetCameraTransform(cameraState, transform);
SetDefaultCameraClippingVolume(cameraState);
cameraState.m_viewportSize = viewportSize;
return cameraState;
}
AzFramework::CameraState CreateIdentityDefaultCamera(const AZ::Vector3& position, const AZ::Vector2& viewportSize)
{
return CreateDefaultCamera(AZ::Transform::CreateTranslation(position), viewportSize);
}
@@ -89,15 +103,15 @@ namespace AzFramework
void CameraState::Reflect(AZ::SerializeContext& serializeContext)
{
serializeContext.Class<CameraState>()->
Field("Position", &CameraState::m_position)->
Field("Forward", &CameraState::m_forward)->
Field("Side", &CameraState::m_side)->
Field("Up", &CameraState::m_up)->
Field("ViewportSize", &CameraState::m_viewportSize)->
Field("NearClip", &CameraState::m_nearClip)->
Field("FarClip", &CameraState::m_farClip)->
Field("FovZoom", &CameraState::m_fovOrZoom)->
Field("Ortho", &CameraState::m_orthographic);
serializeContext.Class<CameraState>()
->Field("Position", &CameraState::m_position)
->Field("Forward", &CameraState::m_forward)
->Field("Side", &CameraState::m_side)
->Field("Up", &CameraState::m_up)
->Field("ViewportSize", &CameraState::m_viewportSize)
->Field("NearClip", &CameraState::m_nearClip)
->Field("FarClip", &CameraState::m_farClip)
->Field("FovZoom", &CameraState::m_fovOrZoom)
->Field("Ortho", &CameraState::m_orthographic);
}
} // namespace AzFramework
@@ -40,10 +40,14 @@ namespace AzFramework
AZ::Vector2 m_viewportSize = AZ::Vector2::CreateZero(); //!< Dimensions of the viewport.
float m_nearClip = 0.01f; //!< Near clip plane of the camera.
float m_farClip = 100.0f; //!< Far clip plane of the camera.
float m_fovOrZoom = 0.0f; //!< Fov or zoom of camera depending on if it is using orthographic projection or not.
float m_fovOrZoom = 0.0f; //!< Vertical fov or zoom of camera depending on if it is using orthographic projection or not.
bool m_orthographic = false; //!< Is the camera using orthographic projection or not.
};
//! Create a camera at the given transform, specifying the near and far clip planes as well as the fov with a specific viewport size.
CameraState CreateCamera(
const AZ::Transform& transform, float nearPlane, float farPlane, float verticalFovRad, const AZ::Vector2& viewportSize);
//! Create a camera at the given transform with a specific viewport size.
//! @note The near/far clip planes and fov are sensible default values - please
//! use SetCameraClippingVolume to override them.
@@ -60,7 +64,7 @@ namespace AzFramework
CameraState CreateCameraFromWorldFromViewMatrix(const AZ::Matrix4x4& worldFromView, const AZ::Vector2& viewportSize);
//! Override the default near/far clipping planes and fov of the camera.
void SetCameraClippingVolume(CameraState& cameraState, float nearPlane, float farPlane, float fovRad);
void SetCameraClippingVolume(CameraState& cameraState, float nearPlane, float farPlane, float verticalFovRad);
//! Override the default near/far clipping planes and fov of the camera by inferring them the specified right handed transform into clip space.
void SetCameraClippingVolumeFromPerspectiveFovMatrixRH(CameraState& cameraState, const AZ::Matrix4x4& clipFromView);
@@ -24,11 +24,12 @@ namespace AzFramework
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
{
m_moveAccumulator += ScreenVectorLength(cursorDelta);
const auto previousDetectionState = m_detectionState;
if (previousDetectionState == DetectionState::WaitingForMove)
{
// only allow the action to begin if the mouse has been moved a small amount
m_moveAccumulator += ScreenVectorLength(cursorDelta);
if (m_moveAccumulator > m_deadZone)
{
m_detectionState = DetectionState::Moved;
@@ -43,7 +44,7 @@ namespace AzFramework
using FloatingPointSeconds = AZStd::chrono::duration<float, AZStd::chrono::seconds::period>;
const auto diff = now - m_tryBeginTime.value();
if (FloatingPointSeconds(diff).count() < m_doubleClickInterval)
if (FloatingPointSeconds(diff).count() < m_doubleClickInterval && m_moveAccumulator < m_deadZone)
{
return ClickOutcome::Nil;
}
@@ -15,6 +15,10 @@
namespace AzFramework
{
//! Default value to use for detecting if the mouse has moved far enough after a mouse down to no longer
//! register a click when a mouse up occurs.
inline constexpr float DefaultMouseMoveDeadZone = 2.0f;
struct ScreenVector;
//! Utility class to help detect different types of mouse click (mouse down and up with
@@ -66,7 +70,7 @@ namespace AzFramework
};
float m_moveAccumulator = 0.0f; //!< How far the mouse has moved after mouse down.
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
float m_deadZone = DefaultMouseMoveDeadZone; //!< How far to move before a click is cancelled (when Move will fire).
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
DetectionState m_detectionState; //!< Internal state of ClickDetector.
//! Mouse down time (happens each mouse down, helps with double click handling).
@@ -24,6 +24,10 @@ namespace AzFramework
serializeContext->Class<ScreenVector>()->
Field("X", &ScreenVector::m_x)->
Field("Y", &ScreenVector::m_y);
serializeContext->Class<ScreenSize>()->
Field("Width", &ScreenSize::m_width)->
Field("Height", &ScreenSize::m_height);
}
}
} // namespace AzFramework
@@ -26,7 +26,7 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}");
ScreenPoint() = default;
ScreenPoint(int x, int y)
constexpr ScreenPoint(int x, int y)
: m_x(x)
, m_y(y)
{
@@ -45,7 +45,7 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}");
ScreenVector() = default;
ScreenVector(int x, int y)
constexpr ScreenVector(int x, int y)
: m_x(x)
, m_y(y)
{
@@ -55,6 +55,22 @@ namespace AzFramework
int m_y; //!< Y screen delta.
};
//! A wrapper around a screen width and height.
struct ScreenSize
{
AZ_TYPE_INFO(ScreenSize, "{26D28916-6E8E-44B8-83F9-C44BCDA370E2}");
ScreenSize() = default;
constexpr ScreenSize(int width, int height)
: m_width(width)
, m_height(height)
{
}
int m_width; //!< Screen size width.
int m_height; //!< Screen size height.
};
void ScreenGeometryReflect(AZ::ReflectContext* context);
inline const ScreenVector operator-(const ScreenPoint& lhs, const ScreenPoint& rhs)
@@ -138,6 +154,16 @@ namespace AzFramework
return !operator==(lhs, rhs);
}
inline const bool operator==(const ScreenSize& lhs, const ScreenSize& rhs)
{
return lhs.m_width == rhs.m_width && lhs.m_height == rhs.m_height;
}
inline const bool operator!=(const ScreenSize& lhs, const ScreenSize& rhs)
{
return !operator==(lhs, rhs);
}
inline ScreenVector& operator*=(ScreenVector& lhs, const float rhs)
{
lhs.m_x = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_x) * rhs));
@@ -152,6 +178,20 @@ namespace AzFramework
return result;
}
inline ScreenSize& operator*=(ScreenSize& lhs, const float rhs)
{
lhs.m_width = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_width) * rhs));
lhs.m_height = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_height) * rhs));
return lhs;
}
inline const ScreenSize operator*(const ScreenSize& lhs, const float rhs)
{
ScreenSize result{ lhs };
result *= rhs;
return result;
}
inline float ScreenVectorLength(const ScreenVector& screenVector)
{
return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y));
@@ -168,4 +208,28 @@ namespace AzFramework
{
return AZ::Vector2(aznumeric_cast<float>(screenVector.m_x), aznumeric_cast<float>(screenVector.m_y));
}
//! Return an AZ::Vector2 from a ScreenSize.
inline AZ::Vector2 Vector2FromScreenSize(const ScreenSize& screenSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenSize.m_width), aznumeric_cast<float>(screenSize.m_height));
}
//! Return a ScreenPoint from an AZ::Vector2.
inline ScreenPoint ScreenPointFromVector2(const AZ::Vector2& vector2)
{
return ScreenPoint(aznumeric_cast<int>(AZStd::lround(vector2.GetX())), aznumeric_cast<int>(AZStd::lround(vector2.GetY())));
}
//! Return a ScreenVector from an AZ::Vector2.
inline ScreenVector ScreenVectorFromVector2(const AZ::Vector2& vector2)
{
return ScreenVector(aznumeric_cast<int>(AZStd::lround(vector2.GetX())), aznumeric_cast<int>(AZStd::lround(vector2.GetY())));
}
//! Return a ScreenSize from an AZ::Vector2.
inline ScreenSize ScreenSizeFromVector2(const AZ::Vector2& vector2)
{
return ScreenSize(aznumeric_cast<int>(AZStd::lround(vector2.GetX())), aznumeric_cast<int>(AZStd::lround(vector2.GetY())));
}
} // namespace AzFramework
@@ -10,6 +10,7 @@
#include <AzCore/Math/Frustum.h>
#include <AzCore/Math/Matrix4x4.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Vector4.h>
#include <AzCore/Math/VectorConversions.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
@@ -112,9 +113,8 @@ namespace AzFramework
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize)
{
const auto ndcNormalizedPosition = WorldToScreenNdc(worldPosition, cameraView, cameraProjection);
// scale ndc position by screen dimensions to return screen position
return ScreenPointFromNdc(AZ::Vector3ToVector2(ndcNormalizedPosition), viewportSize);
return ScreenPointFromNdc(AZ::Vector3ToVector2(WorldToScreenNdc(worldPosition, cameraView, cameraProjection)), viewportSize);
}
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState)
@@ -144,9 +144,7 @@ namespace AzFramework
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize)
{
const auto normalizedScreenPosition = NdcFromScreenPoint(screenPosition, viewportSize);
return ScreenNdcToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
return ScreenNdcToWorld(NdcFromScreenPoint(screenPosition, viewportSize), inverseCameraView, inverseCameraProjection);
}
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState)
@@ -104,9 +104,10 @@ namespace UnitTest
AZStd::shared_ptr<AzFramework::OrbitCameraInput> m_orbitCamera;
AZ::Vector3 m_pivot = AZ::Vector3::CreateZero();
//! This is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based
//! on vertical or horizontal motion) as the rotate speed function is set to be 1/1000.
inline static const int PixelMotionDelta = 1570;
// this is approximately Pi/2 * 1000 - this can be used to rotate the camera 90 degrees (pitch or yaw based
// on vertical or horizontal motion) as the rotate speed function is set to be 1/1000.
inline static const int PixelMotionDelta90Degrees = 1570;
inline static const int PixelMotionDelta135Degrees = 2356;
};
TEST_F(CameraInputFixture, BeginAndEndOrbitCameraInputConsumesCorrectEvents)
@@ -292,7 +293,7 @@ namespace UnitTest
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ PixelMotionDelta });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ PixelMotionDelta90Degrees });
const float expectedYaw = AzFramework::WrapYawRotation(-AZ::Constants::HalfPi);
@@ -310,7 +311,7 @@ namespace UnitTest
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta90Degrees });
const float expectedPitch = AzFramework::ClampPitchRotation(-AZ::Constants::HalfPi);
@@ -331,7 +332,7 @@ namespace UnitTest
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta });
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ PixelMotionDelta90Degrees });
const auto expectedCameraEndingPosition = AZ::Vector3(0.0f, -10.0f, 10.0f);
const float expectedPitch = AzFramework::ClampPitchRotation(-AZ::Constants::HalfPi);
@@ -354,7 +355,7 @@ namespace UnitTest
HandleEventAndUpdate(AzFramework::DiscreteInputEvent{ m_orbitChannelId, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Left, AzFramework::InputChannel::State::Began });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta });
HandleEventAndUpdate(AzFramework::HorizontalMotionEvent{ -PixelMotionDelta90Degrees });
const auto expectedCameraEndingPosition = AZ::Vector3(20.0f, -5.0f, 0.0f);
const float expectedYaw = AzFramework::WrapYawRotation(AZ::Constants::HalfPi);
@@ -366,4 +367,42 @@ namespace UnitTest
EXPECT_THAT(m_camera.m_offset, IsClose(AZ::Vector3(5.0f, -10.0f, 0.0f)));
EXPECT_THAT(m_camera.Translation(), IsCloseTolerance(expectedCameraEndingPosition, 0.01f));
}
TEST_F(CameraInputFixture, CameraPitchCanNotBeMovedPastNinetyDegreesWhenConstrained)
{
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
// pitch by 135.0 degrees
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ -PixelMotionDelta135Degrees });
// clamped to 90.0 degrees
const float expectedPitch = AZ::DegToRad(90.0f);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
}
TEST_F(CameraInputFixture, CameraPitchCanBeMovedPastNinetyDegreesWhenUnconstrained)
{
m_firstPersonRotateCamera->m_constrainPitch = []
{
return false;
};
const auto cameraStartingPosition = AZ::Vector3(15.0f, -20.0f, 0.0f);
m_targetCamera.m_pivot = cameraStartingPosition;
HandleEventAndUpdate(
AzFramework::DiscreteInputEvent{ AzFramework::InputDeviceMouse::Button::Right, AzFramework::InputChannel::State::Began });
// pitch by 135.0 degrees
HandleEventAndUpdate(AzFramework::VerticalMotionEvent{ -PixelMotionDelta135Degrees });
const float expectedPitch = AZ::DegToRad(135.0f);
using ::testing::FloatNear;
EXPECT_THAT(m_camera.m_pitch, FloatNear(expectedPitch, 0.001f));
}
} // namespace UnitTest
@@ -11,6 +11,7 @@
#include <AzFramework/Viewport/CameraState.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AzCore/Math/SimdMath.h>
#include <AzCore/Math/MatrixUtils.h>
#include <AzCore/Math/Matrix4x4.h>
namespace UnitTest
@@ -51,22 +52,6 @@ namespace UnitTest
{
};
// Taken from Atom::MatrixUtils for testing purposes, this can be removed if MakePerspectiveFovMatrixRH makes it into AZ
static AZ::Matrix4x4 MakePerspectiveMatrixRH(float fovY, float aspectRatio, float nearClip, float farClip)
{
float sinFov, cosFov;
AZ::SinCos(0.5f * fovY, sinFov, cosFov);
float yScale = cosFov / sinFov; //cot(fovY/2)
float xScale = yScale / aspectRatio;
AZ::Matrix4x4 out;
out.SetRow(0, xScale, 0.f, 0.f, 0.f );
out.SetRow(1, 0.f, yScale, 0.f, 0.f );
out.SetRow(2, 0.f, 0.f, farClip / (nearClip - farClip), nearClip*farClip / (nearClip - farClip) );
out.SetRow(3, 0.f, 0.f, -1.f, 0.f );
return out;
}
TEST_P(Translation, Permutation)
{
// Given a position
@@ -176,7 +161,8 @@ namespace UnitTest
{
auto [fovY, aspectRatio, nearClip, farClip] = GetParam();
AZ::Matrix4x4 clipFromView = MakePerspectiveMatrixRH(fovY, aspectRatio, nearClip, farClip);
AZ::Matrix4x4 clipFromView;
MakePerspectiveFovMatrixRH(clipFromView, fovY, aspectRatio, nearClip, farClip);
AzFramework::SetCameraClippingVolumeFromPerspectiveFovMatrixRH(m_cameraState, clipFromView);
@@ -144,12 +144,45 @@ namespace UnitTest
{
using ::testing::Eq;
const ClickDetector::ClickOutcome downOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome upOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(50, 50));
const ClickDetector::ClickOutcome downOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome upOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(50, 50));
EXPECT_THAT(downOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(upOutcome, Eq(ClickDetector::ClickOutcome::Release));
}
//! note: ClickDetector does not explicitly return double clicks but if one occurs the ClickOutcome will be Nil
TEST_F(ClickDetectorFixture, DoubleClickIsRegisteredIfMouseDeltaHasMovedLessThanDeadzoneInClickInterval)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome firstDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome firstUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(firstDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(firstUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
EXPECT_THAT(secondDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(secondUpOutcome, Eq(ClickDetector::ClickOutcome::Nil));
}
TEST_F(ClickDetectorFixture, DoubleClickIsNotRegisteredIfMouseDeltaHasMovedMoreThanDeadzoneInClickInterval)
{
using ::testing::Eq;
const ClickDetector::ClickOutcome firstDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(0, 0));
const ClickDetector::ClickOutcome firstUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
const ClickDetector::ClickOutcome secondDownOutcome =
m_clickDetector.DetectClick(ClickDetector::ClickEvent::Down, ScreenVector(10, 10));
const ClickDetector::ClickOutcome secondUpOutcome = m_clickDetector.DetectClick(ClickDetector::ClickEvent::Up, ScreenVector(0, 0));
EXPECT_THAT(firstDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(firstUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
EXPECT_THAT(secondDownOutcome, Eq(ClickDetector::ClickOutcome::Nil));
EXPECT_THAT(secondUpOutcome, Eq(ClickDetector::ClickOutcome::Click));
}
} // namespace UnitTest
@@ -8,12 +8,13 @@
#include <AzCore/UnitTest/TestTypes.h>
#include <AzFramework/Viewport/CursorState.h>
#include <Tests/Utils/Printers.h>
namespace UnitTest
{
using AzFramework::CursorState;
using AzFramework::ScreenVector;
using AzFramework::ScreenPoint;
using AzFramework::ScreenVector;
class CursorStateFixture : public ::testing::Test
{
@@ -0,0 +1,32 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "Printers.h"
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <ostream>
#include <string>
namespace AzFramework
{
void PrintTo(const ScreenPoint& screenPoint, std::ostream* os)
{
*os << "(x: " << screenPoint.m_x << ", y: " << screenPoint.m_y << ")";
}
void PrintTo(const ScreenVector& screenVector, std::ostream* os)
{
*os << "(x: " << screenVector.m_x << ", y: " << screenVector.m_y << ")";
}
void PrintTo(const ScreenSize& screenSize, std::ostream* os)
{
*os << "(width: " << screenSize.m_width << ", height: " << screenSize.m_height << ")";
}
} // namespace AzFramework
@@ -0,0 +1,20 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <iosfwd>
namespace AzFramework
{
struct ScreenPoint;
struct ScreenVector;
struct ScreenSize;
void PrintTo(const ScreenPoint& screenPoint, std::ostream* os);
void PrintTo(const ScreenVector& screenVector, std::ostream* os);
void PrintTo(const ScreenSize& screenSize, std::ostream* os);
} // namespace AzFramework
@@ -11,5 +11,7 @@ set(FILES
Mocks/MockWindowRequests.h
Utils/Utils.h
Utils/Utils.cpp
Utils/Printers.h
Utils/Printers.cpp
FrameworkApplicationFixture.h
)